2010-06-15 36 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 = [] 

,或者如果你想有一個較短的表達式,你可以使用三元語法:

​​