¿Por qué falla setattr en un método enlazado?

En lo siguiente, setattr tiene éxito en la primera invocación, pero falla en la segunda, con:

 AttributeError: 'method' object has no attribute 'i' 

¿Por qué es esto, y hay una manera de establecer un atributo en un método de tal manera que solo exista en una instancia, no para cada instancia de la clase?

 class c: def m(self): print(type(cm)) setattr(cm, 'i', 0) print(type(self.m)) setattr(self.m, 'i', 0) 

Python 3.2.2

La respuesta corta: no hay forma de agregar atributos personalizados a los métodos enlazados.

La respuesta larga sigue.

En Python, hay objetos de función y objetos de método . Cuando define una clase, la instrucción def crea un objeto de función que vive dentro del espacio de nombres de la clase:

 >>> class c: ... def m(self): ... pass ... >>> cm  

Los objetos de función tienen un atributo __dict__ especial que puede contener atributos definidos por el usuario:

 >>> cmi = 0 >>> cm__dict__ {'i': 0} 

Los objetos del método son diferentes bestias. Son objetos pequeños que solo contienen una referencia al objeto de función correspondiente ( __func__ ) y uno a su objeto host ( __self__ ):

 >>> c().m > >>> c().m.__self__ <__main__.c object at 0x02625070> >>> c().m.__func__  >>> c().m.__func__ is cm True 

Los objetos de método proporcionan un __getattr__ especial que reenvía el acceso de atributo al objeto de función:

 >>> c().mi 0 

Esto también es cierto para la propiedad __dict__ :

 >>> c().m.__dict__['a'] = 42 >>> cma 42 >>> c().m.__dict__ is cm__dict__ True 

Sin embargo, la configuración de atributos sigue las reglas predeterminadas y, como no tienen su propio __dict__ , no hay forma de establecer atributos arbitrarios.

Esto es similar a las clases definidas por el usuario que definen __slots__ y no __dict__ slot, cuando se intenta establecer una ranura no existente genera un AttributeError (consulte la documentación en __slots__ para obtener más información):

 >>> class c: ... __slots__ = ('a', 'b') ... >>> x = c() >>> xa = 1 >>> xb = 2 >>> xc = 3 Traceback (most recent call last): File "", line 1, in  AttributeError: 'c' object has no attribute 'c' 

P: “¿Hay una manera de establecer un atributo en un método de modo que solo exista en una instancia, no para cada instancia de la clase?”

A: si

 class c: def m(self): print(type(cm)) setattr(cm, 'i', 0) print(type(self)) setattr(self, 'i', 0) 

La variable estática en las funciones en la publicación a la que te vinculas no es útil para los métodos. Establece un atributo en la función para que este atributo esté disponible la próxima vez que se llame a la función, para que pueda hacer un contador o lo que sea.

Pero los métodos tienen una instancia de objeto asociada con ellos (uno mismo). Por lo tanto, no es necesario establecer atributos en el método, ya que simplemente puede establecerlo en la instancia en su lugar. De hecho, eso es exactamente para lo que sirve la instancia.

La publicación a la que te vinculas muestra cómo hacer una función con una variable estática. Yo diría que en Python hacerlo sería un error. En su lugar, mire esta respuesta: ¿Cuál es el equivalente de Python de las variables estáticas dentro de una función?

Esa es la forma de hacerlo en Python de forma clara y fácilmente comprensible. Usas una clase y la haces llamable. Es posible establecer atributos en las funciones y probablemente haya casos en los que sea una buena idea, pero en general terminará confundiendo a las personas.