用参数装饰类方法
Posted
技术标签:
【中文标题】用参数装饰类方法【英文标题】:decorating class method with arguments 【发布时间】:2019-11-16 23:42:38 【问题描述】:如何?当前代码是
def establish_con(func):
con, meta = db.connect(config.user, config.password, config.db)
meta.reflect(bind=con)
def inner(self, query, con, *args, **kwargs):
return func(self, query, con, *args, **kwargs)
con.close()
return inner
class DataReader:
def __init__(self):
self.data =
@establish_con
def execQuery(self, query, con):
# con, meta = db.connect(config.user, config.password, config.db)
# meta.reflect(bind=con)
result = pd.read_sql(query, con)
# con.close()
return result
test = DataReader()
df = test.execQuery("Select * from backtest limit 10")
print(df)
目前,第一个参数似乎是类实例。我尝试了代码的不同变体,但总是遇到太多/不够/未定义的参数问题。
我读过其他类似的帖子
How do I pass extra arguments to a Python decorator?
Decorators with parameters?
还有其他人,但还是想不通。
编辑:不是 Python decorators in classes 的副本,因为在这个答案中不需要将参数传递给函数。
【问题讨论】:
@fizzybear 不是重复的,因为该答案中没有传递任何参数。 我同意这不是那个问题的重复,但它不是minimal reproducible example。 【参考方案1】:改编@madjardi 来自this post 的答案以使用参数。
import functools
class Example:
con = "Connection"
def wrapper(func):
@functools.wraps(func)
def wrap(self, *args, **kwargs):
print("inside wrap")
return func(self, Example.con, *args, **kwargs) # use *args to pass objects down
return wrap
@wrapper
def method(self, con, arg):
print("METHOD 0 1".format(con, arg))
wrapper = staticmethod(wrapper)
e = Example()
e.method(1) # METHOD Connection 1
【讨论】:
我仍然收到TypeError: execQuery() missing 1 required positional argument: 'con'
,因为当我在装饰器中调用方法+另一个参数(con)时,我正在传递参数
已编辑。基本上,您将连接作为类变量并在 *args 之前传递它。如果你在 __init__ 中初始化它,你也可以做 self.con。
我需要在课堂上使用这个装饰器吗?我可以把它移出来吗?另外,在这种情况下 functools.wraps 的目的是什么?
是的,否则,您无法将self
传递给func
。您可以硬编码一个全局变量并传递它而不是self
,但这是不可取的。 functools.wraps
用于使 wrap
看起来像 func
,用于堆栈跟踪中的文档字符串和函数名称。没有它,调试变得更加混乱。但这并不是绝对必要的。【参考方案2】:
你应该替换:
def inner(self, query, con, *args, **kwargs):
return func(self, query, con, *args, **kwargs)
与:
def inner(self, query, *args, **kwargs): # no 'con' here
return func(self, query, con, *args, **kwargs)
使用您的策略的最小工作示例是(实现与您的目标不一样):
def add_arg(method):
def decorated_method(self, *args):
return method(self, 10, *args)
return decorated_method
class Data:
@add_arg
def summation(self, *args):
return sum(args)
d = Data()
print(d.summation(1, 2)) # prints 13, not 3
【讨论】:
我需要传递在装饰器内部创建的变量 con。 是的,这就是inner
没有con
作为参数的原因。这个值来自周围的上下文(即闭包上下文)。【参考方案3】:
我认为您需要这样做。我已将代码分成两个单独的块,以使其更清晰。
的第一部分只是建立了一个最小的脚手架,以便可以运行(并跟踪执行)第二个块中的代码,并使其尽可能接近您的问题。它本身并不是很重要。
带参数的装饰器实际上变成了装饰器工厂——从某种意义上说,它们必须创建一个返回装饰器函数,然后将其应用于目标函数或方法。
# Scaffolding
class Pandas:
@staticmethod
def read_sql(query, con):
print(f'in Pandas read_sql(query!r, con)')
return 'pandas_dataframe'
class Connection:
def close(self):
print('in Connection.close()')
def __repr__(self):
return '<Connection object>'
con = Connection()
class Meta:
def reflect(self, bind=con):
print(f'in Meta.reflect(bind=bind')
class Database:
def connect(self, user, password, db):
print(f'in Database.connect(user, password, db)')
return Connection(), Meta()
def __repr__(self):
return '<Database object>'
class Config:
def __init__(self, user, password, db):
self.user = user
self.password = password
self.db = db
# Set up a framework for testing.
pd = Pandas()
meta = Meta()
db = Database()
config = Config('username', 'secret', db)
环境建立后,装饰器的编写方法如下。
# Decorator
def establish_con(config):
def wrapper(method):
def wrapped(*args, **kwargs):
con, meta = db.connect(config.user, config.password, config.db)
meta.reflect(bind=con)
args = args + (con,)
result = method(*args, **kwargs)
con.close()
return result
return wrapped
return wrapper
class DataReader:
def __init__(self):
self.data =
@establish_con(config)
def execQuery(self, query, con):
return pd.read_sql(query, con)
test = DataReader()
df = test.execQuery("Select * from backtest limit 10")
print(df)
输出:
in Database.connect(username, secret, <Database object>)
in Meta.reflect(bind=<Connection object>
in Pandas read_sql('Select * from backtest limit 10', <Connection object>)
in Connection.close()
pandas_dataframe
【讨论】:
出于好奇,当其他 2 个答案使用装饰器时,您为什么使用装饰器工厂?有什么优点/缺点?只是想了解有关装饰器的更多信息 因为这就是带参数的装饰器的工作方式——一个明显的优势;¬)
这在文档的Function definitions 部分中关于装饰器的部分中有所提及——注意尤其是@f1(arg)
大致相当于示例中的内容。
我的另一个answer的第一部分更详细地解释了“装饰工厂”的概念。以上是关于用参数装饰类方法的主要内容,如果未能解决你的问题,请参考以下文章