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
- Nunca se habría ejecutado porque
f
tiene un atributo a. - 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.