首页 > 解决方案 > Python,如何添加另一个装饰器来过滤现有多装饰器的输出与python中的属性?

问题描述

我有 2 个现有的装饰器可以在 python 中运行,@ property和@safe_property。这些装饰器无法更改,它们是我无权访问的代码的一部分。


def safe_property(original_property):
    def wrap(self):
        try:
            return original_property(self)
        except AttributeError as e:
            pass
    return wrap

class MyClass(object):
    def __init__(self):
        pass

    @property
    @safe_property
    def do_func(self):
        print("inside do_func!")
        return [2,3,4]

通过调用函数:

a = MyClass()
print(a.do_func)

输出对我有好处!:


inside do_func!
[2, 3, 4]

现在,另一个特性来了,我试图根据(可选)附加参数过滤掉do_func的一些返回值。这意味着一些用户可以像往常一样继续工作并调用:

print(a.do_func)

而其他人可以使用过滤器调用:

print(a.do_func(True))

为了尝试这个,我创建了另一个名为my_decorator的装饰器,例如:

def my_decorator(*args, **kwargs):

    print(args)
    print(kwargs)
    def wrapper(*args):
        print(args)
        if args[1] == True:
            return
            # how do I return filter?
        else:
            return #without the filter?
    return wrapper


class MyClass(object):
    def __init__(self):
        pass

    @my_decorator
    @property
    @safe_property
    def do_func(self):
        print("inside do_func!")
        return [2,3,4]

此功能的当前输出是:

(<property object at 0x02AF0090>,)
{}
(<__main__.MyClass object at 0x00BCDBB0>, True)
None

我怎样才能只过滤奇数**例如,**来自: do_func的返回列表?

谢谢

标签: pythondecoratorpython-decorators

解决方案


您正在将装饰器应用于装饰器的输出@property。该装饰器产生一个property()对象,而不是一个函数。这是因为装饰器是从函数定义向外应用的;请参阅我对装饰器执行顺序的回答;so@safe_property首先应用,然后是@property,然后是@my_decorator

如果你想装饰 getter 函数,请将你的装饰器放在def语句的正上方,它将首先执行,无论你的装饰器返回什么都将传递给safe_property()装饰器(它添加了自己的包装函数):

@property
@safe_property
@my_decorator
def do_func(self):
    print("inside do_func!")
    return [2,3,4]

或者,看到 as@safe_property也会产生一个适合作为 getter 函数的包装器函数,您可以将装饰器放在@safe_property@property行之间以包装返回前者的包装器函数:

@property
@my_decorator
@safe_property
def do_func(self):
    print("inside do_func!")
    return [2,3,4]

无论哪种方式,您的装饰器包装器都会传递可调用来装饰,并且应该返回一个替换。属性 getter 仅 take self,您的替换也将被调用self,并且没有其他参数:

def my_decorator(func):
    def wrapper(self):  # a replacement getter function, so only self is passed in
        result = func(self)  # call the original getter
        if self.some_flag:  # you can access the instance in the wrapper
            # return only odd values from the getter
            return [i for i in result if i % 2]
        else:
            # otherwise return the values unchanged
            return result
    return wrapper

放在@my_decorator顶部是装饰一个property()对象,而不是一个函数,所以你需要专门处理传递这样一个对象(你可以在我之前写的答案@property中看到装饰器是如何工作的)。

例如,您可以从属性中提取property().fgetgetter ,然后返回适当的替换(这将是另一个property()对象):

def my_decorator(prop):
    getter = prop.fget
    def wrapper(self):  # a replacement getter function, so only self is passed in
        result = getter(self)  # call the original getter, taken from the property
        if self.some_flag:  # you can access the instance in the wrapper
            # return only odd values from the getter
            return [i for i in result if i % 2]
        else:
            # otherwise return the values unchanged
            return result
    # return a new property object, with the wrapper as the getter function
    # and copying across all other property attributes
    return property(wrapper, prop.fset, prop.fdel, prop.doc)

请注意,属性getter函数只会被传递self,没有其他可能的属性获取器参数。

然而,property直接处理一个对象并没有比将装饰器放低一行有任何优势,它只会使事情复杂化,因为必须添加prop.fget引用和property(...)返回值。


推荐阅读