¿Por qué acceder a la variable de clase dentro de la clase necesita “self” en Python?

Posible duplicado:
Python ‘auto’ explicado

Estoy aprendiendo Python y tengo una pregunta, más teórica que práctica, sobre el acceso a las variables de clase del método de esta clase.

Por ejemplo tenemos:

class ExampleClass: x = 123 def example_method(self): print(self.x) 

¿Por qué es necesariamente escribir exactamente self.x , no solo x ? x pertenece al espacio de nombres de la clase, y el método que lo usa también le pertenece. ¿Qué me estoy perdiendo? ¿Qué razón hay detrás de ese estilo?

En C ++ puedes escribir:

 class ExampleClass { public: int x; void example_method() { x = 123; cout << x; }; }; 

¡Y funcionará!

De la historia de Python: Agregar soporte para clases definidas por el usuario :

En su lugar, decidí renunciar a la idea de referencias implícitas a variables de instancia. Los lenguajes como C ++ le permiten escribir this-> foo para hacer referencia explícita a la variable de instancia foo (en caso de que haya una variable local separada foo). Por lo tanto, decidí hacer tales referencias explícitas la única forma de hacer referencia a las variables de instancia. Además, decidí que en lugar de hacer que el objeto actual (“este”) fuera una palabra clave especial, simplemente haría de “esto” (o su equivalente) el primer argumento con nombre de un método. Las variables de instancia solo serían referenciadas como atributos de ese argumento.

Con referencias explícitas, no es necesario tener una syntax especial para las definiciones de los métodos ni tiene que preocuparse por una semántica complicada en relación con la búsqueda de variables. En su lugar, uno simplemente define una función cuyo primer argumento corresponde a la instancia, que por convención se denomina “self”. Por ejemplo:

 def spam(self,y): print self.x, y 

Este enfoque se parece a algo que había visto en Modula-3, que ya me había proporcionado la syntax para la importación y el manejo de excepciones. Modula-3 no tiene clases, pero le permite crear tipos de registro que contienen miembros de puntero de función completamente escritos que se inicializan de manera predeterminada a las funciones definidas en las cercanías, y agrega azúcar sintáctica de modo que si x es una variable de registro, y m es un miembro de la función puntero de ese registro, inicializado a la función f, luego llamar a xm (args) es equivalente a llamar a f (x, args). Esto coincide con la implementación típica de objetos y métodos, y hace posible igualar las variables de instancia con los atributos del primer argumento.

Por lo tanto, declarado por el propio BDFL, la única razón real por la que decidió usar el yo explícito sobre el yo implícito es que:

  • es explícito
  • es más fácil de implementar, ya que la búsqueda se debe realizar en tiempo de ejecución (y no en tiempo de comstackción como otros idiomas) y tener un yo implícito podría haber aumentado la complejidad (y por lo tanto el costo) de las búsquedas.

Editar: También hay una respuesta en las preguntas frecuentes de Python.

Parece estar relacionado con el módulo vs manejo de scope de clase, en Python:

 COLOR = 'blue' class TellColor(object): COLOR = 'red' def tell(self): print self.COLOR # references class variable print COLOR # references module variable a = TellColor() a.tell() > red > blue 

Aquí está el contenido que hice en una antigua respuesta con respecto a esta característica:


El problema que encontraste se debe a esto:

Un bloque es una parte del texto del progtwig Python que se ejecuta como una unidad. Los siguientes son bloques: un módulo, un cuerpo de función y una definición de clase.

(…)

Un ámbito define la visibilidad de un nombre dentro de un bloque.

(…)

El scope de los nombres definidos en un bloque de clase se limita al bloque de clase; no se extiende a los bloques de código de los métodos; esto incluye expresiones generadoras, ya que se implementan utilizando un ámbito de función. Esto significa que lo siguiente fallará:

clase A:

  a = 42 b = list(a + i for i in range(10)) 

http://docs.python.org/reference/executionmodel.html#naming-and-binding

Lo anterior significa que: un cuerpo de función es un bloque de código y un método es una función, luego los nombres definidos fuera del cuerpo de función presente en una definición de clase no se extienden al cuerpo de función.


Me pareció extraño cuando estaba leyendo esto, pero así es como Python está hecho a mano:

El scope de los nombres definidos en un bloque de clase se limita al bloque de clase; No se extiende a los bloques de código de los métodos.

Esa es la documentación oficial que dice esto.

.

EDITAR

Heltonbiker escribió un código interesante:

 COLOR = 'blue' class TellColor(object): COLOR = 'red' def tell(self): print self.COLOR # references class variable print COLOR # references module variable a = TellColor() a.tell() > red > blue 

Me hizo preguntarme cómo la instrucción de print COLOR escrita dentro del método tell() provoca la impresión del valor del objeto global COLOR definido fuera de la clase.
Encontré la respuesta en esta parte de la documentación oficial:

Los métodos pueden hacer referencia a nombres globales de la misma manera que las funciones ordinarias. El scope global asociado a un método es el módulo que contiene su definición. (Una clase nunca se usa como un scope global.) Mientras que uno rara vez encuentra una buena razón para usar datos globales en un método, hay muchos usos legítimos del scope global: por una parte, las funciones y los módulos importados en el scope global pueden ser utilizado por métodos, así como funciones y clases definidas en él. Generalmente, la clase que contiene el método se define en este ámbito global (…)

http://docs.python.org/2/tutorial/classes.html#method-objects

Cuando el intérprete tiene que ejecutar print self.COLOR , ya que COLOR no es un atributo de instancia (es decir, el identificador ‘COLOR’ no pertenece al espacio de nombres de la instancia), el intérprete ingresa en el espacio de nombres de la clase de la instancia en busca del identificador ‘COLOR’ y encuéntrelo, por lo que imprime el valor de TellColor.COLOR

Cuando el intérprete tiene que ejecutar el print COLOR , ya que no hay acceso a atributos escrito en esta instrucción, buscará el identificador ‘COLOR’ en el espacio de nombres global, que la documentación oficial dice que es el espacio de nombres del módulo.

Los nombres de atributos que se adjuntan a un objeto (y su clase, y los ancestros de esa clase) no se pueden decidir en el momento de la comstackción. Entonces, o bien hace que la búsqueda de atributos sea explícita, o bien:

  • erradicar variables locales (en métodos) y siempre usar variables de instancia. Esto no sirve, ya que esencialmente elimina las variables locales con todas sus ventajas (al menos en los métodos).
  • decida si una base x refiere a un atributo o local en tiempo de ejecución (con algunas reglas adicionales para decidir cuándo x = ... agrega un nuevo atributo si no hay self.x ). Esto hace que el código sea menos legible, ya que nunca se sabe cuál se supone que debe ser un nombre, y esencialmente convierte todas las variables locales en todos los métodos en parte de la interfaz pública (al adjuntar un atributo de ese nombre cambia el comportamiento de un método).

Ambos tienen la desventaja adicional de que requieren una carcasa especial para los métodos. En este momento, un “método” es solo una función regular a la que se puede acceder mediante un atributo de clase. Esto es muy útil para una gran variedad de buenos casos de uso.