我是第一次使用装饰器的经历,我创建了一个装饰器类,用于过滤目标函数的结果,默认情况下会返回某个序列:
class Filter(object):
def __init__(self, id=None):
self.id = id
def __call__(self, func):
def wrapper(*args):
entity_ids = func(*args)
result = {}
for k, v in entity_ids.items():
if self.id:
if '_' + str(self.id) in k:
result.update({k: v})
return result
return wrapper
我在其他一些类方法中使用装饰器:
class SomeClass(object):
@Filter(id=None)
def get_ids(*args):
return result_sequence
如何在调用类方法时定义装饰器的参数:
>>>sc = SomeClass()
>>>sc.get_ids(*args) # I want to pass the id kwarg for Filter here
提前致谢
答案 0 :(得分:3)
您在类定义中应用了Filter
装饰器;你在<{1}}参数中传递了那里:
id
如果@Filter(id=None)
应该是其他内容,则需要在那里传递该值。
在id
行中创建Filter()
对象,然后调用。您还可以将代码重写为:
@Filter(id=None)
因为这是Python在处理装饰器时所做的事情。
替换class SomeClass(object):
def get_ids(*args):
return result_sequence
get_ids = Filter(id=None)(get_ids)
的{{1}}方法的返回值,您当时无法再指定Filter.__call__()
对象的参数。 get_ids
现在是装饰器返回的嵌套Filter()
函数。
如果要在调用修饰方法时指定SomeClass.get_ids()
,则需要更改wrapper()
签名以接受(可选)额外id
参数。因为您已经支持wrapper()
,所以您唯一的选择是添加id
catch-all参数以支持可选的关键字参数:
*args
此处,包装器的**kwargs
关键字参数不会直接使用def wrapper(*args, **kwargs):
id = kwargs.get('id', self.id)
entity_ids = func(*args)
result = {}
for k, v in entity_ids.items():
if id:
if '_' + str(id) in k:
result.update({k: v})
return result
,而是覆盖装饰器类上设置的self.id
值:
id
您可能还希望将任何关键字参数传递给包装函数;在那种情况下,我会使用:
id
此处,sc.get_ids(*args, id='foo')
关键字参数已删除,然后将剩余的关键字参数传递给包装函数。
答案 1 :(得分:2)
马丁的答案很详尽......或差不多。如果你想在调用装饰函数时能够覆盖“id”参数,你可以使用关键字arg来做,即:
class Filter(object):
def __init__(self, id=None):
self.id = id
def __call__(self, func):
def wrapper(*args, **kwargs):
id = kwargs.get("id", self.id)
entity_ids = func(*args)
result = {}
for k, v in entity_ids.items():
if id:
if '_' + str(id) in k:
result.update({k: v})
return result
return wrapper
但请注意,这意味着 1.如果要重载默认值,则必须将'id'作为关键字参数传递 2.你可以通过那种方式将kargs传递给装饰函数(但你还是没有通过kargs)
作为旁注(这里稍微加点但是......),你的包装函数的实现可以有所改进:
def wrapper(*args, **kwargs):
id = kwargs.get("id", self.id)
if not id:
# no need to go further
return {}
id = "_%s" % id
entity_ids = func(*args)
result = dict(
(k, v) for k, v in entity_ids.items()
if id in k
)
return result
答案 2 :(得分:0)
你必须改变你的包装器:
class Filter(object):
def __init__(self, id=None):
self.id = id
def __call__(self, func):
def wrapper(*args, **kwargs):
id = kwargs.get('id', self.id)
entity_ids = func(*args)
result = {}
for k, v in entity_ids.items():
if self.id:
if '_' + str(self.id) in k:
result.update({k: v})
return result
return wrapper
这使您有机会在调用包装方法时提供替代id
值:
class SomeClass(object):
@Filter(id=None)
def get_ids(*args):
return result_sequence
Filter
对象的__call__
方法现在返回一个包含额外id
kwarg的包装器。因此,您可以覆盖在类定义时给出的id
值:
sc = SomeClass()
sc.get_ids(*args, id='other') # I want to pass the id kwarg for Filter here