2011-05-20 65 views
17

擴展父類的方法正確的方法,我經常做這樣的事情:什麼是現代的Python

class Person(object): 
    def greet(self): 
     print "Hello" 

class Waiter(Person): 
    def greet(self): 
     Person.greet(self) 
     print "Would you like fries with that?" 

線Person.greet(個體經營)看起來不正確。如果我改變了Waiter繼承的類別,我將不得不追蹤每一個這些類別並全部替換它們。

這是做現代Python的正確方法是什麼? 2.x和3.x,據我所知在這方面有變化3.

如果它關係任何我一般堅持單繼承,但如果額外的東西是必需的,以適應多重繼承正確,這將是很好的瞭解這一點。

回答

18

您使用super

返回一個代理對象,代表 方法調用的父母或兄弟姐妹 類類型的。這對 訪問在類中覆蓋 的繼承方法很有用。 的搜索順序與 getattr()的順序相同,只是跳過類型本身 。

換句話說,super的調用返回一個假對象,它將屬性查找委託給繼承鏈中上方的類。注意事項:

  • 這並不與老式類工作 - 如果你正在使用Python 2.x中,你需要確保你的層次結構中的頂級從object繼承。
  • 您需要將您自己的類和實例傳遞給Python 2.x中的super。這個要求在3.x中被放棄了。
  • 這將正確處理所有多繼承。 (當你有Python中的多重繼承樹,method resolution order生成和查詢辦理父類的順序。)

採取護理:有很多地方弄不清楚Python中多重繼承。您可能需要閱讀super() Considered Harmful。如果您確定要堅持單一繼承樹,並且您不打算更改所述樹中類的名稱,則可以像上面那樣對類名進行硬編碼,並且一切都可以正常工作。

+1

這是3.x版本[super()]的文檔鏈接(http://docs.python.org/py3k/library/functions.html#super)。 – martineau 2011-05-20 11:07:38

+0

只是爲了記錄,當在'super()'函數中引用'Waiter'時,不要使用類名,而要使用'self .__ class__'。這樣,您將不必重寫所有超級用戶,以免將來更改班級名稱。像這樣(在'Waiter.greet()')內:'super(self .__ class__,self).greet()'。 – 2017-07-13 21:18:09

+0

我會注意到一個方法的基本實現通常不會調用super。因此,在多重繼承中,重寫方法的基類在MRO中比任何其他重寫方法更重要。這是mixin在基地之前被列出的規則的根源。如果只有一個基類,並且可以通過繼承樹的每個級別上最右邊的父級來訪問,則可以保證這種情況得到平凡的保證。例如,如果你在覆蓋一個Django類之後,最右邊的父母應該總是導致Django類。 – 2017-07-17 14:45:12

8

不知道你是否正在尋找這個,但你可以通過這樣做來調用父母而不用提及它。

super(Waiter, self).greet() 

這將調用greet()功能Person

+1

爲了記錄,你也可以使用'super(self .__ class__,self) .greet()'。這樣,如果您更改服務員的姓名,則不必重寫您的超級人員。 – 2017-07-13 21:15:11

3

katrielalex的答案真的是你的問題的答案,但這不適合評論。

如果您打算到處使用super,並且您曾考慮多重繼承,請務必閱讀「super()Caused Harmful」鏈接。 super()是一個很棒的工具,但需要理解才能正確使用。根據我的經驗,對於那些看起來不太可能陷入複雜的鑽石繼承纏結中的簡單事情來說,直接調用超類並在更改基類的名稱時處理重命名實際上更簡單,也更簡單。

事實上,在Python2中,您必須包含當前類名,它通常比基類名更有可能改變。 (事實上​​,如果你做的是古怪的事情,有時候很難將引用傳遞給當前類;在定義方法的時候,類不會綁定到任何名稱,並且在這種情況下,當super調用被執行的類的原始名稱可能不會被綁定到類,例如當你使用類裝飾器時)