3

我是第一次使用装饰器,我创建了一个装饰器类来过滤目标函数的结果,它默认返回一个特定的序列:

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 

提前致谢

4

3 回答 3

3

Filter在类定义中应用了装饰器;您在那里传递了id参数:

@Filter(id=None)

如果id应该是别的东西,你需要在那里传递那个值。

Filter()对象在该@Filter(id=None)行中创建,然后被调用。您还可以重写代码以读取:

class SomeClass(object):
    def get_ids(*args):
        return result_sequence
    get_ids = Filter(id=None)(get_ids)

因为这就是 Python 在处理装饰器时所做的事情。

替换的是方法的返回值,此时不能再为对象指定参数。现在是您的装饰器返回的嵌套函数。Filter.__call__()get_idsFilter()SomeClass.get_ids()wrapper()

如果您想id在调用装饰方法时指定,则需要更改wrapper()签名以接受(可选)额外id参数。因为您已经支持*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的关键字参数不是直接使用,而是覆盖装饰器类上设置的值:idid

sc.get_ids(*args, id='foo')

您可能还想将任何关键字参数传递给包装函数;在这种情况下,我会使用:

def wrapper(*args, **kwargs):
    id = kwargs.pop('id', self.id)
    entity_ids = func(*args, **kwargs)
    result = {}
    for k, v in entity_ids.items():
        if id:
            if '_' + str(id) in k:
                result.update({k: v})
    return result

在这里,在将剩余的关键字id参数传递给包装函数之前,删除了关键字参数。

于 2013-10-18T09:52:57.490 回答
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反正)

作为旁注(这里有点 OT 但是......),您的包装函数的实现可以有所改进:

        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
于 2013-10-18T10:32:43.727 回答
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__现在返回一个包装器,它有一个额外的idkwarg。因此,您可以覆盖id在类定义时给出的值:

sc = SomeClass()
sc.get_ids(*args, id='other')  # I want to pass the id kwarg for Filter here 
于 2013-10-18T10:16:05.617 回答