2010-06-15 51 views
1

有人可以解释为什么Python执行以下操作吗?Python和对象/类attrs - 发生了什么?

>>> class Foo(object): 
... bar = [] 
... 
>>> a = Foo() 
>>> b = Foo() 
>>> a.bar.append(1) 
>>> b.bar 
[1] 
>>> a.bar = 1 
>>> a.bar 
1 
>>> b.bar 
[1] 
>>> a.bar = [] 
>>> a.bar 
[] 
>>> b.bar 
[1] 
>>> del a.bar 
>>> a.bar 
[1] 

这很让人困惑!

+2

哪一部分是混淆吗? – 2010-06-15 12:52:53

+2

真的吗?很容易看出,如果有人在几乎任何其他OOP语言中花费时间,“bar”如何成为本地实例变量。 – Oli 2010-06-15 13:03:04

+1

@Oli:没有时间在Python教程? – SilentGhost 2010-06-15 13:05:59

回答

0

正如其他人所说,写入的代码会创建一个类变量而不是实例变量。您需要在__init__中分配以创建实例变量。

希望这个注解你的代码的副本是有帮助的解释发生了什么事情,在每一个阶段:

>>> class Foo(object): 
... bar = []   # defines a class variable on Foo (shared by all instances) 
... 
>>> a = Foo() 
>>> b = Foo() 
>>> a.bar.append(1)  # appends the value 1 to the previously empty list Foo.bar 
>>> b.bar    # returns the value of the class variable Foo.bar 
[1] 
>>> a.bar = 1   # binds 1 to the instance variable a.bar, masking the access 
>>> a.bar    # you previously had to the class variable through a.bar 
1 
>>> b.bar    # b doesn't have an instance variable 'bar' so this still 
[1]      # returns the class variable 
>>> a.bar = []   # bind a's instance variable to to an empty list 
>>> a.bar 
[] 
>>> b.bar    # b doesn't have an instance variable 'bar' so this still 
[1]      # returns the class variable 
>>> del a.bar   # unbinds a's instance variable unmasking the class variable 
>>> a.bar    # so a.bar now returns the list with 1 in it. 
[1] 

此外,打印出的Foo.bar值(类变量通过类而不是通过一个实例访问)在你的每个陈述之后可能有助于澄清发生了什么。

7

这是因为你写的方式,bar是一个类变量而不是一个实例变量。

要定义一个实例变量,在构造函数将其绑定:

class Foo(object): 
    def __init__(self): 
    self.bar = [] 

注意,它现在属于Fooself)的单个实例,而不是Foo类,你会看到结果你期待何时分配给它。

0

当您在类中声明一个元素时,它将被该类的所有实例共享。为了使属于每个实例适当的类的成员,分别在__init__创建它如下所示:

class Foo(object): 
    def __init__(self): 
     self.bar = [] 
0

在开始时,bar是一个类变量,它是ab之间共享,两者a.barb.bar指的是同一个对象。

当您将新值分配给a.bar时,它不会覆盖类变量,它将向a对象添加新的实例变量,并在访问a.bar时隐藏类变量。如果删除a.bar(实例变量),则a.bar将再次解析为类变量。

b.bar另一方面总是指类变量,它不会受a对象上额外的bar或任何赋值给它的值的影响。

要设置类变量,您可以通过类本身访问:

Foo.bar = 1 
0
>>> class Foo(object): 
... bar = [] 
... 

bar是共享类变量,而不是一个实例变量。我相信这会处理你大部分的困惑。要使其成为实例var,请在其他答案中将其定义在课程的__init__中。

>>> a = Foo() 
>>> b = Foo() 
>>> a.bar.append(1) 
>>> b.bar 
[1] 

这就是证明。

>>> a.bar = 1 
>>> a.bar 
1 
>>> b.bar 
[1] 

现在您已将a.bar重新定义为实例变量。这是默认情况下在外部定义变量时发生的情况。

>>> a.bar = [] 
>>> a.bar 
[] 
>>> b.bar 
[1] 
>>> del a.bar 
>>> a.bar 
[1] 

再次相同。 b.bar仍然是共享类变量。

0

与此相关的,你应该知道这个陷阱,你有时可能会很快看到的:

class A: 
    def __init__(self, mylist = []): 
     self.mylist = mylist 


a = A() 
a2 = A() 

a.mylist.append(3) 
print b.mylist #prints [3] ??? 

这迷惑了很多人,并与代码是如何解释的事情。 Python实际上首先解释函数标题,因此它评估__init__(self, mylist = [])并将对该列表的引用存储为默认参数。这意味着A的所有实例(除非提供自己的列表)都会引用原始列表。做这样的事情,正确的代码将

class A: 
    def __init__(self, mylist=None): 
     if mylist: 
     self.mylist = mylist 
     else: 
     self.mylist = [] 

,或者如果你想有一个较短的表达式,你可以使用三元语法:

​​