装饰器更改对象的状态

时间:2020-09-01 15:05:57

标签: python class inheritance decorator

我想创建一个在别处继承的类,并使用装饰器将特定方法存储在属性中。

我尝试了以下装饰器

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

并使用

定义类
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]

并使用以下内容测试课程

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一起应用),但是我可以

有任何线索吗?

2 个答案:

答案 0 :(得分:1)

filtermethod本身在创建类时被调用。因此,您需要在课堂上做些事情。

下面,我更改了代码以显示其工作方式。我要做的就是用装饰器用__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]

编辑:固定为优先

答案 1 :(得分:1)

如果运行代码并查看方法列表,您会发现它为空。装饰器将根据定义而不是实例初始化对方法进行装饰。看到这种情况的一种方法是在装饰器中放置一些图片并仅运行类定义。

为了达到您想要的目的,一种方法是将类变量用作方法注册表,并将装饰器稍作更改:

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]

请注意,self是在apply_filter方法中作为参数传递的。

运行时:

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

您将获得:

[7, 13, 9]