改变对象状态的装饰器

Decorator to change the state of an object

我想创建一个class继承到别处,用装饰器把具体的方法存放在一个属性中

我尝试了以下装饰器

def filtermethod(f):
    def wrapped(self, *args, **kwargs):
        self.methods.append(f)
        return f(self, *args, **kwargs)
    return wrapped

并用

定义classes
class foo:
    def __init__(self):
        self.methods = []

    def apply_filter(self, x):
        for fun in self.methods:
            x = fun(x)
        return x

class foo2(foo):
    @filtermethod
    def method1(self, x):
        return [i for i in x if i > 5]

    @filtermethod
    def method2(self, x):
        return [i for i in x if i < 18]

并使用以下

测试class
foo2().apply_filter([1, 4, 1, 5, 73, 25, 7, 2, 26, 13, 46, 9])

并希望所有装饰函数都应用到参数,但我明白了

[1, 4, 1, 5, 73, 25, 7, 2, 26, 13, 46, 9]

而不是

[7,13,9]

基本上,我想将每个用 @filtermethod 修饰的函数附加到属性 self.methods,(与 self.apply_filter 连续应用) 但我根本做不到。

有什么线索吗?

filtermethod 本身在创建 class 时被调用。因此,您需要在 class 级别上做一些事情。

下面,我更改了您的代码以展示其工作原理。我所做的就是让装饰器用一个变量标记它包装的函数,__init_subclass__ 获取并添加到 _methods(作为未绑定方法)。

def filtermethod(f):
    f._is_filter_method = True
    return f


class foo:
    def __init_subclass__(cls, **kwargs):
        funcs = (getattr(cls, v) for v in dir(cls))
        cls._methods = [f for f in funcs if hasattr(f, '_is_filter_method')]

    def apply_filter(self, x):
        for fun in self._methods:
            x = fun(self, x)
        return x


class foo2(foo):
    @filtermethod
    def method1(self, x):
        return [i for i in x if i > 5]

    @filtermethod
    def method2(self, x):
        return [i for i in x if i < 18]


class foo3(foo2):
    @filtermethod
    def method1(self, x):
        return [i for i in x if i == 4]

编辑:修复了覆盖

如果您 运行 您的代码并查看方法列表,您会发现它是空的。装饰器将在定义而不是实例初始化时装饰方法。看到这种情况发生的一种方法是在装饰器中放置一些印刷品,并且 运行 仅 class 定义。

为了实现您想要的效果,一种方法是使用 class 变量作为方法注册表并稍微更改装饰器:

def filtermethod(registry):
    def inner(f):
        registry.append(f)
        def wrapped(*args, **kwargs):
            return f(*args, **kwargs)
        return wrapped
    return inner

class foo:
    methods = []

    def apply_filter(self, x):
        for fun in foo.methods:
            x = fun(self, x)
        return x

class foo2(foo):
    @filtermethod(foo.methods)
    def method1(self, x):
        return [i for i in x if i > 5]

    @filtermethod(foo.methods)
    def method2(self, x):
        return [i for i in x if i < 18]

请注意 selfapply_filter 方法中作为参数传递。

当你运行:

foo2().apply_filter([1, 4, 1, 5, 73, 25, 7, 2, 26, 13, 46, 9])

您将获得:

[7, 13, 9]