Python,如何添加另一个装饰器来过滤现有多装饰器的输出与python中的属性?

Posted

技术标签:

【中文标题】Python,如何添加另一个装饰器来过滤现有多装饰器的输出与python中的属性?【英文标题】:Python, How to add another decorator to filter the output of the existing multi decorators with property in python? 【发布时间】:2019-09-20 22:32:31 【问题描述】:

我有 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?

谢谢

【问题讨论】:

问题在于如何嵌套装饰器。 @property 应该是最顶层的装饰器,如果你的其他装饰器处理函数。 property() 将函数转换为其他东西(属性;这是一个描述符),因此任何 above 的装饰器都需要处理属性而不是函数。 【参考方案1】:

您正在将您的装饰器应用于@property 装饰器的输出。该装饰器生成property() object,而不是函数。这是因为装饰器是从函数定义中向外应用的;见我的answer on decorator execution order;所以首先应用@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]

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

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

无论哪种方式,您的装饰器包装器都会传递可调用来装饰,并且应该返回一个替换。属性获取器只使用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 装饰器在@ 中的工作方式987654323@)。

例如您可以从property().fget 属性中提取 getter,然后返回适当的替换(这将是另一个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(...) 返回值。

【讨论】:

谢谢,我正在尝试了解您的解决方案,我正在执行您的代码,当我尝试发送 时,您的代码中的 some_flag 是什么print(a.do_func(True)) @raptor0102 这是实例上的示例属性。它不存在,我只是想表明您可以访问实例本身。 谢谢,你帮我解决了我的问题,现在代码可以正常工作了。感谢您的努力

以上是关于Python,如何添加另一个装饰器来过滤现有多装饰器的输出与python中的属性?的主要内容,如果未能解决你的问题,请参考以下文章

Python开发第十四篇装饰器

如何创建一个装饰器来装饰生成器函数? [关闭]

如何在 Python 中使用类装饰器来混合行为?

你如何使用 Flask-Login 和自定义 Python 装饰器来分配用户权限?

在 Python 中,是不是可以使用相同的装饰器来装饰类和非类方法?

如何使用@property 装饰器来限制列表中的值