2010-09-26 50 views
1

Level:初學者面向對象編程基礎:繼承與投影(Python)

我正在做面向對象編程的第一步。代碼旨在展示如何通過鏈條傳遞方法。因此,當我撥打UG.say(person, 'but i like')時,方法say被指示撥打MITPerson。鑑於MITPerson不包含say方法,它會將其傳遞給Person。我認爲代碼沒有問題,因爲它是講座的一部分(參見下面的源代碼)。我認爲是我在運行代碼時省略了定義某些內容。不知道是什麼。我認爲UG instance錯誤信息正在尋找作爲第一個參數是指self,但原則上,不需要提供,是否正確?任何提示?

class Person(object): 
    def __init__(self, family_name, first_name): 
     self.family_name = family_name 
     self.first_name = first_name 
    def familyName(self): 
     return self.family_name 
    def firstName(self): 
     return self.first_name 
    def say(self,toWhom,something): 
     return self.first_name + ' ' + self.family_name + ' says to ' + toWhom.firstName() + ' ' + toWhom.familyName() + ': ' + something 


class MITPerson(Person): 
    def __init__(self, familyName, firstName): 
     Person.__init__(self, familyName, firstName) 


class UG(MITPerson): 
    def __init__(self, familyName, firstName): 
     MITPerson.__init__(self, familyName, firstName) 
     self.year = None 
    def say(self,toWhom,something): 
     return MITPerson.say(self,toWhom,'Excuse me, but ' + something) 



>>> person = Person('Jon', 'Doe') 
>>> person_mit = MITPerson('Quin', 'Eil') 
>>> ug = UG('Dylan', 'Bob') 
>>> UG.say(person, 'but i like') 


    UG.say(person, 'bla') 
**EDIT (for completeness)**: it should say UG.say(person, 'but i like') #the 'bla' creeped in from a previous test 
TypeError: unbound method say() must be called with UG instance as first argument (got Person instance instead) 

來源:麻省理工學院開放式http://ocw.mit.edu計算機科學導論和程序2008年秋季

回答

5

您正在調用類而不是實例。

>>> ug = UG('Dylan', 'Bob') 
>>> UG.say(person, 'but i like') 


UG.say(person, 'bla') 

呼叫實例,而不是

>>> ug = UG('Dylan', 'Bob') 
>>> ug.say(person, 'but i like') 
3

變化

UG.say(person, 'but i like') 

ug.say(person, 'but i like') 

UG.say返回不受約束的方法say。 「不受限制」意味着say的第一個參數不會自動填充。未綁定的方法say需要3個參數,第一個必須是UG的實例。相反, UG.say(person, 'but i like')作爲第一個參數發送Person的實例。這解釋了Python給你的錯誤信息。

相反,ug.say返回綁定方法say。 「綁定」意味着要說的第一個參數將是ug。綁定方法需要2個參數,toWhomsomething。因此,ug.say(person, 'but i like')按預期工作。

unbound method has been removed from Python3的概念。相反,UG.say只是返回一個函數(仍然)需要3個參數。唯一的區別是沒有更多的類型檢查第一個參數。你仍然會得到一個錯誤,但是,只是一個不同的:

TypeError: say() takes exactly 3 positional arguments (2 given) 

PS。當開始學習Python時,我認爲我只是試圖接受UG.say返回一個未綁定的方法(期待3個參數),而ug.say是調用方法(期待2個參數)的正常方法。後來,要真正瞭解Python如何實現這種行爲差異(同時保持相同的限定名稱語法),您需要研究descriptorsattribute lookup的規則。

+0

@all:非常感謝你的解釋!這也是一個很好的例子,顯示了名稱(變量,類等)的混淆是多麼的容易......特別是如果基礎仍然有點不穩定......我會盡量小心/注意命名約定。 – raoulbia 2010-09-26 15:46:24

+0

不客氣,巴巴。祝你學習好運。 – unutbu 2010-09-26 16:44:08

3

OK,時間上Python方法一個簡短的教程。

當你定義一個類中的功能:

>>> class MITPerson: 
...  def say(self): 
...    print ("I am an MIT person.") 
... 
>>> class UG(MITPerson): 
...  def say(self): 
...    print ("I am an MIT undergrad.") 
... 

,然後檢索使用點查找該功能,你會得到一個特殊的對象返回稱爲「綁定方法」,其中第一個參數是自動作爲被調用的實例傳遞給函數。請參閱:

>>> ug = UG() 
>>> ug.say 
<bound method UG.say of <__main__.UG object at 0x022359D0>> 

但是,因爲該功能還對類中定義的,你可以看看它通過類而不是通過一個具體的實例。如果你這樣做,但是,你不會得到一個綁定的方法(顯然 - 沒有什麼可綁定的!)。你會得到原來的功能,你需要傳遞要調用它的實例:

>>> UG.say() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: unbound method say() must be called with UG instance as first argument (got nothing instead) 
>>> ug.say() 
I am an MIT undergrad. 
>>> UG.say(ug) 
I am an MIT undergrad. 

第一次調用失敗,因爲該函數say預計UG實例作爲其第一個參數,一無所獲。第二次調用自動綁定第一個參數,所以它的工作原理;第三個手動傳遞您想要使用的實例。第二和第三是等同的。


有一件事更何況,這是它似乎並不像say功能實際上需要的UG這是做俗話說的實例。如果沒有,你可以作爲一個「靜態方法」,它告訴Python的不是第一屬性綁定對其進行註冊:

>>> class UG: 
...  @staticmethod 
...  def say(): 
...    print("foo") 
... 
>>> ug = UG() 
>>> UG.say() 
foo 
>>> ug.say() 
foo 
4

的答案是相當不錯,但有一個方面說明,我認爲是作出重要。摘錄片段(MITPerson):

def __init__(self, familyName, firstName): 
    Person.__init__(self, familyName, firstName) 

此代碼完全無用且冗餘。當子類不需要在其超類的方法實現中覆蓋任何東西時,子類看起來像是「覆蓋」該方法是完全沒有用的,然後只是將所有工作委託給沒有任何更改的無論如何,超類。

代碼完全沒有目的,永遠不會有任何區別(除了邊緣放緩整個系統),因此可以刪除沒有任何傷害,應該被刪除:爲什麼有它呢?!任何代碼都存在於你的程序中,但沒有任何用處,這不可避免地會損害程序的質量:這種無用的「鎮流器」會稀釋有用的工作代碼,使得你的程序難以閱讀,維護和調試等等。大多數人似乎在大多數情況下直觀地理解了這一點(所以你沒有看到大量的代碼,「假 - 覆蓋」大多數方法,但在方法體內只是向超類實現),除了__init__ - 出於某種原因,許多人似乎有一個精神盲點,只是看不出與其他方法完全相同的規則。這個盲區可能來自於熟悉其他完全不同的語言,其中規則不適用於類的構造函數,加上一個誤解,認爲__init__作爲一個構造函數,實際上它不是(它是一個初始值設定項) 。

因此,總結一下:子類應該定義__init__當且僅當它需要在超類自己的初始化程序之前,之後或之前和之後做其他事情時(很少它可能想要做某事而不是,即而不是委託給超類初始化器,但這幾乎沒有好的做法)。如果子類的__init__正文具有只是調用超類的__init__(具有完全相同順序的參數),則從您的代碼中刪除子類的__init__(就像您對任何其他類似冗餘的方法所做的那樣)。