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

标签 python decorator python-decorators

我有 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() 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]

无论哪种方式,你的装饰器包装器都会传递可调用对象来装饰,并且应该返回一个替换。属性 getter 只接受 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() 对象,而不是函数,因此您需要专门处理传递这样的对象(您可以在 an answer I wrote before 中了解 @property 装饰器的工作原理)。

例如您可以从 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,属性 getter 没有其他参数。

但是,直接处理 property 对象并不比将装饰器放置在低一行上有任何优势,它只会使事情变得复杂,因为必须添加 prop.fget 引用和 property(...) 返回值。

关于Python,如何添加另一个装饰器来过滤具有Python属性的现有多重装饰器的输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55951832/

相关文章:

python - 使用 "Python Descriptors"的条件动态验证器

python - Pandas 在列上旋转

python - 如何使用 python 子进程模块执行 .profile 中定义的 bash 函数?

python - 大双数组中小双数组的最佳匹配

javascript - 我可以在对象属性上使用装饰器吗?

Python装饰器函数调用深度,结构化输出

python - 以侵入性最小、最隐形的方式检测对象使用情况

使用 line_profiler 进行 Python 分析 - 即时删除 @profile 语句的巧妙方法?

python - 查找最接近未完全排序的列表中的值的项目的索引

Python 装饰器在调用函数之前的导入期间运行