Czy jest jakaś różnica między dzwonieniem len([1,2,3])a [1,2,3].__len__()?
Jeśli nie ma widocznej różnicy, co robi się inaczej za kulisami?
Odpowiedzi:
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:
>>> 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 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).
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ć?
__len__specjalną metodę (nie funkcję) na rozpatrywanym obiekcie.
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.
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
map(list.__len__, somelist)
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()
operator.methodcallerzamiast operator.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