Zen of Python mówi, że powinien istnieć tylko jeden sposób robienia rzeczy - jednak często napotykam problem z podjęciem decyzji, kiedy użyć funkcji, a kiedy metody.
Weźmy trywialny przykład - obiekt ChessBoard. Powiedzmy, że potrzebujemy sposobu, aby uzyskać wszystkie legalne ruchy króla dostępne na planszy. Czy piszemy ChessBoard.get_king_moves () czy get_king_moves (chess_board)?
Oto kilka powiązanych pytań, na które spojrzałem:
- Dlaczego Python używa „magicznych metod”?
- Czy istnieje powód, dla którego łańcuchy Pythona nie mają metody określającej długość łańcucha?
Odpowiedzi, które otrzymałem, były w dużej mierze niejednoznaczne:
Dlaczego Python używa metod dla niektórych funkcji (np. List.index ()), a funkcje dla innych (np. Len (lista))?
Głównym powodem jest historia. Funkcje były używane dla tych operacji, które były ogólne dla grupy typów i które miały działać nawet dla obiektów, które w ogóle nie miały metod (np. Krotki). Wygodne jest również posiadanie funkcji, którą można łatwo zastosować do amorficznej kolekcji obiektów, gdy używasz funkcji funkcjonalnych Pythona (map (), apply () et al).
W rzeczywistości implementacja len (), max (), min () jako funkcji wbudowanej to w rzeczywistości mniej kodu niż implementacja ich jako metod dla każdego typu. Można spierać się o pojedyncze przypadki, ale jest to część Pythona i jest już za późno, aby wprowadzić tak fundamentalne zmiany. Funkcje muszą pozostać, aby uniknąć masowego uszkodzenia kodu.
Chociaż jest to interesujące, powyższe nie mówi zbyt wiele o tym, jaką strategię przyjąć.
Jest to jeden z powodów - w przypadku metod niestandardowych programiści mogliby wybrać inną nazwę metody, na przykład getLength (), length (), getlength () lub jakąkolwiek inną. Python wymusza ścisłe nazewnictwo, aby można było użyć wspólnej funkcji len ().
Nieco bardziej interesujące. Uważam, że funkcje są w pewnym sensie Pythonową wersją interfejsów.
Na koniec od samego Guido :
Rozmowa o zdolnościach / interfejsach sprawiła, że pomyślałem o niektórych z naszych „nieuczciwych” nazw metod specjalnych. W dokumencie Language Reference jest napisane: „Klasa może implementować pewne operacje, które są wywoływane za pomocą specjalnej składni (takie jak operacje arytmetyczne lub indeksowanie i wycinanie), definiując metody o specjalnych nazwach”. Ale są wszystkie te metody ze specjalnymi nazwami, takimi jak
__len__
lub,__unicode__
które wydają się być dostarczane z korzyścią dla funkcji wbudowanych, a nie dla obsługi składni. Przypuszczalnie w Pythonie interfejsu opartego na te metody zamieni się regularnie wymienionych metod działania w ABC, tak że__len__
stałby sięclass container: ... def len(self): raise NotImplemented
Chociaż myśląc o tym więcej, nie rozumiem, dlaczego wszystkie operacje składniowe nie wywoływałyby po prostu odpowiedniej metody o normalnej nazwie na określonym ABC. „
<
”, Na przykład, przypuszczalnie wywołać „object.lessthan
” (albo "comparable.lessthan
„). Więc kolejną korzyścią byłaby możliwość odzwyczajenia Pythona od tej dziwacznej nazwy, co wydaje mi się poprawą HCI .Hm. Nie jestem pewien, czy się zgadzam (wyobraź sobie :-).
Istnieją dwa fragmenty „uzasadnienia Pythona”, które chciałbym najpierw wyjaśnić.
Przede wszystkim wybrałem len (x) zamiast x.len () z powodów związanych z HCI (
def __len__()
pojawiło się znacznie później). W rzeczywistości istnieją dwa powiązane ze sobą powody, oba HCI:(a) W przypadku niektórych operacji notacja przedrostków po prostu czyta lepiej niż operacje na postfiksach - prefiksach (i wrostkach!) mają długą tradycję w matematyce, która lubi notacje, gdzie wizualizacje pomagają matematykowi w myśleniu o problemie. Porównaj łatwość, z jaką przepisujemy formułę
x*(a+b)
na,x*a + x*b
z niezdarnością robienia tego samego przy użyciu surowej notacji OO.(b) Kiedy czytam kod, który mówi,
len(x)
że wiem , że prosi o długość czegoś. To mówi mi o dwóch rzeczach: wynik jest liczbą całkowitą, a argumentem jest jakiś kontener. Wręcz przeciwnie, kiedy czytamx.len()
, muszę już wiedzieć, żex
jest to jakiś kontener implementujący interfejs lub dziedziczący po klasie, która ma standardlen()
. Świadek zamieszanie niekiedy mają kiedy klasa, która nie realizuje mapowanie maget()
lubkeys()
metody, lub coś, co nie jest plikiem mawrite()
metody.Mówiąc to samo w inny sposób, widzę „len” jako operację wbudowaną . Nie chciałbym tego stracić. Nie jestem pewien, czy miałeś to na myśli, czy nie, ale „def len (self):…” z pewnością brzmi tak, jakbyś chciał zdegradować to do zwykłej metody. Jestem zdecydowanie -1 w tej sprawie.
Drugie uzasadnienie Pythona, które obiecałem wyjaśnić, to powód, dla którego wybrałem specjalne metody wyszukiwania,
__special__
a nie tylkospecial
. Spodziewałem się wielu operacji, które klasy mogłyby chcieć przesłonić, niektóre standardowe (np.__add__
Lub__getitem__
), niektóre nie tak standardowe (np. Pikle__reduce__
przez długi czas nie były w ogóle obsługiwane w kodzie C). Nie chciałem, aby te specjalne operacje używały zwykłych nazw metod, ponieważ wcześniej istniejące klasy lub klasy napisane przez użytkowników bez encyklopedycznej pamięci dla wszystkich metod specjalnych byłyby podatne na przypadkowe zdefiniowanie operacji, których nie zamierzali zaimplementować , co może mieć katastrofalne skutki. Ivan Krstić wyjaśnił to bardziej zwięźle w swoim przesłaniu, które dotarło po tym, jak to wszystko napisałem.- --Guido van Rossum (strona domowa: http://www.python.org/~guido/ )
Rozumiem to, że w niektórych przypadkach notacja przedrostków ma po prostu więcej sensu (tj. Duck.quack ma więcej sensu niż szarlatan (Duck) z językowego punktu widzenia) i znowu funkcje pozwalają na „interfejsy”.
W takim przypadku przypuszczam, że zaimplementowałbym get_king_moves wyłącznie na podstawie pierwszego punktu Guido. Ale to wciąż pozostawia wiele otwartych pytań dotyczących, powiedzmy, implementacji klasy stosu i kolejki z podobnymi metodami push i pop - czy powinny to być funkcje czy metody? (tutaj zgadywałbym funkcje, bo naprawdę chcę zasygnalizować interfejs push-pop)
TLDR: Czy ktoś może wyjaśnić, jaka powinna być strategia podejmowania decyzji, kiedy używać funkcji, a kiedy metod?
X.frob
czyX.__frob__
wolnostojącefrob
.