2010-03-20 15 views
59

¿Hay alguna diferencia entre llamar len([1,2,3]) o [1,2,3].__len__()? Si no hay una diferencia visible, ¿qué se hace de forma diferente detrás de las escenas?diferencia entre len() y .__ len __()?

+2

Ver http://stackoverflow.com/questions/496009/is-there-any-case-where-lensomeobj-does-not-call-someobjs-len- función/497096 # 497096 –

Respuesta

65

len es una función para obtener la longitud de una colección. Funciona llamando al método __len__ de un objeto. __something__ atributos son especiales y generalmente más de lo que parece, y generalmente no se deben llamar directamente.

Se decidió en algún momento hace mucho tiempo que la longitud de algo debería ser una función y no un código de método, razonando que el significado de len(a) sería claro para los principiantes, pero a.len() no sería tan claro. Cuando Python comenzó __len__ ni siquiera existía y len era algo especial que funcionaba con algunos tipos de objetos. Independientemente de si la situación que esto nos deja tiene sentido o no, está aquí para quedarse.

+3

Está aquí para quedarse, pero no estaría de más tener un método 'some_collection.length()' además. En realidad, '.size()' sería mejor, porque las estructuras de datos basadas en árboles tienen un tamaño, pero no se representa fácilmente para tener una longitud. –

+3

@EvgeniSergeev Y, de hecho, objetos como ese tienen tales atributos: una matriz numpy tiene un atributo 'forma' (que no es un método en absoluto, ya que es una propiedad del objeto, no una acción sobre el objeto). –

+4

fwiw, una razón plausible para esto es que python _realmente really_ no le gusta reservar nombres de métodos - una clase 'Rectangle' podría querer tener' length' y 'width', y si' .length() 'se reservaron efectivamente, tendrías que cambiar el nombre torpemente o romper el tipado pato. – Eevee

19

Se puede pensar en len() por ser más o menos equivalente a

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

Una de las ventajas es que permite escribir cosas como

somelist = [[1], [2, 3], [4, 5, 6]] 
map(len, somelist) 

en lugar de

map(list.__len__, somelist) 

o

map(operator.methodcaller('__len__'), somelist) 

Sin embargo, hay un comportamiento ligeramente diferente. Por ejemplo, en el caso de enteros

>>> (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

Supongo que te refieres a 'operator.methodcaller' en lugar de' operator.attrgetter'. – Elazar

+0

@Elazar, sí, lo hice. gracias –

52

Es a menudo el caso de que el comportamiento "típico" de un built-in o el explotador es llamar (con diferentes y sintaxis mejor) adecuados métodos mágicos (queridos con nombres como __whatever__) en los objetos involucrados. A menudo, el operador incorporado tiene "valor agregado" (es capaz de tomar diferentes caminos dependiendo de los objetos involucrados) - en el caso de len contra __len__, es solo un poco de cordura verificar en el built-in que falta del método mágico:

>>> 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 

Cuando vea una llamada a la len incorporado, que está seguro de que, si el programa continúa después de que en lugar de lanzar una excepción, la llamada ha devuelto un número entero , no negativo, y menos de 2 ** 31 - cuando ve una llamada al xxx.__len__(), no tiene certeza (excepto que el autor del código no está familiarizado con Python o no sirve para nada ;-).

Otros componentes incorporados proporcionan incluso más valor agregado más allá de simples controles de cordura y legibilidad. Al diseñar uniformemente todo el Python para que funcione a través de llamadas a builtins y uso de operadores, nunca a través de llamadas a métodos mágicos, los programadores se libran de la carga de recordar qué caso es cuál. (A veces se produce un error: hasta 2.5, tuvo que llamar al foo.next() - en 2.6, mientras que todavía funciona para la compatibilidad con versiones anteriores, debe llamar al next(foo), y en 3.*, el método mágico se llama correctamente __next__ en lugar de "oops- ey "next! -).

Por lo tanto, la regla general debe ser no llamar directamente a un método mágico (pero siempre indirectamente a través de un built-in) a menos que sepa exactamente por qué necesita hacerlo (p., cuando anula dicho método en una subclase, si la subclase debe diferir a la superclase, debe hacerlo mediante una llamada explícita al método mágico).

+0

Soy un usuario principiante de Python (no el programador principiante pensado) y no estoy seguro de "Cuando veas una llamada a la len incorporada, estás seguro de que si el programa continúa después de eso en lugar de presentar una excepción ". Intenté esto: 'def len (x): \t return" I am a string. " print (len (42)) print (len ([1,2,3])) 'e imprimió' I am string' dos veces. ¿Puedes explicarlo más? –

+2

@ DarekNędza Esto no tiene nada que ver con lo anterior, que se trata de la lenidad integrada. Usted acaba de definir su función len, que por supuesto puede devolver lo que quiera. OP habló sobre la lenidad incorporada, que llama a '__len__' special _method_ (no funciona) en el objeto bajo consideración. – Veky

+0

@Veky ¿Cómo puedo estar seguro de que estoy llamando a la función incorporada 'len' no a alguna otra función (como en mi ejemplo) que pasó a tener el mismo nombre -' len'. No hay advertencia como "Estás redefiniendo función incorporada len" o algo como esto. En mi opinión, no puedo estar seguro de lo que dijo Alex en su respuesta. –

2

Puede comprobar 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 
Cuestiones relacionadas