2009-10-27 76 views
9

我是一個python新手,我不確定爲什麼python實現了len(obj),max(obj),和min(obj)作爲一個靜態函數(我是從java語言中)obj.len(),obj.max()和obj.min()之外還有什麼優點和缺點(而不是明顯不一致)有len()...在方法調用?靜態函數len(),max()和min()優於繼承方法調用

爲什麼guido選擇了這個方法調用? (這可能已經在python3中解決,如果需要的話,但它並沒有改變python3,所以有充足的理由...我希望)

謝謝!!

+2

我個人發現'min(1,2)'和'min([1,2])'是非常一致的。不是所有的東西都應該是Java,像 – 2009-10-27 01:06:16

+0

@TomLeys:'len(x)'如何與像'x.foo()'這樣的方法調用保持一致?他們都是對象x的操作,不是嗎?這不是關於像Java一樣。這符合[最低驚奇原則](http://en.wikipedia.org/wiki/Principle_of_least_surprise)。 Ruby遵循這個原則。 – 2011-10-19 23:18:08

回答

19

最大的好處是內置函數(和運算符)可以在適當的時候應用額外的邏輯,而不僅僅是調用特殊的方法。例如,min可以查看幾個參數並應用適當的不等式檢查,或者它可以接受一個可迭代的參數並進行類似的操作; abs當在沒有特殊方法的對象上調用__abs__時,可以嘗試將所述對象與0進行比較,並根據需要使用對象更改符號方法(雖然目前沒有)。等等。因此,爲了一致性,所有具有廣泛適用性的操作必須始終通過內置插件和/或操作符,而內建的責任是查找並應用適當的特殊方法(對一個或多個參數),適用時使用替代邏輯,等等。

這個原理沒有被正確應用的一個例子(但在Python 3中修正了不一致)是「迭代器前進」:在2.5及更早版本中,您需要定義並調用非特定名稱next方法在迭代器上。在2.6及更高版本中,您可以按照正確的方式進行操作:迭代器對象定義爲__next__,新的next內置可以調用它應用額外的邏輯,例如提供一個默認值(以2爲單位)。6你仍然可以用舊的方式做到這一點,爲了向後兼容,儘管在3.*中你不能再使用它)。

另一個例子:考慮表達式x + y。在傳統的面向對象的語言中(如果x是一些內置類型,並且y是您自己的),則只能分派最左邊參數的類型 - 如Python,Ruby,Java,C++,C#,& c)花式新型,如果語言堅持將所有邏輯委託給實現加法的type(x)的方法(假設該語言允許操作符重載;-)),那麼您很遺憾不幸運。

在Python中,+運營商(以及類似的過程中內置operator.add,如果這就是你喜歡什麼)嘗試X的類型的__add__,如果一個不知道做什麼用y做,然後嘗試ÿ的類型的__radd__。因此,您可以定義知道如何將自己添加到整數,浮點數,複數等等的類型,以及知道如何將自己的內置數值類型添加到自己的類型的類型(即,您可以編寫它以便x + yy + x都可以正常工作,當y是您喜歡的新類型的實例,x是某些內置數值類型的實例)。

「通用功能」(如PEAK)是一個更優雅的方式(允許從未基於類型組合的任何壓倒一切,與瘋狂的偏執狂重點對OOP鼓勵最左邊的參數 - !),但( a)他們不幸被Python 3接受,並且(b)他們當然要求泛型函數被表達爲獨立的(如果將函數視爲「屬於」任何單一類型,其中整個POINT可以根據其多個參數的類型的任意組合而被不同地重載/重載! - )。任何在Common Lisp,Dylan或PEAK中編程的人都知道我在說什麼;-)。因此,獨立函數和運算符只是一個正確的,一致的方式(即使缺少Python中的通用函數,也會將一些部分內在優雅刪除,但仍然是優雅和實用性的合理組合! - )。

+0

+1對內置插件的好處有很好的解釋。 – whaley 2009-10-27 01:40:22

+1

你說「只能派發最左邊參數的類型 - 比如Python,C++ ......」,然後顯示python也可以用'__radd__'在右邊參數上派發。另外,如果將'operator +'聲明爲一個自由函數,則C++可以在兩個參數上分派。這似乎有點不一致...... – sth 2009-10-27 02:13:36

+2

@sth,Python不能在RHS參數上使用_dispatch_ - 它可以使用內建函數或operator_來完成繁重的工作。在C++中,如果'operator +'是一個獨立函數,它可以基於操作數的_statically visible_(又名編譯時可見)類型來重載,它只是**不能** ** DISPATCH **,嚴格使用** runtime **類型的操作數來選擇要執行的代碼(作爲成員函數可以,但僅基於** LEFT HAND SIDE **類型)。沒有什麼不一致的,對於那些知道調度意味着什麼的讀者,以及編譯時和運行時類型之間的區別! - ) – 2009-10-27 03:51:55

3

它強調對象的功能,而不是它的方法或類型。 Capabilites由「幫助」功能聲明,如__iter____len__,但它們不構成接口。該接口在內置函數中,除此之外,還可以在內置運算符(如+和[])中進行索引和切片。

有時,它不是一對一的對應關係:例如,iter(obj)會返回一個對象的迭代器,即使沒有定義__iter__也會工作。如果沒有定義,它會繼續查看對象是否定義了__getitem__並且將返回一個訪問對象索引的迭代器(如數組)。

這與Python的鴨子打字一起,我們只關心我們可以用一個對象做什麼,而不是它是一種特定的類型。

3

實際上,這些不是你想象中的「靜態」方法。它們是built-in functions,它們實際上只是實現它們的python對象上的某些方法的別名。

>>> class Foo(object): 
...  def __len__(self): 
...    return 42 
... 
>>> f = Foo() 
>>> len(f) 
42 

無論對象是否實現它們,總是可以調用它們。關鍵是要有一定的一致性。代替的具有稱爲長度()方法和另一個稱爲尺寸()的一些類,慣例是實施len個並讓呼叫者總是由多個可讀LEN(OBJ)訪問它代替obj.methodThatDoesSomethingCommon

+0

不,這只是len。沒有'__min__'或'__max__'特殊方法。 – 2009-10-27 01:55:03

+1

問題是,這個問題對待他們都是一樣的,這是不正確的。這個答案正確地說 - 這不是一般的模式,但是有一個共同符號的混合物。 – 2009-10-27 02:19:06

+0

即使'f .__ len __()'也不是'len(f)'的別名。如果你的'__len__'特殊方法返回任何非整數(或者甚至是一個大整數),那麼'len'函數將失敗,並帶有'TypeError'。 – 2009-10-27 07:55:06

1

我認爲原因是這樣的基本操作可以在與容器相同的接口的迭代器上完成。但是,它實際上不適用於len:

def foo(): 
    for i in range(10): 
     yield i 
print len(foo()) 

...因TypeError失敗。 len()不會消耗和計算迭代器;它只適用於調用__len__的對象。

所以,就我而言,len()不應該存在。說obj.len比len(obj)更自然,並且與其他語言和標準庫更加一致。我們不會說append(lst,1);我們說lst.append(1)。有一個單獨的全局長度方法是一個奇怪的,不一致的特例,並且在全局命名空間中吃了一個非常明顯的名字,這是Python的一個非常不好的習慣。

這與鴨打字無關;你可以說getattr(obj, "len")決定你是否可以像使用getattr(obj, "__len__")一樣容易 - 更加一致 - 地使用len。

所有這些,像語言疣一樣 - 對於那些認爲這是一個疣的人 - 這是一個容易與之共處的人。

另一方面,最小和最大工作迭代器,除了任何特定的對象,它們的使用。這是簡單的,所以我就舉一個例子:

import random 
def foo(): 
    for i in range(10): 
     yield random.randint(0, 100) 
print max(foo()) 

然而,沒有__min____max__方法來覆蓋這些行爲,因此沒有爲有序容器提供高效的檢索中沒有一致的方法。如果一個容器按照您要搜索的同一個鍵進行排序,則最小/最大值是O(1)操作而不是O(n),並且唯一的方法是通過不同的不一致的方法公開該操作。 (當然,這可以用相對簡單的語言修正)。

爲了跟進另一個問題:它阻止使用Python的方法綁定。舉一個簡單的,人爲的例子,你可以這樣做是爲了提供一個功能值添加到列表:

def add(f): 
    f(1) 
    f(2) 
    f(3) 
lst = [] 
add(lst.append) 
print lst 

,這適用於所有的成員函數。儘管如此,你不能用min,max或len來實現,因爲它們不是它們操作對象的方法。相反,你必須求助於functools.partial,這是一種在其他語言中很常見的笨拙的第二類解決方法。

當然,這是一個不常見的情況;但是不常見的情況告訴我們一種語言的一致性。