简体   繁体   English

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

[英]How to use a decorator to modify mutable class variables?

I have the following code: 我有以下代码:

class X(object):
    items = []

class Y(X):
    pass

class Z(X):
    pass

I'm trying to create a decorator that will add an object only to Y's "item" list. 我正在尝试创建一个装饰器,该装饰器仅将一个对象添加到Y的“项目”列表中。 So the following code, 所以下面的代码,

@addItems
class Y(X):
    pass

should append something (for sake of example, let's say the integer 1) to Y.items but not modify X.items, Z.items, or any other subclasses of X. Basically, Y.items should have items = [1] but X.items and Z.items should both have items = [] . 应该在Y.items后面附加一些内容(例如,整数1),但不要修改X.items,Z.items或X的任何其他子类。基本上,Y.items应该具有items = [1]但X.items和Z.items都应具有items = [] This is the decorator function definition I created that was supposed to achieve that goal: 这是我创建的装饰器函数定义,旨在实现该目标:

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

This does not work though - when I use the decorator, it ends up modifying the "items" list for X and all other subclasses of X. What gives? 但是,这不起作用-当我使用装饰器时,它最终会修改X和X的所有其他子类的“项目”列表。这有什么用? I know that class attributes in Python are shared among all instances of X, however, I did not know that subclasses were affected. 我知道Python的类属性在X的所有实例之间共享,但是,我不知道子类会受到影响。 Perhaps this behavior occurs only for mutable attributes? 也许这种行为只发生在可变属性上?

What is the correct way of achieving my goal? 实现目标的正确方法是什么?

The issue is not with the decorator. 问题不在于装饰器。 You have a misunderstanding about where/how the items attribute is stored. 您对items属性的存储位置/方式存在误解。 Y and Z do not have their own items attribute. YZ没有自己的items属性。 Rather, there is only one items attribute. 而是只有一个items属性。 You defined it on X , and if you do Y.items (or Z.items ), since the attribute is not found on the subclass, it is dynamically looked up on the superclass. 您在X上定义了它,如果执行Y.items (或Z.items ),则由于在子类上找不到该属性,因此会在超类上动态查找它。 If you do X.items is Y.items you will see that it evalutes to True --- there is only one items list. 如果你X.items is Y.items你会看到它演算值为真---只有一个items清单。 That means that when you modify it, all classes see the modification. 这意味着,当您对其进行修改时,所有类都会看到该修改。

If you want to give each class its own items list, you have to, well, give each class its own items list: 如果要为每个班级提供自己的items列表,则必须为每个班级提供自己的items列表:

class X(object):
    items  = []

class Y(object):
    items  = []

class Z(object):
    items  = []

Python never copies anything unless you tell it to. 除非您告知,否则Python绝不会复制任何内容。 Just because you define a subclass that inherits from a class with an attribute, that doesn't mean Python makes a new copy of the attribute for the subclass; 仅仅因为您定义了一个继承自具有属性的类的子类,但这并不意味着Python会为该子类创建该属性的新副本。 it just re-uses the same object from the superclass. 它只是重复使用超类中的同一对象。

If you want to "magically" give each class its own items attribute, you could do it inside the decorator: 如果您想“神奇地”为每个类赋予自己的项目属性,则可以在装饰器中进行操作:

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

This first sets the class's items to an empty list. 这首先将类的items设置为空列表。 This actually sets the attribute on the particular class it operates on, not on any sub- or superclass. 这实际上将属性设置在它所操作的特定类上,而不是在任何子类或超类上。 Now when you access that, it will work as you expect. 现在,当您访问它时,它将按预期工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM