我正在学习this和this来理解类属性。但与以下代码片段的输出混淆。
class A:
aliases = None
name = None
def __init__(self,name):
self.name = name
self.aliases = set([name])
def add_aliases(self,a):
self.aliases.add(a)
def __repr__(self):
return str(self.name) + str(self.aliases)
arr = []
for i in range(3):
arr.append(A(i))
arr[-1].add_aliases(i+1)
for item in arr:
print item
A.aliases = set([]) ##Modify the static element of class
for item in arr:
print item
Python解释器:2.7.9
输出
0set([0, 1])
1set([1, 2])
2set([2, 3])
0set([0, 1])
1set([1, 2])
2set([2, 3])
我期待这样的东西作为输出。
0set([2, 3])
1set([2, 3])
2set([2, 3])
0set([])
1set([])
2set([])
答案 0 :(得分:5)
并且解释是,当我们写self.aliases = set([])
时,我们实际上创建 新实例属性,隐藏类属性
因此,如果我们按照以下方式设置__init__
函数,我们就会获得预期的输出。
def __init__(self,name):
self.name = name
A.aliases = set([name]) #using the class attribute directly
还请考虑以下代码段:
class A:
aliases = set([])
name = None
def __init__(self,name):
self.name = name
self.aliases.add(name) # using the class attribute indirectly.
def add_aliases(self,a):
self.aliases.add(a)
def __repr__(self):
return str(self.name) + str(self.aliases)
因为在这种情况下,我们不创建实例属性,所以没有阴影。问题中的测试代码会产生这样的输出:
0set([0, 1, 2, 3])
1set([0, 1, 2, 3])
2set([0, 1, 2, 3])
0set([])
1set([])
2set([])
这是预期的,因为类属性在所有实例中共享。
此处A.alias
也可称为self.alias
内的init
或任何其他功能。并且因为它没有影响静态属性,所以给出了预期的输出 - 当所有对象共享一个公共属性时。
在使用string
等不可变数据结构时,不了解此概念的人不会注意到任何事情。但是对于list
和dictionary
这样的数据结构,这可能会让人感到惊讶。
另请考虑以下init
的定义。
def __init__(self,name):
self.name = name
self.aliases.add(name) # referring to class attribute
self.aliases = set([]) # creating a instance attribute
在这种情况下,它最终创建了instance attribute
,测试代码产生的输出是:
0set([1])
1set([2])
2set([3])
0set([1])
1set([2])
2set([3])
形成这一切,我的学习是:
始终使用对象名称引用具有类名称和实例属性的class属性,即在表示类属性A.aliases
时写入aliases
,不要写self.aliases
来间接引用{ {1}} 强>