如何使用装饰器修改可变类变量?

Posted

技术标签:

【中文标题】如何使用装饰器修改可变类变量?【英文标题】:How to use a decorator to modify mutable class variables? 【发布时间】:2013-02-22 05:51:27 【问题描述】:

我有以下代码:

class X(object):
    items = []

class Y(X):
    pass

class Z(X):
    pass

我正在尝试创建一个装饰器,它只会将一个对象添加到 Y 的“项目”列表中。所以下面的代码,

@addItems
class Y(X):
    pass

应该在 Y.items 上附加一些东西(例如,假设是整数 1),但不能修改 X.items、Z.items 或 X 的任何其他子类。基本上,Y.items 应该有items = [1]但是 X.items 和 Z.items 都应该有items = []。这是我为实现该目标而创建的装饰器函数定义:

def addItems(cls):
    cls.items.append(1)
    return cls

但这不起作用 - 当我使用装饰器时,它最终会修改 X 和所有其他 X 子类的“项目”列表。什么给出了?我知道 Python 中的类属性在 X 的所有实例之间共享,但是,我不知道子类受到影响。也许这种行为只发生在可变属性上?

实现我的目标的正确方法是什么?

【问题讨论】:

由于 Y 没有 items 属性,它搜索超类并找到 X。总体而言,您要实现什么目标? 【参考方案1】:

问题不在于装饰器。您对 items 属性的存储位置/方式存在误解。 YZ 没有自己的 items 属性。相反,只有一个items 属性。您在X 上定义了它,如果您在Y.items(或Z.items)上定义它,由于在子类上找不到该属性,因此在超类上动态查找它。如果您执行X.items is Y.items,您将看到它评估为True --- 只有一个items 列表。这意味着当你修改它时,所有类都会看到修改。

如果你想给每个班级自己的items 列表,你必须给每个班级自己的items 列表:

class X(object):
    items  = []

class Y(object):
    items  = []

class Z(object):
    items  = []

除非你告诉它,否则 Python 从不复制任何东西。仅仅因为您定义了一个继承自具有属性的类的子类,这并不意味着 Python 会为子类创建一个新的属性副本。它只是重用了超类中的同一个对象。

如果你想“神奇地”为每个类赋予它自己的 items 属性,你可以在装饰器中做到这一点:

def addItems(cls):
    cls.items = []
    cls.items.append(1)
    return cls

这首先将类的items 设置为一个空列表。这实际上设置了它所操作的特定类的属性,而不是任何子类或超类。现在,当您访问它时,它将按您的预期工作。

【讨论】:

你的解释很有道理。您的解决方案效果很好,但是有没有什么神奇的方法可以实现这种效果,而不必为每个子类重新指定属性?原因是在我的应用程序中,子类是由应用程序的用户定义的,我试图尽量减少他们需要编写的样板代码量。 @trinth:查看我编辑的答案。由于您已经在编写装饰器,因此您可以让装饰器在类上创建新属性。 我认为这很优雅!

以上是关于如何使用装饰器修改可变类变量?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用装饰器来修改 *args 的类型?

如何在 spyne 中使用装饰器继承类

Python - 元类装饰器 - 如何使用 @classmethod

如何在 Python 的方法装饰器中使用类属性?

如何将实例变量传递给打字稿装饰器参数?

如何在 TypeScript 中使用装饰器