类方法上的条件属性装饰器

Posted

技术标签:

【中文标题】类方法上的条件属性装饰器【英文标题】:Conditional property decorator on class method 【发布时间】:2021-10-22 06:42:25 【问题描述】:

我有一个 python 数据类,我想根据一些全局变量有条件地分配某些装饰器。

在脚本顶部检查条件,但对于下面的示例,我只是提供了该检查的结果。如果检查是True,我想给这些方法@functools.cached_property 装饰器。如果是False,我只希望他们收到标准的@property 装饰器。

我一直遇到的问题是我无法完全弄清楚如何(或者甚至可能)使它作为一个简单的装饰器工作。在调用或操作test.x_times_y 时,我通常会收到有关方法对象的错误,并且我不确定是否可以编写函数,使得在下面的示例中调用test.x_times_y 实际上会产生我想要的结果。

import functools
import dataclasses

_value_checked = False


def myDecorator(func):
    def decorator(self):
        if not _value_checked:
            return property(func)(self)
        else:
            return functools.cached_property(func)(self)

    return decorator


@dataclasses.dataclass
class MyClass():
    x: int
    y: int
    z: int = 0

    @myDecorator
    def x_times_y(self):
        return self.x*self.y


test = MyClass(5,6,7)

我还想避免使用 getter 和 setter 方法,所以我希望这是可能的。我在这里查看了很多答案(例如this one),但无法找到实际有效的答案,因为大多数不适用于装饰方法。我为此使用 Python 3.8。

【问题讨论】:

为什么不myDecorator = functools.cached_property if _value_checked else property 【参考方案1】:

你想要的行为可以通过一个简单的条件赋值来实现:

my_decorator = functools.cached_property if _value_checked else property

if _value_checked:
    my_decorator = functools.cached_property
else:
    my_decorator = property

如果你需要在每次使用装饰器时做更复杂的逻辑,你可以使用返回你想要的装饰器的函数:

def my_decorator():
    if not _value_checked:
        return property
    else
        return functools.cached_property

不需要复杂的参数转发。只需委托给您已有的装饰器即可。

【讨论】:

当然!这行得通。我想我尝试了一个类似的解决方案,但可能使用了 property() 或其他东西并放弃了它,假设我想要的东西必须更复杂。谢谢!【参考方案2】:

您编写myDecorator 的方式只能应用于接受单个参数的函数:

def myDecorator(func):
    def decorator(self):
        if not _value_checked:
            return property(func)(self)
        else:
            return functools.cached_property(func)(self)

    return decorator

最简单的方法是只返回函数而不是在包装器中调用它:

def myDecorator(func):
    if not _value_checked:
        return property(func)
    else
        return functools.cached_property(func)

如果您确实需要构建包装器,通常正确的方法是让包装器函数采用任意 *args, **kwargs 参数,以便您可以使用它们调用包装器函数:

def myDecorator(func):
    def wrapper(*args, **kwargs):
        if not _value_checked:
            return property(func)(*args, **kwargs)
        else:
            return functools.cached_property(func)(*args, **kwargs)

    return wrapper

请注意,myDecorator 返回的函数本身不是装饰器,它是替换装饰函数的包装器——这就是我在上述实现中重命名它的原因。

还请注意,这些实现之间存在实际差异,即第二个版本(带有包装器)评估_value_checked在调用函数时,而第一个版本评估在定义函数时。如果该值是一个常量,则无关紧要,但如果您希望能够在运行时切换它并动态更改行为,则需要第二个版本。

【讨论】:

以上是关于类方法上的条件属性装饰器的主要内容,如果未能解决你的问题,请参考以下文章

从0开始的TypeScriptの十二:装饰器

在 python memoization 装饰器类中设置 get/set 属性

装饰器、装饰器类与类装饰器(三)

装饰器类学习小结

Python__new__方法定制属性访问描述符与装饰器

使用类作为方法装饰器[重复]