python: ¿Qué sucede cuando el atributo de clase, el atributo de instancia y el método tienen el mismo nombre?

¿Cómo distingue Python un atributo de clase, un atributo de instancia y un método cuando los nombres son los mismos?

class Exam(object): test = "class var" def __init__(self, n): self.test = n def test(self): print "method : ",self.test test_o = Exam("Fine") print dir(test_o) print Exam.test print test_o.test test_o.test() 

Salida:

 ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'test']  Fine Traceback (most recent call last): File "example.py", line 32, in  test_o.test() TypeError: 'str' object is not callable 

Como llamar

  1. atributo de clase, Exam.test -> salida muestra método
  2. atributo de instancia test_o.test -> "Fine"
  3. método test_o.test() -> TypeError: 'str' object is not callable

Los atributos de clase son accesibles a través de la clase:

 YourClass.clsattribute 

oa través de la instancia (si la instancia no ha sobrescrito el atributo de clase):

 instance.clsattribute 

Los métodos, como lo indica ecatmur en su respuesta , son descriptores y se establecen como atributos de clase.

Si accede a un método a través de la instancia, entonces la instancia se pasa como el parámetro self al descriptor. Si desea llamar a un método de la clase, debe pasar explícitamente una instancia como primer argumento. Así que estos son equivalentes:

 instance.method() MyClass.method(instance) 

Usar el mismo nombre para un atributo de instancia y un método hará que el método se oculte a través de la instancia, pero el método todavía está disponible a través de la clase:

 #python3 >>> class C: ... def __init__(self): ... self.a = 1 ... def a(self): ... print('hello') ... >>> Ca  >>> instance = C() >>> instance.a 1 >>> Ca(instance) hello 

Conclusión: no le dé el mismo nombre a los atributos y métodos de instancia. Evito esto dando nombres significativos. Los métodos son acciones, por lo general uso verbos u oraciones para ellos. Los atributos son datos, por lo que uso sustantivos / adjetivos para ellos, y esto evita el uso de los mismos nombres tanto para los métodos como para los atributos.

Tenga en cuenta que simplemente no puede tener un atributo de clase con el mismo nombre que un método, porque el método lo anularía por completo (al final, los métodos son solo atributos de clase que se pueden llamar y que reciben automáticamente una instancia de la clase como primer atributo) .

Puedes escribir

 Exam.test(test_o) 

o

 Exam.test.__get__(test_o)() 

En este último caso, está utilizando el hecho de que los métodos son descriptores para convertir el en un método enlazado, por lo que puede llamarlo entre corchetes.

Cuando escribes test_o.test() , Python no sabe que estás tratando de llamar a un método; es posible que esté intentando llamar a una función u objeto llamable que se haya instalado en el objeto como un miembro de datos de instancia. En su lugar, busca la test atributos, primero en el objeto y luego en su clase, pero como el atributo existe en el objeto, oculta el método en la clase.

El miembro de la clase

 test = "class var" 

no es accesible (de hecho, no existe en ninguna parte), ya que se sobrescribe con la test método; cuando se ejecuta una instrucción de class , su espacio de nombres se recostack en un dictado antes de pasar a su metaclase, y los nombres posteriores anulan los anteriores.

Puede llamar al método como método de clase y pasar su instancia a él:

 Exam.test(test_o) 

O, si no quieres usar el Exam :

 type(test_o).test(test_o) 

Como llamar
atributo de clase, Exam.test

No se puede, porque al ejecutar def test(self) la test del nombre está vinculada al método en la clase y se pierde la referencia a "class var" .

atributo de instancia test_o.test -> “Fine”

Ya lo hiciste

método test_o.test()

No puede llamarlo así porque al ejecutar self.test = n la test nombre está vinculada a cualquier objeto n referencias en la instancia y la referencia al método en la instancia se pierde.

Pero como se señaló en otras respuestas, puede llamar al método en la clase y pasarle la instancia: Exam.test(test_o)