tutorial - ¿Por qué Python usa dos guiones bajos para ciertas cosas?
tutorial python desde cero (7)
Soy bastante nuevo en los lenguajes de programación actuales, y Python es el primero. Conozco un poco de Linux, lo suficiente como para conseguir un trabajo de verano (todavía estoy en la escuela secundaria), y en el trabajo, tengo mucho tiempo libre que estoy usando para aprender Python.
Una cosa me ha estado ayudando. ¿Qué es exactamente diferente en Python cuando tienes expresiones como
x.__add__(y) <==> x+y
x.__getattribute__(''foo'') <==> x.foo
Sé lo que hacen los métodos y esas cosas, y entiendo lo que hacen, pero mi pregunta es: ¿En qué se diferencian esos dos métodos de guión bajo de los equivalentes de aspecto más simple?
PD, no me importa que me enseñen sobre la historia de la programación, de hecho, me parece muy útil saber :) Si estos son principalmente aspectos históricos de Python, no dudes en comenzar a divagar.
Aquí está el creador de Python explicándolo :
... en lugar de idear una nueva sintaxis para tipos especiales de métodos de clase (como inicializadores y destructores), decidí que estas características podrían manejarse simplemente requiriendo al usuario implementar métodos con nombres especiales como
__init__
,__del__
, y así adelante. Esta convención de nomenclatura se tomó de C, donde los identificadores que comienzan con guiones bajos están reservados por el compilador y a menudo tienen un significado especial (por ejemplo, macros como__FILE__
en el preprocesador C).
...
También utilicé esta técnica para permitir que las clases de usuarios redefinan el comportamiento de los operadores de Python. Como se señaló anteriormente, Python se implementa en C y utiliza tablas de punteros a función para implementar diversas capacidades de objetos incorporados (por ejemplo, "obtener atributo", "agregar" y "llamar"). Para permitir que estas capacidades se definan en clases definidas por el usuario,
__add__
los diversos punteros a nombres de métodos especiales, como__getattr__
,__add__
y__call__
. Existe una correspondencia directa entre estos nombres y las tablas de indicadores de función que uno debe definir al implementar nuevos objetos de Python en C.
Bueno, el poder para el programador es bueno, así que debería haber una forma de personalizar el comportamiento. Al igual que la sobrecarga del operador ( __add__
, __div__
, __ge__
, ...), acceso a atributos ( __getattribute__
, __getattr__
(esos dos son diferentes), __delattr__
, ...) etc. En muchos casos, al igual que los operadores, los mapas de sintaxis habituales 1: 1 al método respectivo. En otros casos, hay un procedimiento especial que en algún momento implica llamar al método respectivo; por ejemplo, __getattr__
solo se __getattr__
si el objeto no tiene el atributo solicitado y __getattribute__
no se implementa o no se __getattribute__
AttributeError. Y algunos de ellos son temas realmente avanzados que te hacen entrar en las agallas del sistema de objetos y rara vez se necesitan. Entonces no hay necesidad de aprenderlos todos, solo consulte la referencia cuando lo necesite / desee saber. Hablando de referencia, aquí está .
Cuando inicia un método con dos guiones bajos (y sin guiones bajos), se aplican las reglas de creación de nombres de Python. Esta es una forma de simular libremente la palabra clave private
de otros lenguajes OO, como C ++ y Java. (Aun así, el método aún no es técnicamente privado en la forma en que los métodos Java y C ++ son privados, pero es "más difícil de conseguir" desde fuera de la instancia).
Los métodos con dos guiones bajos principales y dos finales se consideran métodos "incorporados", es decir, son utilizados por el intérprete y generalmente son implementaciones concretas de operadores sobrecargados u otras funcionalidades incorporadas.
Desde una perspectiva histórica, los guiones bajos principales a menudo se han usado como un método para indicar al programador que los nombres se deben considerar internos al paquete / módulo / biblioteca que los define. En los idiomas que no proporcionan un buen soporte para espacios de nombres privados, el uso de guiones bajos es una convención para emular eso. En Python, cuando defines un método llamado ''__foo__'', el programador de mantenimiento sabe por el nombre que está sucediendo algo especial que no está sucediendo con un método llamado ''foo''. Si Python hubiera elegido usar ''agregar'' como el método interno para sobrecargar ''+'', entonces nunca podría tener una clase con un método ''agregar'' sin causar mucha confusión. Los guiones bajos sirven como una señal de que algo de magia sucederá.
Se usan para especificar que el intérprete de Python debería usarlos en situaciones específicas.
Por ejemplo, la función __add__
permite que el operador +
trabaje para clases personalizadas. De lo contrario, obtendrá algún tipo de error no definido al intentar agregar.
Varias otras preguntas ahora se marcan como duplicados de esta pregunta, y al menos dos de ellas preguntan qué se llama a los métodos __spam__
, o a qué se llama la convención, y ninguna de las respuestas existentes lo cubre, por lo que:
Realmente no hay un nombre oficial para ninguno de los dos.
Muchos desarrolladores los llaman extraoficialmente "métodos dunder", para "Double UNDERscore".
Algunas personas usan el término "métodos mágicos", pero eso es algo ambiguo entre los métodos de dunder de significado, los métodos especiales (ver a continuación), o algo en algún lugar entre los dos.
Hay un término oficial "atributos especiales", que se superpone de cerca pero no completamente con los métodos dunder. El capítulo Modelo de datos en la referencia nunca explica completamente qué es un atributo especial, pero la idea básica es que es al menos uno de los siguientes:
- Un atributo que proporciona el propio intérprete o su código incorporado, como
__name__
en una función. - Un atributo que es parte de un protocolo implementado por el propio intérprete, como
__add__
para el operador+
o__getitem__
para indexación y división. - Un atributo que el intérprete puede consultar especialmente ignorando la instancia y dirigiéndose directamente a la clase, como
__add__
nuevo.
La mayoría de los atributos especiales son métodos, pero no todos (por ejemplo, __name__
no). Y la mayoría usa la convención "dunder", pero no todos (por ejemplo, el next
método en iteradores en Python 2.x).
Y mientras tanto, la mayoría de los métodos dunder son atributos especiales, pero no todos, en particular, no es raro que stdlib o las bibliotecas externas quieran definir sus propios protocolos que funcionan de la misma manera, como el protocolo pickle
.
[Especulación] Python fue influenciado por Algol68 , Guido posiblemente usó Algol68 en la Universidad de Ámsterdam, donde Algol68 tiene un " régimen de stropping " similar llamado "Quote stropping". En Algol68 los operadores, tipos y palabras clave pueden aparecer en un tipo de letra diferente (usualmente ** negrita **, o __simultado __), en los archivos de código fuente este tipo de letra se logra con comillas, ej. ''Abs'' (citando similar a citar en '' wikitext '' )
Algol68 ⇒ Python (operadores asignados a funciones miembro)
- ''y'' ⇒ __and__
- ''o'' ⇒ __or__
- ''no'' ⇒ no
- ''entier'' ⇒ __trunc__
- ''shl'' ⇒ __lshift__
- ''shr'' ⇒ __rshift__
- ''upb'' ⇒ __sizeof__
- ''largo'' ⇒ __long__
- ''int'' ⇒ __int__
- ''real'' ⇒ __float__
- ''formato'' ⇒ __format__
- ''repr'' ⇒ __repr__
- ''abs'' ⇒ __abs__
- ''menos'' ⇒ __neg__
- ''menos'' ⇒ __sub__
- ''más'' ⇒ __add__
- ''veces'' ⇒ __mul__
- ''mod'' ⇒ __mod__
- ''div'' ⇒ __truediv__
- ''sobre'' ⇒ __div__
- ''arriba'' ⇒ __pow__
- ''im'' ⇒ imag
- ''re'' ⇒ real
- ''conj'' ⇒ conjugado
En Algol68 estos se denominaron como nombres audaces , por ejemplo, abs , pero "bajo-bajo-abs" __abs__ en python.
Mis 2 centavos: ¢ Entonces, a veces, como un fantasma, cuando cortas y pegas las clases de Python en un wiki reencarnas mágicamente las palabras clave en negrita de Algol68. ¢