简体   繁体   English

当我实例化一个python子类时,它会覆盖基类的属性

[英]When I instantiate a python subclass it overwrites base class' attribute

The code looks like: 代码如下:

class A(object):
    x = 0
    y = 0
    z = []
    def __init__(self):
        super(A, self).__init__()
        if not self.y:
            self.y = self.x
        if not self.z:
            self.z.append(self.x)

class B(A):
    x = 1

class C(A):
    x = 2

print C().y, C().z
print B().y, B().z

The output is 输出是

2 [2]
1 [2]

Why is z overwritten but not y ? 为什么z被覆盖而不是y Is it because it's not a immutable type? 是因为它不是一个不可变的类型吗? I looked on python's documentation and didn't find an explanation. 我查看了python的文档,但没有找到解释。

Yes, it's because one is immutable and one isn't. 是的,这是因为一个是不可变的,一个不是。 Or rather, it's that you are mutating one and not the other. 或者更确切地说,就是你正在改变一个而不是另一个。 (What matters isn't whether the object "is mutable", what matters is whether you actually mutate it.) It's also because you're using class variables instead of instance variables (see this question ). (重要的不是对象“是否可变”,重要的是你是否真的改变了它。)这也是因为你使用的是类变量而不是实例变量(参见这个问题 )。

In your class definition, you create three class variables, shared among all instances of the class. 在类定义中,您将创建三个类变量,在类的所有实例之间共享。 After creating an instance of your class, if you do self.x on that instance, it will not find x as an attribute on the instance, but will look it up on the class. 创建类的实例后,如果在该实例上执行self.x ,它将不会在实例上找到x作为属性,但会在类上查找它。 Likewise self.z will look up on the class and find the one on the class. 同样, self.z会查看课程并在课堂上找到一个。 Note that because you made z a class variable, there is only one list that is shared among all instances of the class (including all instances of all subclasses, unless they override z ). 请注意,因为您使z成为一个类变量,所以只有一个列表在该类的所有实例之间共享(包括所有子类的所有实例,除非它们覆盖z )。

When you do self.y = self.x , you create a new attribute, an instance attribute, on only the instance. 当你执行self.y = self.x ,只在实例上创建一个属性,一个实例属性。

However, when you do self.z.append(...) , you do not create a new instance variable. 但是,当您执行self.z.append(...) ,不会创建新的实例变量。 Rather, self.z looks up the list stored on the class, and then append mutates that list. 相反, self.z查找存储在类中的列表,然后append该列表的变体。 There is no "overwriting". 没有“覆盖”。 There is only one list, and when you do the append you are changing its contents. 只有一个列表,当您执行追加时,您正在更改其内容。 (Only one item is appended, because you have if not self.z , so after you append one, that is false and subsequent calls do not append anything more.) (只附加一个项目,因为if not self.z ,那么在附加一个项目之后,这是假的,后续调用不会追加任何其他内容。)

The upshot is that reading the value of an attribute is not the same as assigning to it. 结果是读取属性的值与分配属性的值不同。 When you read the value of self.x , you may be retrieving a value that is stored on the class and shared among all instances. 当您读取self.x的值时,您可能正在检索存储在类中并在所有实例之间共享的值。 However, if you assign a value to self.x , you are always assigning to an instance attribute; 但是,如果为self.x分配值,则始终分配给实例属性; if there is already a class attribute with the same name, your instance attribute will hide that. 如果已经有一个具有相同名称的类属性,则您的实例属性将隐藏该属性。

The issue is that x and y are immutable, while z is mutable, and you mutate it. 问题是xy是不可变的,而z是可变的,你可以改变它。

self.z.append() does not replace z , it just adds an item to z . self.z.append()不替换z ,它只是向z添加一个项目。

After you run C() in print C().y, C().z (which is creating two different C objects), self.z no longer evaluates to False because it is no longer empty. print C().y, C().z (创建两个不同的C对象)中运行C()后, self.z不再计算为False因为它不再为空。

If you reverse your two print lines, you'll find the output is 如果你反转两条print线,你会发现输出是

1 [1]
2 [1]

When Python evaluates the body of class A it instantiates a single list object and assigns it to z . 当Python评估class A的主体时,它实例化一个list对象并将其分配给z Since the subclasses don't override it, and since list objects are stored by reference in Python, all three classes share the same z list, so the first one you instantiate gets to populate z and then the rest just get whatever was put there. 由于子类不会覆盖它,并且由于list对象在Python中通过引用存储,因此所有三个类共享相同的z列表,因此您实例化的第一个用于填充z ,然后其余的只是获取放在那里的任何内容。 Although you changed the contents of the list, you did not change which list z refers to. 虽然您更改了列表的内容 ,但您没有更改z引用的列表。

This does not affect y because you're assigning the integer directly into the object's internal dictionary, replacing the previous value. 这不会影响y因为您将整数直接分配到对象的内部字典中,替换以前的值。

To fix this, create your array inside the constructor, thus assigning: 要解决此问题,请在构造函数中创建数组,从而分配:

class A(object):
    x = 0
    y = 0
    z = None
    def __init__(self):
        super(A, self).__init__()
        if not self.y:
            self.y = self.x
        if not self.z:
            # create a new list
            z = [self.x]

class B(A):
    x = 1

class C(A):
    x = 2

print C().y, C().z
print B().y, B().z

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

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