2014-03-27 109 views
2

爲什麼下面的例子表現不同?Python中的靜態類變量和`self`

實施例1:foo似乎表現得象一個類變量的特定於各種對象

class A: 
    foo = 1 
a, b = A(), A() 
a.foo = 5 
print b.foo 
---------------- 
Output: 1 

實施例2:foo似乎表現得象一個靜態類變量,它是對所有對象是相同的。也許這種行爲與作爲指針工作的列表有關。

class A: 
    foo = [] 
a, b = A(), A() 
a.foo.append(5) 
print b.foo 
---------------- 
Output: [5] 

實施例3:不工作

class A: 
    self.foo = [] 
a, b = A(), A() 
a.foo.append(5) 
print b.foo 
---------------- 
Output: Error 
+0

不要忘記接受,+2代表你! :) –

回答

5

前兩個例子都是類屬性。他們看起來不同的原因是因爲在兩種情況下你都沒有做同樣的事情:你在第一種情況下分配一個新值並在第二種情況下修改現有值。

請注意,您在前兩個示例中沒有做同樣的事情。在第一個例子中,你做a.foo = 5,分配一個新的值。在第二個例子中,如果你做了類似的事情,分配a.foo = [5],你會看到與第一個例子中相同的結果。但是,您改變了現有的名單a.foo.append(5),所以行爲是不同的。 a.foo = 5僅更改變量(即,它指向什麼值); a.foo.append(5)更改本身。

(請注意,在第一個例子中沒有辦法做第二個例子的等價物,也就是說,沒有任何東西像a.foo.add(1)加1到5那是因爲整數是不可變的,但是列表是。沒有列出「是」可變的,但你突變之一。換句話說,這不要緊,你可以什麼用列表,它重要的是你在特定的代碼實際上做的。)

另請注意,儘管在類定義中定義的foo是類屬性,但在執行a.foo = 5時,您正在實例上創建新屬性。它恰好與類屬性具有相同的名稱,但它不會更改class屬性的值,這是b.foo仍然可以看到的值。

最後一個例子不起作用,因爲就像在前兩個例子中一樣,class塊內的代碼位於類作用域。沒有self,因爲在定義類時還沒有實例。

在StackOverflow上有很多關於這個問題的其他許多問題,我敦促您搜索並閱讀其中的一堆,以更全面地瞭解它的工作原理。

+0

我明白了例3和'self'。 'self'作爲我們定義的類的實際對象,所以在這一點上沒有任何對象。但是,我沒有看到前兩個例子的區別。我顯然可以改變整數的內容,因爲在第一個例子中打印a.foo = 5。你能澄清一點嗎? – Konstantin

+1

@Konstantin:這不會改變整數*值*,它正在改變*變量*指向的內容。請參閱[本頁](http://foobarnbaz.com/2012/07/08/understanding-python-variables/),瞭解變量是否在Python中的簡單解釋。 – BrenBarn

+1

「他們看起來不同的原因是因爲您可以更改列表的內容,但不能更改整數的內容。」不。他們所不同的是,1)涉及重新分配和2)就地變更(當然,這隻能在可變對象中才能實現)。你在下一段中說清楚了,但第一個可能有點誤導。 – glglgl

1

這不起作用:

class A: 
    self.foo = [] 

這引發了一個錯誤。

NameError: name 'self' is not defined 

因爲self並不是一個Python關鍵字,它只是共同轉讓給傳遞到類的方法,當類被稱爲類的實例變量名。

下面是一個例子:

class A(object): 
    def __init__(self): 
     self.foo = [] 

a, b = A(), A() 
a.foo.append(5) 
print(b.foo) 

然後返回:

[] 

當每一個被初始化,他們各自獲得自己的列表,它可以通過屬性foo訪問,並且當一個人修改,另一個是存儲在內存中不同位置的單獨列表,不受影響。

+1

亞倫,非常好的解釋!掙扎了一下,因爲我開始使用Python而不是C/C++做一些東西 – Konstantin

+1

@Konstantin感謝您的認可。 :)我正要離開布倫,但我想我有些東西可以補充他。 –

1

區別與可變性/不可變性,但執行什麼操作。

在示例1中,該類具有屬性foo。創建對象之後,您會爲對象提供另一個屬性foo,該屬性會遮蔽前一個。所以class屬性可以作爲一種「默認」或「後備」。

在示例2中,您有一個對象執行操作(當然,它只適用於可變對象)。因此,由A.foo引用的對象也可以通過a.foob.foo訪問,由於缺少具有相同名稱的實例屬性,所以添加了5

示例3不起作用,因爲在您使用它的地方不存在self

注意,例如1人以及與可變對象,如列表工作:

class A: 
    foo = [] 
a, b = A(), A() 
a.foo = [] 
a.foo.append(5) 
b.foo.append(10) 
print a.foo # [5] 
print b.foo # [10] 
print A.foo # [10] 

這裏a.foo得到一個新的空單。缺少實例屬性的b.foo繼續引用類屬性。所以我們有兩個相互獨立的空列表,就像我們在.append() ing時看到的那樣。

+0

有關示例1中的「fallback」屬性的好處,因此它的作用類似於類的靜態成員,同時也是實例的默認屬性。當你像'A.foo = 7'那樣將其改變爲類的靜態變量時,默認屬性會改變 – Konstantin