Solución:
len
es una función para obtener la longitud de una colección. Funciona llamando a un objeto __len__
método. __something__
los atributos son especiales y, por lo general, más de lo que se ve a simple vista y, por lo general, no deben llamarse directamente.
Se decidió en algún momento hace mucho tiempo que obtener la longitud de algo debería ser una función y no un código de método, razonando que len(a)
El significado sería claro para los principiantes, pero a.len()
no sería tan claro. Cuando comenzó Python __len__
ni siquiera existía y len
era algo especial que funcionaba con algunos tipos de objetos. Ya sea que la situación que esto nos deja tenga total sentido o no, está aquí para quedarse.
A menudo ocurre que el comportamiento “típico” de un operador incorporado o es llamar (con una sintaxis diferente y más agradable) métodos mágicos adecuados (aquellos con nombres como __whatever__
) en los objetos involucrados. A menudo, el operador integrado o tiene un “valor agregado” (es capaz de tomar diferentes caminos dependiendo de los objetos involucrados) – en el caso de len
vs __len__
, es solo un poco de verificación de cordura en el incorporado que falta en el 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 al len
incorporado, eres seguro que, si el programa continúa después de eso en lugar de generar una excepción, la llamada ha devuelto un número entero, no negativo y <= sys.maxsize
– cuando ve una llamada a xxx.__len__()
, no tienes certeza (excepto que el autor del código no está familiarizado con Python o no es bueno ;-).
Otros componentes incorporados brindan aún más valor agregado más allá de las simples comprobaciones de cordura y legibilidad. Al diseñar uniformemente todo Python para que funcione a través de llamadas a incorporaciones y el 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 aparece un error: hasta la 2.5, tenía que llamar foo.next()
– en 2.6, aunque eso todavía funciona para la compatibilidad con versiones anteriores, debe llamar next(foo)
, y en 3.*
, el método mágico tiene el nombre correcto __next__
en lugar del “oops-ey” next
! -).
Entonces, la regla general debería ser nunca llamar a un método mágico directamente (pero siempre indirectamente a través de un método incorporado) a menos que sepa exactamente por qué necesita hacerlo (por ejemplo, cuando está anulando un método de este tipo en una subclase, si el subclase necesita diferir a la superclase que debe hacerse mediante una llamada explícita al método mágico).
Puede pensar que len () es aproximadamente equivalente a
def len(x):
return x.__len__()
Una ventaja es que te 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 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()