Python类中的类属性阴影

jha*_*a-G 5 python

我正在学习这个,这是为了理解类的属性.但与以下代码片段的输出混淆.

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  
Run Code Online (Sandbox Code Playgroud)

Python解释器:2.7.9

输出是

0set([0, 1])
1set([1, 2])
2set([2, 3])
0set([0, 1])
1set([1, 2])
2set([2, 3])
Run Code Online (Sandbox Code Playgroud)

我期待这样的东西作为输出.

0set([2, 3])
1set([2, 3])
2set([2, 3])
0set([])
1set([])
2set([])
Run Code Online (Sandbox Code Playgroud)

jha*_*a-G 5

并且解释是,当我们编写时,self.aliases = set([])我们实际上正在创建一个新的实例属性,遮蔽了类属性.

因此,如果我们__init__按照以下方式进行操作,我们就会获得预期的输出.

def __init__(self,name):
    self.name = name
    A.aliases = set([name])  #using the class attribute directly
Run Code Online (Sandbox Code Playgroud)

还请考虑以下代码段:

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)
Run Code Online (Sandbox Code Playgroud)

因为在这种情况下,我们没有创建实例属性,所以没有阴影.问题中的测试代码会产生这样的输出:

0set([0, 1, 2, 3])
1set([0, 1, 2, 3])
2set([0, 1, 2, 3])
0set([])
1set([])
2set([])
Run Code Online (Sandbox Code Playgroud)

这是预期的,因为类属性在所有实例之间共享.

这里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
Run Code Online (Sandbox Code Playgroud)

而且这种情况也是如此,它最终创建instance attribute和测试代码产生的输出是:

0set([1])
1set([2])
2set([3])
0set([1])
1set([2])
2set([3])
Run Code Online (Sandbox Code Playgroud)

形成这一切我的学习是:

始终使用类名和实例属性引用类属性和对象名称,即A.aliases在表示类属性时写入aliases,不要写入self.aliases间接引用self.aliases