调用装饰函数时将参数传递给装饰器

时间:2013-10-18 09:46:14

标签: python

我是第一次使用装饰器的经历,我创建了一个装饰器类,用于过滤目标函数的结果,默认情况下会返回某个序列:

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 

提前致谢

3 个答案:

答案 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 
相关问题