lento funkcja pozwalająca uzyskać długość zbioru. Działa poprzez wywołanie __len__metody obiektu . __something__atrybuty są szczególne i zwykle bardziej niż na pierwszy rzut oka i generalnie nie powinny być wywoływane bezpośrednio.
Dawno temu zdecydowano, że uzyskanie długości czegoś powinno być funkcją, a nie kodem metody, rozumując, że len(a)to znaczenie będzie jasne dla początkujących, ale a.len()nie będzie tak jasne. Kiedy zaczął Python, __len__nawet nie istniał i lenbył specjalną rzeczą, która działała z kilkoma typami obiektów. Niezależnie od tego, czy sytuacja, którą nas to pozostawia, ma sens, jest tutaj na stałe.
Często jest tak, że „typowym” zachowaniem elementu wbudowanego lub operatora jest wywołanie (o innej i ładniejszej składni) odpowiednich metod magicznych (o takich nazwach __whatever__) na obiektach, których dotyczy. Często element wbudowany lub operator ma „wartość dodaną” (jest w stanie obrać różne ścieżki w zależności od zaangażowanych obiektów) - w przypadku lenvs __len__jest to tylko odrobina sprawdzenia poprawności wbudowanego elementu wbudowanego, którego brakuje w magiczna metoda:
>>> classbah(object):... def__len__(self):return"an inch"... >>> bah().__len__()
'an inch'>>> len(bah())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object cannot be interpreted as an integer
Kiedy widzisz wywołanie funkcji lenwbudowanej, jesteś pewien, że jeśli program kontynuuje działanie po tym, zamiast zgłaszać wyjątek, wywołanie zwróciło liczbę całkowitą, nieujemną i mniejszą niż 2 ** 31 - kiedy widzisz wezwanie xxx.__len__(), nie masz pewności (poza tym, że autor kodu albo nie zna Pythona, albo nie ma nic dobrego ;-).
Inne wbudowane funkcje zapewniają jeszcze większą wartość dodaną, wykraczającą poza zwykłe sprawdzenie poprawności i czytelności. Jednolite zaprojektowanie całego Pythona do pracy poprzez wywołania wbudowanych i użycie operatorów, nigdy przez wywołania magicznych metod, oszczędza programistom ciężaru zapamiętywania, który przypadek jest który. (Czasami pojawia się błąd: do 2.5 trzeba było wywołać foo.next()- w 2.6, chociaż nadal działa to dla wstecznej kompatybilności, powinieneś wywołać next(foo), a 3.*magiczna metoda jest poprawnie nazwana __next__zamiast "oops-ey" next! - ).
Zatem ogólną zasadą powinno być, aby nigdy nie wywoływać metody magicznej bezpośrednio (ale zawsze pośrednio przez wbudowaną), chyba że dokładnie wiesz, dlaczego musisz to zrobić (np. Kiedy nadpisujesz taką metodę w podklasie, jeśli podklasa musi odroczyć do nadklasy, która musi zostać wykonana przez jawne wywołanie metody magic).
Jestem początkującym użytkownikiem Pythona (nie myślą początkującym programistą) i nie jestem pewien, czy „Kiedy widzisz wywołanie wbudowanego len, jesteś pewien, że jeśli program będzie kontynuował działanie, zamiast zgłaszać wyjątek”. Spróbowałem tego: def len(x): return "I am a string." print(len(42)) print(len([1,2,3]))i wydrukowano I am stringdwukrotnie. Czy możesz to bardziej wyjaśnić?
Darek Nędza
4
@ DarekNędza To nie ma nic wspólnego z powyższym, czyli o wbudowanym len. Właśnie zdefiniowałeś swoją funkcję len, która oczywiście może zwrócić cokolwiek chcesz. OP mówił o wbudowanym len, który wywołuje __len__specjalną metodę (nie funkcję) na rozpatrywanym obiekcie.
Veky
@ Veky Jak mogę się upewnić, że wywołuję funkcję wbudowaną, a lennie inną funkcję (jak w moim przykładzie), która miała taką samą nazwę - len. Nie ma ostrzeżenia typu „Przedefiniowujesz wbudowaną funkcję len” lub coś w tym rodzaju. Moim zdaniem nie jestem pewien, co powiedział Alex w swojej odpowiedzi.
Darek Nędza
3
Alex wyraźnie powiedział , że jeśli dzwonisz do wbudowanego, to na pewno ..._. Nie powiedział nic o upewnieniu się, że dzwonisz do wbudowanego. Ale jeśli chcesz wiedzieć, że można: len in vars(__builtins__).values().
Veky
1
Niestety, to kolejny przykład braku wspólnej klasy bazowej dla obiektów w Pythonie. Przełączanie kontekstów syntaktycznych zawsze było szalone. W niektórych przypadkach powszechnym idiomem jest użycie metody podkreślenia, w innych należy użyć czegoś w rodzaju funkcji, aby zrobić coś wspólnego dla wielu obiektów. Jest to również dziwne, ponieważ wiele obiektów nie ma znaczenia semantycznego dla len. Czasami model obiektowy jest bardziej podobny do C ++, zlewozmywak kuchenny ...
uchuugaka
28
Możesz myśleć o len () jako o mniej więcej odpowiedniku
Jest jednak nieco inne zachowanie. Na przykład w przypadku ints
>>> (1).__len__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__len__'>>> len(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
Odpowiedzi:
len
to funkcja pozwalająca uzyskać długość zbioru. Działa poprzez wywołanie__len__
metody obiektu .__something__
atrybuty są szczególne i zwykle bardziej niż na pierwszy rzut oka i generalnie nie powinny być wywoływane bezpośrednio.Dawno temu zdecydowano, że uzyskanie długości czegoś powinno być funkcją, a nie kodem metody, rozumując, że
len(a)
to znaczenie będzie jasne dla początkujących, alea.len()
nie będzie tak jasne. Kiedy zaczął Python,__len__
nawet nie istniał ilen
był specjalną rzeczą, która działała z kilkoma typami obiektów. Niezależnie od tego, czy sytuacja, którą nas to pozostawia, ma sens, jest tutaj na stałe.źródło
Często jest tak, że „typowym” zachowaniem elementu wbudowanego lub operatora jest wywołanie (o innej i ładniejszej składni) odpowiednich metod magicznych (o takich nazwach
__whatever__
) na obiektach, których dotyczy. Często element wbudowany lub operator ma „wartość dodaną” (jest w stanie obrać różne ścieżki w zależności od zaangażowanych obiektów) - w przypadkulen
vs__len__
jest to tylko odrobina sprawdzenia poprawności wbudowanego elementu wbudowanego, którego brakuje w magiczna metoda:>>> class bah(object): ... def __len__(self): return "an inch" ... >>> bah().__len__() 'an inch' >>> len(bah()) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'str' object cannot be interpreted as an integer
Kiedy widzisz wywołanie funkcji
len
wbudowanej, jesteś pewien, że jeśli program kontynuuje działanie po tym, zamiast zgłaszać wyjątek, wywołanie zwróciło liczbę całkowitą, nieujemną i mniejszą niż 2 ** 31 - kiedy widzisz wezwaniexxx.__len__()
, nie masz pewności (poza tym, że autor kodu albo nie zna Pythona, albo nie ma nic dobrego ;-).Inne wbudowane funkcje zapewniają jeszcze większą wartość dodaną, wykraczającą poza zwykłe sprawdzenie poprawności i czytelności. Jednolite zaprojektowanie całego Pythona do pracy poprzez wywołania wbudowanych i użycie operatorów, nigdy przez wywołania magicznych metod, oszczędza programistom ciężaru zapamiętywania, który przypadek jest który. (Czasami pojawia się błąd: do 2.5 trzeba było wywołać
foo.next()
- w 2.6, chociaż nadal działa to dla wstecznej kompatybilności, powinieneś wywołaćnext(foo)
, a3.*
magiczna metoda jest poprawnie nazwana__next__
zamiast "oops-ey"next
! - ).Zatem ogólną zasadą powinno być, aby nigdy nie wywoływać metody magicznej bezpośrednio (ale zawsze pośrednio przez wbudowaną), chyba że dokładnie wiesz, dlaczego musisz to zrobić (np. Kiedy nadpisujesz taką metodę w podklasie, jeśli podklasa musi odroczyć do nadklasy, która musi zostać wykonana przez jawne wywołanie metody magic).
źródło
def len(x): return "I am a string." print(len(42)) print(len([1,2,3]))
i wydrukowanoI am string
dwukrotnie. Czy możesz to bardziej wyjaśnić?__len__
specjalną metodę (nie funkcję) na rozpatrywanym obiekcie.len
nie inną funkcję (jak w moim przykładzie), która miała taką samą nazwę -len
. Nie ma ostrzeżenia typu „Przedefiniowujesz wbudowaną funkcję len” lub coś w tym rodzaju. Moim zdaniem nie jestem pewien, co powiedział Alex w swojej odpowiedzi.len in vars(__builtins__).values()
.Możesz myśleć o len () jako o mniej więcej odpowiedniku
def len(x): return x.__len__()
Zaletą jest to, że pozwala pisać takie rzeczy jak
somelist = [[1], [2, 3], [4, 5, 6]] map(len, somelist)
zamiast
lub
map(operator.methodcaller('__len__'), somelist)
Jest jednak nieco inne zachowanie. Na przykład w przypadku ints
>>> (1).__len__() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'int' object has no attribute '__len__' >>> len(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: object of type 'int' has no len()
źródło
operator.methodcaller
zamiastoperator.attrgetter
.Możesz sprawdzić dokumentację Pythonda :
>>> class Meta(type): ... def __getattribute__(*args): ... print "Metaclass getattribute invoked" ... return type.__getattribute__(*args) ... >>> class C(object): ... __metaclass__ = Meta ... def __len__(self): ... return 10 ... def __getattribute__(*args): ... print "Class getattribute invoked" ... return object.__getattribute__(*args) ... >>> c = C() >>> c.__len__() # Explicit lookup via instance Class getattribute invoked 10 >>> type(c).__len__(c) # Explicit lookup via type Metaclass getattribute invoked 10 >>> len(c) # Implicit lookup 10
źródło