2010-03-20 18 views
59

Czy jest jakaś różnica między wywołaniem len([1,2,3]) lub [1,2,3].__len__()? Jeśli nie ma widocznej różnicy, co robi się inaczej za kulisami?różnica między len() i .__ len __()?

+2

Zobacz http://stackoverflow.com/questions/496009/is-there-any-case-where-lensomeobj-does-not-call-someobjs-len- function/497096 # 497096 –

Odpowiedz

65

len to funkcja pozwalająca uzyskać długość kolekcji. Działa poprzez wywołanie metody obiektu __len__. Atrybuty __something__ są wyjątkowe i zwykle bardziej niż na pierwszy rzut oka, i generalnie nie powinny być wywoływane bezpośrednio.

Zdecydowano w pewnym momencie dawno coraz długość coś powinno być funkcją, a nie kod metoda, rozumowanie, że sens len(a) „s byłoby jasne dla początkujących ale a.len() nie byłoby tak oczywiste. Kiedy Python się rozpoczął __len__ nawet nie istniał, a len był specjalną rzeczą, która działała z kilkoma typami obiektów. Niezależnie od tego, czy sytuacja, którą nam to pozostawia, ma sens, pozostajemy tutaj.

+3

Zostanie na miejscu, ale nie zaszkodzi mieć również metody 'some_collection.length()'. Właściwie, '.size()' byłaby lepsza, ponieważ struktury danych oparte na drzewach mają rozmiar, ale nie są łatwo ilustrowane, aby mieć długość. –

+3

@EvgeniSergeev Rzeczywiście, takie obiekty mają takie atrybuty - tablica numpy ma atrybut "shape" (który w ogóle nie jest metodą, ponieważ jest to właściwość obiektu, a nie akcja na obiekcie). –

+4

fwi, jednym z prawdopodobnych powodów jest to, że python _really naprawdę_ nie lubi rezerwowania nazw metod - klasa 'Rectangle' może chcieć mieć' length' i 'width', a jeśli' .length() 'zostały skutecznie zarezerwowane, musiałbyś niezręcznie zmienić nazwę lub przerwać pisanie na maszynie. – Eevee

19

Można myśleć o len() jako grubsza odpowiada

def len(x): 
    return x.__len__() 

Jedną z zalet jest to, że pozwala na pisanie 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 trochę inne zachowanie. Na przykład w przypadku int

>>> (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() 
+2

Zakładam, że masz na myśli 'operator.methodcaller' zamiast' operator.attrgetter'. – Elazar

+0

@Elazar, tak, zrobiłem. dziękuje –

52

To często zdarza się, że „typowe” zachowanie wbudowany lub operator jest wywołanie (z różnych i ładniejsze składniowych) nadaje metod magicznych (te o nazwach takich jak __whatever__) na przedmiotach. Często wbudowany lub operator ma "wartość dodaną" (może przyjmować różne ścieżki w zależności od obiektów) - w przypadku len vs __len__, to tylko trochę sprawdzenia poprawności na wbudowanym, którego brakuje z magicznym sposobem:

>>> 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 len wbudowany, jesteś pewien, że jeśli program jest kontynuowany po tym zamiast podniesienia wyjątek, rozmowa wróciła liczbę całkowitą , nieujemny i mniej niż 2 ** 31 - kiedy widzisz połączenie z xxx.__len__(), nie masz pewności (z wyjątkiem tego, że autor kodu jest albo nie zna Pythona, albo do niczego dobrego ;-).

Inne wbudowane elementy zapewniają jeszcze więcej wartości dodanej niż proste testy poprawności i czytelność. Poprzez jednolite projektowanie całego Pythona do pracy za pośrednictwem wywołań do wbudowań i korzystania z operatorów, nigdy poprzez połączenia z magicznymi metodami, programiści są oszczędzani od ciężaru pamiętania, który przypadek jest który. (Czasami pojawia się błąd: do wersji 2.5 trzeba było zadzwonić pod numer foo.next() - w wersji 2.6, mimo że nadal działa pod kątem kompatybilności wstecznej, należy wywołać next(foo), a w 3.* metoda magiczna ma poprawną nazwę __next__ zamiast "oops- ey "next! -).

Tak więc ogólną zasadą powinno być nigdy nie wywoływanie metody magicznej bezpośrednio (ale zawsze pośrednio poprzez wbudowane), chyba że wiesz dokładnie, dlaczego musisz to zrobić (np., kiedy nadpisujesz taką metodę w podklasie, jeśli podklasa musi odroczyć nadklasę, która musi zostać wykonana poprzez jawne wywołanie metody magicznej).

+0

Jestem początkującym użytkownikiem Pythona (nie myślał o tym początkujący programista) i nie jestem pewien "Kiedy zobaczysz połączenie z wbudowanym lenem, jesteś pewien, że jeśli program będzie kontynuowany po tym, zamiast zgłaszać wyjątek ". Próbowałem tego: 'def len (x): \t return" Jestem ciągiem ". print (len (42)) print (len ([1,2,3])) 'i wydrukowano' I'm string' dwa razy. Czy możesz wyjaśnić to więcej? –

+2

@ DarekNędza To nie ma nic wspólnego z powyższym, co dotyczy wbudowanego lena. Po prostu zdefiniowałeś swoją funkcję len, która może oczywiście przywrócić cokolwiek zechcesz. OP mówił o builtin len, który nazywa '__len__' specjalnym _method_ (nie funkcją) na rozważanym obiekcie. – Veky

+0

@ Veky Jak mogę się upewnić, że wywołuję wbudowaną funkcję 'len', a nie inną funkcję (jak w moim przykładzie), która miała tę samą nazwę -' len'. Nie ma ostrzeżenia typu "Jesteś redefiniujesz wbudowaną funkcję len" lub coś podobnego. Moim zdaniem nie mogę być pewien, co Alex powiedział w swojej odpowiedzi. –

2

Można sprawdzić Pythond docs:

>>> 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 
Powiązane problemy