当我实例化一个Python子类,它将覆盖基类的属性
代码如下:当我实例化一个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的文档,并没有找到解释。
是的,这是因为一个是不可变的,一个不是。或者说,是因为你在改变一个而不是另一个。 (重要的不是对象是否“可变”,重要的是你是否实际改变它)。这也是因为你使用类变量而不是实例变量(请参阅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
,则始终分配给实例属性;如果已经有一个具有相同名称的类属性,那么您的实例属性将隐藏该属性。
问题是x
和y
是不可变的,而z
是可变的,你可以改变它。
self.z.append()
不会取代z
,它只是增加一项到z
。
运行在print C().y, C().z
C()
(这是创建两个不同C
对象)后,self.z
不再计算为False
,因为它不再是空的。
如果颠倒你的两个print
行,你会发现输出是
1 [1]
2 [1]
当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
感谢有道理 – 2013-03-18 04:37:18