2016-07-13 33 views
0

如果我在Python中創建一個類,我給它一個類屬性(這是直接取自文檔,here),作爲從實例修改類屬性,根據類型不同的行爲?

class Dog: 

    tricks = []    

    def __init__(self, name): 
     self.name = name 

    def add_trick(self, trick): 
     self.tricks.append(trick) 

我看到的是,作爲文檔建議,在做的時候

d1 = Dog('d1') 
d2 = Dog('d2') 

d1.add_trick('trick') 
print d2.tricks 

我得到的伎倆被添加到D2,以及:

['trick'] 

這是因爲tricks是一個類屬性,而不是一個插件tance屬性在所有實例中共享(如果這不是正統的話,請糾正我)。

現在,假設我這樣做,而不是

class Dog: 

    a = 1 

    def __init__(self, name): 
     self.name = name 

    def improve_a(self): 
     self.a += 1 

,我跑

d1 = Dog('d1') 
d2 = Dog('d2') 

d1.improve_a() 
print d1.a, d2.a 

這分別給了我2和1,即二審計數並沒有改變。爲什麼這是行爲差異?

+0

'self.a + = 1'確實執行爲'self.a = self.a + 1' ... – deceze

+3

因爲在一種情況下,您正在變更所有實例共享的可變類屬性(http:///stackoverflow.com/q/1680528/3001761),另一方面,你使用不可變的實例屬性來映射不可變的類屬性。他們的情況並非如此。 *「取決於類型的不同行爲」*正是您應該期望的,否則OOP中的事情確實很無聊! – jonrsharpe

回答

2

int類沒有定義+ =運算符(__iadd__方法)。這是沒有道理的,因爲它是不可改變的。

這就是爲什麼+=默認爲+,然後=reference

self.a += 1成爲self.a = self.a + 1

現在你第一次調用improve_a會發生以下情況:

  1. 讀取的類屬性a,並把它在棧上
  2. 添加1到該項目在棧上
  3. 創建新實例屬性a併爲其分配棧

這意味着class屬性的值根本改變,添加新的實例屬性。

對於同一對象上的每個後續調用improve,實例屬性都會增加,因爲屬性查找是在實例dict上開始的,並且只在該屬性不存在的情況下才會轉到類dict。

如果你做同樣的與重載__iadd__方法可以得到不同的行爲可變類:

class HasList: 
    some_list = [] 

    def add_something(self, value): 
     some_list += [value] 

fst = HasList() 
sec = HasList() 

fst.add_something(1) 
fst.add_something(2) 
sec.add_something(3) 

print(HasList.some_list, fst.some_list, sec.some_list) 

你會看到,所有實例和類本身仍持有相同的列表。打印每次顯示相同的列表[1, 2, 3]。您還可以檢查所有三個列表是否相同:fst.some_list is sec.some_list and fst.some_list is HasList.some_list # -> True

這是因爲list.__iadd__只是調用list.extend並在方法內返回自己(至少如果它是用python寫的)。

相關問題