Dużą zaletą jest to, że wbudowane funkcje (i operatory) mogą w razie potrzeby zastosować dodatkową logikę, poza zwykłym wywołaniem specjalnych metod. Na przykład min
może przeglądać kilka argumentów i stosować odpowiednie sprawdzanie nierówności lub może akceptować pojedynczy argument iteracyjny i postępować podobnie; abs
po wywołaniu obiektu bez specjalnej metody może próbować porównać wymieniony obiekt z 0 i, w razie potrzeby, użyć metody zmiany znaku obiektu (mimo że obecnie nie działa); i tak dalej.
Dla zachowania spójności wszystkie operacje o szerokim zastosowaniu muszą zawsze być realizowane przez wbudowane i/lub operatory, a to one są odpowiedzialne za wyszukiwanie i stosowanie odpowiednich specjalnych metod (na jednym lub kilku argumentach).), stosuj logikę alternatywną, jeśli ma to zastosowanie, i tak dalej.
Przykład, w którym ta zasada nie została poprawnie zastosowana (ale niespójność została rozwiązana w języku Python 3), to "wykonaj krok iteratora do przodu": w wersji 2.5 i wcześniejszej konieczne było zdefiniowanie i wywołanie niezwiązanego specjalnie z nazwą next
metoda na iteratorze. W wersji 2.6 i późniejszych możesz zrobić to we właściwy sposób: obiekt iteratora definiuje __next__
, nowy next
może nazwać go i zastosować dodatkową logikę, na przykład do podania wartości domyślnej (w 2.6 nadal możesz to zrobić w zły, stary sposób, ze względu na kompatybilność wsteczną, jednak w 3.*
nie możesz już więcej.
Inny przykład: rozważ wyrażenie: x + y
. W tradycyjnym języku zorientowanym obiektowo (zdolnym do wysyłania tylko na typ skrajnego lewego argumentu - jak Python, Ruby, Java, C++, C#, & c) jeśli x
jest jakiegoś wbudowanego typu, a y
jest Twoim własnym fantazyjny nowy typ, niestety nie masz szczęścia, jeśli język nalega na przekazanie całej logiki do metody type(x)
, która implementuje dodatek (zakładając, że język pozwala operatorowi przeładować ;-).
W Pythonie, operator +
(i podobnie oczywiście wbudowane operator.add
, jeśli to, co wolisz) próbuje x typ za __add__
, a jeśli to się nie wie, co zrobić z y
, a następnie próbuje Y typ na __radd__
. Możesz więc zdefiniować typy, które potrafią dodawać się do liczb całkowitych, zmiennoprzecinkowych, złożonych itp., A także takie, które wiedzą, jak dodać takie wbudowane typy liczbowe (tzn. Możesz je kodować tak, aby x + y
i y + x
oba działają poprawnie, gdy y
jest instancją Twojego nowego, nowego typu, a x
jest instancją jakiegoś wbudowanego typu numerycznego).
„Ogólne funkcje” (jak w peak) są bardziej eleganckie podejście (umożliwiając dowolną nadpisywanie oparty na kombinacji typów nigdy z szalonym naciskiem monomania na skrajnej lewej argumentów OOP zachęca -!), Ale (a) nie zostały niestety zaakceptowane w Pythonie 3, i (b) oczywiście wymagają, aby funkcja generyczna była wyrażana jako wolnostojąca (byłoby całkowicie szalone, aby uważać funkcję za "przynależną" do dowolnego typu) , gdzie cały POINT może być inaczej przesłonięty/przeciążony w oparciu o dowolną kombinację jego typów argumentów! -). Każdy, kto kiedykolwiek był zaprogramowany w Common Lisp, Dylan lub PEAK, wie o czym mówię ;-).
Tak więc, niezależne funkcje i operatory są po prostu właściwą, spójną drogą do przejścia (nawet jeśli brak ogólnych funkcji, w gołych kościach Python, usuwa pewną część nieodłącznej elegancji, to wciąż jest rozsądna mieszanka elegancji i praktyczności! -).
Osobiście uważam, że 'min (1,2)' i 'min ([1,2])' są wyjątkowo spójne. Nie wszystko powinno być napisane w Javie: –
@TomLeys: W jaki sposób 'len (x)' jest spójne z wywołaniami metod, takimi jak 'x.foo()'? Obie są operacjami na obiekcie x, prawda? Nie chodzi o to, że jest jak Java. Chodzi o zgodność z [Principle of least Least Surprise] (http://en.wikipedia.org/wiki/Principle_of_least_surprise). Ruby przestrzega tej zasady. –