如何使用装饰器修改可变类变量?
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
属性的存储位置/方式存在误解。 Y
和 Z
没有自己的 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:查看我编辑的答案。由于您已经在编写装饰器,因此您可以让装饰器在类上创建新属性。 我认为这很优雅!以上是关于如何使用装饰器修改可变类变量?的主要内容,如果未能解决你的问题,请参考以下文章