【问题标题】:Decorator to change the state of an object装饰器改变对象的状态
【发布时间】:2020-12-20 17:29:06
【问题描述】:

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

我尝试了以下装饰器

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 连续应用)但我根本做不到。

有什么线索吗?

【问题讨论】:

  • 您将包装方法添加到 self.methods 的唯一时间是您实际调用包装方法时 - 例如,method1method2

标签: python class inheritance decorator


【解决方案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]

编辑:修复覆盖

【讨论】:

  • 谢谢。我可以用新的子类foo3(foo2) 覆盖method1 吗?
  • 使用新课程foo3(foo2) 我无法正确覆盖method1method2 变为非活动状态或两种方法都不起作用
  • @mccandar 立即尝试 - 修复它
【解决方案2】:

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

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

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]

【讨论】:

    猜你喜欢
    • 2022-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-22
    • 1970-01-01
    • 1970-01-01
    • 2023-01-09
    相关资源
    最近更新 更多