2013-03-18 83 views
3

代码如下:当我实例化一个Python子类,它将覆盖基类的属性

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 

输出是

2 [2] 
1 [2] 

为什么z覆盖但不y?是因为它不是不可变的类型吗?我看着python的文档,并没有找到解释。

回答

3

是的,这是因为一个是不可变的,一个不是。或者说,是因为你在改变一个而不是另一个。 (重要的不是对象是否“可变”,重要的是你是否实际改变它)。这也是因为你使用类变量而不是实例变量(请参阅this question)。

在你的类定义中,你创建了三个类变量,在类的所有实例之间共享。在创建类的实例后,如果在该实例上执行self.x,它将不会在实例上找到x作为属性,但会在该类上查找它。同样,self.z会查看课程并找到课程上的一个。请注意,因为您制作了z一个类变量,所以只有一个一个列表在该类的所有实例(包括所有子类的所有实例,除非它们覆盖了z)之间共享。

当你做self.y = self.x,创建一个新的属性,一个实例属性,仅在实例。

但是,当您执行self.z.append(...)时,您不会创建新的实例变量。相反,self.z查找存储在该类上的列表,然后append改变该列表。没有“覆盖”。只有一个列表,当你做追加时你正在改变它的内容。 (只有一个项目被追加,因为你有if not self.z,所以在你追加一个后,这是错误的,随后的调用不会再追加任何东西。)

结果是,读取属性的值不一样分配给它。当您读取self.x的值时,您可能正在检索存储在该类中并在所有实例之间共享的值。但是,如果将值分配给self.x,则始终分配给实例属性;如果已经有一个具有相同名称的类属性,那么您的实例属性将隐藏该属性。

+0

感谢有道理 – 2013-03-18 04:37:18

0

问题是xy是不可变的,而z是可变的,你可以改变它。

self.z.append()不会取代z,它只是增加一项到z

运行在print C().y, C().zC()(这是创建两个不同C对象)后,self.z不再计算为False,因为它不再是空的。

如果颠倒你的两个print行,你会发现输出是

1 [1] 
2 [1] 
0

当Python评估的class A它实例化一个对象list并将其分配到z身体。因为每个子类不重写,并且由于list对象由在Python参考存储,所有这三类共享相同的z列表,让你实例化的第一个得到填充z,然后休息一下就不管放在那里。虽然你改变了内容列表,您没有更改z指列表。

因为你直接分配到整数对象的内部字典,取代了以前的值这并不影响y

为了解决这个问题,创建在构造函数中的数组,从而分配:

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 
相关问题