He estado jugando con Python recientemente, y una cosa que encuentro un poco extraña es el uso extensivo de 'métodos mágicos', por ejemplo, para hacer que su longitud esté disponible, un objeto implementa un método def __len__(self)
, y luego se llama cuando escribe len(obj)
.
Me preguntaba por qué los objetos no definen simplemente un len(self)
método y lo llaman directamente como miembro del objeto, por ejemplo obj.len()
. Estoy seguro de que debe haber buenas razones para que Python lo haga de la forma en que lo hace, pero como novato aún no he descubierto cuáles son.
python
magic-methods
Greg Beech
fuente
fuente
len()
o sereversed()
aplica a muchos tipos de objetos, pero un método comoappend()
solo se aplica a secuencias, etc.Respuestas:
AFAIK,
len
es especial en este sentido y tiene raíces históricas.Aquí hay una cita de las preguntas frecuentes :
Los otros "métodos mágicos" (en realidad llamados métodos especiales en el folclore de Python) tienen mucho sentido y existen funciones similares en otros lenguajes. Se usan principalmente para código que se llama implícitamente cuando se usa una sintaxis especial.
Por ejemplo:
y así...
fuente
Del Zen de Python:
Esta es una de las razones - con métodos personalizados, los desarrolladores tendrán la libertad de elegir un nombre de método diferente, como
getLength()
,length()
,getlength()
o en absoluto. Python impone un nombre estricto para quelen()
se pueda usar la función común .Todas las operaciones que son comunes para muchos tipos de objetos se ponen en métodos mágicos, como
__nonzero__
,__len__
o__repr__
. Sin embargo, en su mayoría son opcionales.La sobrecarga de operadores también se realiza con métodos mágicos (p
__le__
. Ej. ), Por lo que tiene sentido usarlos también para otras operaciones comunes.fuente
Python usa la palabra "métodos mágicos" , porque esos métodos realmente hacen magia para su programa. Una de las mayores ventajas de utilizar los métodos mágicos de Python es que proporcionan una forma sencilla de hacer que los objetos se comporten como tipos integrados. Eso significa que puede evitar formas desagradables, contrarias a la intuición y no estándar de realizar operadores básicos.
Considere el siguiente ejemplo:
Esto da un error, porque el tipo de diccionario no admite la adición. Ahora, ampliemos la clase de diccionario y agreguemos el método mágico "__add__" :
Ahora, da la siguiente salida.
Por lo tanto, al agregar este método, de repente ha sucedido la magia y el error que estaba recibiendo antes ha desaparecido.
Espero que te aclare las cosas. Para obtener más información, consulte:
Una guía de los métodos mágicos de Python (Rafe Kettler, 2012)
fuente
Algunas de estas funciones hacen más de lo que un solo método podría implementar (sin métodos abstractos en una superclase). Por ejemplo,
bool()
actúa así:También puede estar 100% seguro de que
bool()
siempre devolverá Verdadero o Falso; si confiara en un método, no podría estar completamente seguro de lo que obtendría.Algunas otras funciones que tienen implementaciones relativamente complicadas (más complicadas de lo que probablemente sean los métodos mágicos subyacentes) son
iter()
ycmp()
, y todos los métodos de atributo (getattr
,setattr
ydelattr
). Cosas comoint
también acceden a métodos mágicos al hacer coerción (puede implementar__int__
), pero cumplen una doble función como tipos.len(obj)
es en realidad el único caso en el que no creo que sea diferenteobj.__len__()
.fuente
hasattr()
usaríatry:
/except AttributeError:
y en lugar deif obj.__len__(): return True else: return False
solo diría,return obj.__len__() > 0
pero esas son solo cosas estilísticas.bool(x)
hace referencia por ciertox.__nonzero__()
), su método no funcionaría. Las instancias bool tienen un método__nonzero__()
, y su código seguiría llamándose a sí mismo una vez que obj fuera bool. ¿Quizásbool(obj.__bool__())
debería ser tratado de la misma manera que usted__len__
? (¿O este código realmente funciona para Python 3?)len(x)
yx.__len__()
es que el primero generará OverflowError para longitudes que excedansys.maxsize
, mientras que el segundo generalmente no lo hará para tipos implementados en Python. Sin embargo, eso es más un error que una característica (por ejemplo, el objeto de rango de Python 3.2 puede manejar rangos arbitrariamente grandes, pero usarloslen
con ellos puede fallar. Sin__len__
embargo, también fallan, ya que están implementados en C en lugar de Python)En realidad, no son "nombres mágicos". Es solo la interfaz que un objeto tiene que implementar para proporcionar un servicio determinado. En este sentido, no son más mágicas que cualquier definición de interfaz predefinida que tengas que volver a implementar.
fuente
Si bien la razón es principalmente histórica, existen algunas peculiaridades en la
len
que hacen que el uso de una función en lugar de un método sea apropiado.Algunas operaciones en Python se implementan como métodos, por ejemplo
list.index
ydict.append
, mientras que otras se implementan como métodos invocables y mágicos, por ejemplostr
yiter
yreversed
. Los dos grupos difieren lo suficiente como para justificar el enfoque diferente:str
,int
Y los amigos son tipos. Tiene más sentido llamar al constructor.iter
podría llamar__getitem__
si__iter__
no está disponible y admite argumentos adicionales que no encajan en una llamada de método. Por la misma razónit.next()
se ha cambiado anext(it)
en versiones recientes de Python: tiene más sentido.__iter__
y__next__
se llamafor
bucle. Por coherencia, una función es mejor. Y lo hace mejor para ciertas optimizaciones.repr
actúa como lostr
hace. Tenerstr(x)
versusx.repr()
sería confuso.isinstance
.getattr(x, 'a')
es otra forma de hacerx.a
ygetattr
comparte muchas de las cualidades antes mencionadas.Yo personalmente llamo al primer grupo como método y al segundo grupo como operador. No es una distinción muy buena, pero espero que ayude de alguna manera.
Dicho esto,
len
no encaja exactamente en el segundo grupo. Está más cerca de las operaciones en la primera, con la única diferencia de que es mucho más común que casi todas. Pero lo único que hace es llamar__len__
, y está muy cercaL.index
. Sin embargo, hay algunas diferencias. Por ejemplo,__len__
podría ser llamado para la implementación de otras características, como por ejemplobool
, si se llamó al métodolen
, podría romperbool(x)
con unlen
método personalizado que hace algo completamente diferente.En resumen, tiene un conjunto de características muy comunes que las clases pueden implementar a las que se puede acceder a través de un operador, a través de una función especial (que generalmente hace más que la implementación, como lo haría un operador), durante la construcción del objeto, y todas ellas. comparten algunos rasgos comunes. Todo lo demás es un método. Y
len
es una excepción a esa regla.fuente
No hay mucho que agregar a las dos publicaciones anteriores, pero todas las funciones "mágicas" no son realmente mágicas en absoluto. Son parte del módulo __ builtins__ que se importa implícita / automáticamente cuando se inicia el intérprete. Es decir:
sucede cada vez antes de que comience su programa.
Siempre pensé que sería más correcto si Python solo hiciera esto para el shell interactivo, y requiriera scripts para importar las diversas partes de las incorporaciones que necesitaban. También probablemente un manejo __ main__ diferente sería bueno en shells frente a interactivo. De todos modos, revise todas las funciones y vea cómo es sin ellas:
fuente