type define argument python oop encapsulation getattr getattribute

define - list api python



Comprender la diferencia entre__getattr__ y__getattribute__ (3)

Estoy tratando de entender la diferencia entre __getattr__ y __getattribute__ , sin embargo, estoy fallando en ello.

Pregunta de desbordamiento de pila Diferencia entre getattr vs getattribute dice,

__getattribute__ se invoca antes de ver los atributos reales en el objeto, por lo que puede ser difícil de implementar correctamente. Puedes terminar en infinitas recursiones muy fácilmente.

No tengo ni idea de lo que eso significa.

Luego continúa diciendo:

Es casi seguro que quieres __getattr__ .

¿Por qué?

Leí que si __getattribute__ falla, se llama __getattr__ . Entonces, ¿por qué hay dos métodos diferentes haciendo lo mismo? Si mi código implementa las nuevas clases de estilo, ¿qué debería usar?

Estoy buscando algunos ejemplos de código para aclarar esta pregunta. Busqué en Google lo mejor que pude, pero las respuestas que encontré no discuten el problema a fondo.

Si hay alguna documentación, estoy listo para leer eso.


Algunos conceptos básicos primero.

Con los objetos, debes lidiar con sus atributos. Ordinariamente hacemos instance.attribute . A veces necesitamos más control (cuando no conocemos el nombre del atributo por adelantado).

Por ejemplo, instance.attribute se convertiría en getattr(instance, attribute_name) . Usando este modelo, podemos obtener el atributo suministrando el nombre_atributo como una cadena.

Uso de __getattr__

También puede decirle a una clase cómo tratar con los atributos que no gestiona explícitamente y hacerlo a través del método __getattr__ .

Python llamará a este método cada vez que solicite un atributo que aún no se haya definido, para que pueda definir qué hacer con él.

Un caso de uso clásico:

class A(dict): def __getattr__(self, name): return self[name] a = A() # Now a.somekey will give a[''somekey'']

Advertencias y uso de __getattribute__

Si necesita capturar cada atributo independientemente de si existe o no , use __getattribute__ en __getattribute__ lugar. La diferencia es que __getattr__ solo recibe un llamado por atributos que realmente no existen. Si establece un atributo directamente, al hacer referencia a ese atributo lo recuperará sin llamar __getattr__ .

__getattribute__ se llama todas las veces.


Creo que las otras respuestas han hecho un gran trabajo al explicar la diferencia entre __getattr__ y __getattribute__ , pero una cosa que podría no estar clara es por qué querrías usar __getattribute__ . Lo bueno de __getattribute__ es que esencialmente te permite sobrecargar el punto cuando accedes a una clase. Esto le permite personalizar cómo se accede a los atributos en un nivel bajo. Por ejemplo, supongamos que quiero definir una clase en la que todos los métodos que solo toman un auto argumento se traten como propiedades:

# prop.py import inspect class PropClass(object): def __getattribute__(self, attr): val = super(PropClass, self).__getattribute__(attr) if callable(val): argcount = len(inspect.getargspec(val).args) # Account for self if argcount == 1: return val() else: return val else: return val

Y del intérprete interactivo:

>>> import prop >>> class A(prop.PropClass): ... def f(self): ... return 1 ... >>> a = A() >>> a.f 1

Por supuesto, este es un ejemplo tonto y es probable que nunca quieras hacer esto, pero te muestra el poder que puedes obtener anulando __getattribute__ .


__getattribute__ se __getattribute__ cada vez que se produce un acceso de atributo.

class Foo(object): def __init__(self, a): self.a = 1 def __getattribute__(self, attr): try: return self.__dict__[attr] except KeyError: return ''default'' f = Foo(1) f.a

Esto causará recursión infinita. El culpable aquí es el return self.__dict__[attr] línea return self.__dict__[attr] . Vamos a pretender (está lo suficientemente cerca de la verdad) que todos los atributos están almacenados en self.__dict__ y disponibles por su nombre. La línea

f.a

intenta acceder al atributo a de f . Esto llama a f.__getattribute__(''a'') . __getattribute__ luego intenta cargar self.__dict__ . __dict__ es un atributo de self == f y entonces python llama a f.__getattribute__(''__dict__'') que nuevamente intenta acceder al atributo ''__dict__ ''. Esta es una recursión infinita.

Si __getattr__ hubiera usado en su lugar, entonces

  1. Nunca se habría ejecutado porque f tiene un atributo a.
  2. Si se hubiera ejecutado, (digamos que usted solicitó fb ), entonces no se habría llamado para encontrar __dict__ porque ya está allí y __getattr__ se invoca solo si todos los otros métodos para encontrar el atributo han fallado .

La forma "correcta" de escribir la clase anterior usando __getattribute__ es

class Foo(object): # Same __init__ def __getattribute__(self, attr): return super(Foo, self).__getattribute__(attr)

super(Foo, self).__getattribute__(attr) vincula el método __getattribute__ de la superclase ''más cercana'' (formalmente, la siguiente clase del orden de resolución de métodos de la clase, o MRO) al objeto actual self y luego lo llama y deja eso la obra.

Todos estos problemas se evitan mediante el uso de __getattr__ que permite que Python lo haga de manera normal hasta que no se encuentre un atributo. En ese punto, Python le da el control a su método __getattr__ y le permite obtener algo.

También vale la pena señalar que puede ejecutar la recursión infinita con __getattr__ .

class Foo(object): def __getattr__(self, attr): return self.attr

Lo dejaré como un ejercicio.