Bawiłem się ostatnio Pythonem i jedną rzeczą, która wydaje mi się nieco dziwna, jest szerokie użycie `` magicznych metod '', np. Aby udostępnić swoją długość, obiekt implementuje metodę def __len__(self)
, a następnie jest wywoływany, gdy piszesz len(obj)
.
Zastanawiałem się tylko, dlaczego obiekty po prostu nie definiują len(self)
metody i nie są wywoływane bezpośrednio jako element członkowski obiektu, np. obj.len()
? Jestem pewien, że muszą istnieć dobre powody, aby Python robił to w taki sposób, ale jako początkujący jeszcze nie odkryłem, czym one są.
python
magic-methods
Greg Beech
źródło
źródło
len()
lubreversed()
stosuje się do wielu typów obiektów, ale metoda takaappend()
ma zastosowanie tylko do sekwencji itp.Odpowiedzi:
AFAIK
len
jest pod tym względem wyjątkowy i ma historyczne korzenie.Oto cytat z FAQ :
Inne „magiczne metody” ( w folklorze Pythona właściwie nazywane metodami specjalnymi ) mają wiele sensu, a podobne funkcje istnieją w innych językach. Są one głównie używane w przypadku kodu, który jest wywoływany niejawnie, gdy używana jest specjalna składnia.
Na przykład:
i tak dalej...
źródło
Z Zen Pythona:
Jest to jeden z powodów - z metod niestandardowych, programiści będą wolne, aby wybrać inną nazwę metody, jak
getLength()
,length()
,getlength()
albo w ogóle. Python wymusza ścisłe nazewnictwo, abylen()
można było używać wspólnej funkcji .Wszystkie operacje, które są wspólne dla wielu typów obiektów, są umieszczane w metodach magicznych, takich jak
__nonzero__
,__len__
lub__repr__
. Są one jednak w większości opcjonalne.Przeciążanie operatorów również odbywa się za pomocą metod magicznych (np.
__le__
), Więc sensowne jest używanie ich również do innych typowych operacji.źródło
Python używa słowa „magiczne metody” , ponieważ te metody naprawdę wykonują magię za program. Jedną z największych zalet korzystania z magicznych metod Pythona jest to, że zapewniają one prosty sposób na sprawienie, by obiekty zachowywały się jak typy wbudowane. Oznacza to, że możesz uniknąć brzydkich, sprzecznych z intuicją i niestandardowych sposobów wykonywania podstawowych operatorów.
Rozważmy następujący przykład:
Daje to błąd, ponieważ typ słownika nie obsługuje dodawania. Teraz rozszerzmy klasę słownika i dodajmy magiczną metodę „__add__” :
Teraz daje następujący wynik.
Tak więc, dodając tę metodę, nagle pojawiła się magia i błąd, który otrzymywałeś wcześniej, zniknął.
Mam nadzieję, że to wszystko wyjaśnia. Więcej informacji można znaleźć w:
Przewodnik po magicznych metodach Pythona (Rafe Kettler, 2012)
źródło
Niektóre z tych funkcji robią więcej niż jedna metoda byłaby w stanie zaimplementować (bez metod abstrakcyjnych w nadklasie). Na przykład
bool()
działa mniej więcej tak:Możesz także mieć 100% pewności, że
bool()
zawsze zwróci True lub False; jeśli polegałeś na metodzie, nie możesz być całkowicie pewien, co otrzymasz.Niektóre inne funkcje, które stosunkowo skomplikowane wdrożenia (bardziej skomplikowane niż podstawowych metod magicznych są prawdopodobnie) są
iter()
icmp()
, a wszystkie metody (atrybutgetattr
,setattr
adelattr
). Rzeczy takie jakint
również dostęp do magicznych metod podczas stosowania przymusu (możesz zaimplementować__int__
), ale wykonuj podwójne obowiązki jako typy.len(obj)
jest właściwie jedynym przypadkiem, w którym nie wierzę, że kiedykolwiek się różniobj.__len__()
.źródło
hasattr()
użyłbymtry:
/except AttributeError:
a zamiast tegoif obj.__len__(): return True else: return False
powiedziałbym tylko, żereturn obj.__len__() > 0
to tylko stylistyczne rzeczy.bool(x)
przywołanox.__nonzero__()
) Twoja metoda nie zadziała. instancje bool mają metodę__nonzero__()
, a twój kod będzie się wywoływał, gdy obj był bool. Możebool(obj.__bool__())
powinieneś być traktowany tak samo jak ty__len__
? (A może ten kod faktycznie działa dla Pythona 3?)len(x)
ix.__len__()
jest to, że pierwszy z nich zgłosi OverflowError dla długości, które przekraczająsys.maxsize
, podczas gdy drugi generalnie nie dla typów zaimplementowanych w Pythonie. To raczej błąd niż funkcja (np. Obiekt range w Pythonie 3.2 może w większości obsłużyć dowolnie duże zakresy, ale używanielen
z nimi może się nie powieść. Ich również__len__
zawodzi, ponieważ są zaimplementowane w C, a nie w Pythonie)Nie są to tak naprawdę „magiczne imiona”. To tylko interfejs, który obiekt musi zaimplementować, aby świadczyć daną usługę. W tym sensie nie są one bardziej magiczne niż jakakolwiek predefiniowana definicja interfejsu, którą musisz ponownie zaimplementować.
źródło
Chociaż przyczyna jest głównie historyczna, w Pythonie istnieją pewne osobliwości,
len
które sprawiają, że użycie funkcji zamiast metody jest właściwe.Niektóre operacje w Pythonie są zaimplementowane na przykład jako metody,
list.index
adict.append
podczas gdy inne są zaimplementowane jako wywołania i metody magiczne, na przykładstr
iiter
ireversed
. Te dwie grupy różnią się wystarczająco, więc inne podejście jest uzasadnione:str
,int
I przyjaciele są typy. Bardziej sensowne jest wywołanie konstruktora.iter
może wywołać,__getitem__
jeśli__iter__
nie jest dostępny, i obsługuje dodatkowe argumenty, które nie pasują do wywołania metody. Z tego samego powoduit.next()
został zmienionynext(it)
w ostatnich wersjach Pythona - ma to większy sens.__iter__
i__next__
- to się nazywafor
pętla. Dla spójności funkcja jest lepsza. I to sprawia, że jest to lepsze dla niektórych optymalizacji.repr
działa jakstr
działa. Mającstr(x)
kontrax.repr()
byłoby mylące.isinstance
.getattr(x, 'a')
to inny sposób działaniax.a
igetattr
mają wiele z wyżej wymienionych cech.Osobiście nazywam pierwszą grupę metodą metodyczną, a drugą grupę operacyjną. To niezbyt dobre rozróżnienie, ale mam nadzieję, że jakoś pomoże.
Powiedziawszy to,
len
nie pasuje dokładnie do drugiej grupy. Jest bardziej zbliżony do operacji z pierwszej, z tą różnicą, że jest o wiele bardziej powszechny niż prawie każda z nich. Ale jedyne, co robi, to dzwonienie__len__
i jest bardzo bliskoL.index
. Istnieją jednak pewne różnice. Na przykład__len__
może zostać wywołany w celu implementacji innych funkcji, na przykładbool
, jeśli metoda została wywołana,len
możesz zerwaćbool(x)
zlen
metodą niestandardową , która robi zupełnie inne rzeczy.Krótko mówiąc, masz zestaw bardzo typowych funkcji, które mogą implementować klasy, a do których można uzyskać dostęp za pośrednictwem operatora, poprzez specjalną funkcję (która zwykle robi więcej niż implementacja, jak zrobiłby to operator), podczas konstruowania obiektu i wszystkie z nich mają wspólne cechy. Cała reszta to metoda. I
len
jest pewnym wyjątkiem od tej reguły.źródło
Nie ma wiele do dodania do powyższych dwóch postów, ale wszystkie „magiczne” funkcje wcale nie są magiczne. Są częścią modułu __ builtins__, który jest niejawnie / automatycznie importowany podczas uruchamiania interpretera. To znaczy:
dzieje się za każdym razem przed rozpoczęciem programu.
Zawsze uważałem, że byłoby bardziej poprawne, gdyby Python robił to tylko dla powłoki interaktywnej i wymagał skryptów do importowania różnych części z wbudowanych, których potrzebowali. Prawdopodobnie inna obsługa __ main__ byłaby fajna w powłokach niż interaktywna. W każdym razie sprawdź wszystkie funkcje i zobacz, jak to jest bez nich:
źródło