Python类中的类属性阴影

时间:2016-06-27 06:28:28

标签: python

我正在学习thisthis来理解类属性。但与以下代码片段的输出混淆。

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([])

1 个答案:

答案 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等不可变数据结构时,不了解此概念的人不会注意到任何事情。但是对于listdictionary这样的数据结构,这可能会让人感到惊讶。

另请考虑以下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}}