python - define - Diferencia entre__getattr__ vs__getattribute__
list api python (5)
Este es solo un ejemplo basado en la explicación de Ned Batchelder .
__getattr__
ejemplo:
class Foo(object):
def __getattr__(self, attr):
print "looking up", attr
value = 42
self.__dict__[attr] = value
return value
f = Foo()
print f.x
#output >>> looking up x 42
f.x = 3
print f.x
#output >>> 3
print (''__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found'')
Y si se usa el mismo ejemplo con __getattribute__
, obtendría >>> RuntimeError: maximum recursion depth exceeded while calling a Python object
Estoy tratando de entender cuándo usar __getattr__
o __getattribute__
. La documentation menciona que __getattribute__
aplica a las clases de nuevo estilo. ¿Qué son las clases de nuevo estilo?
Las clases de estilo nuevo se heredan del object
o de otra clase de estilo nuevo:
class SomeObject(object):
pass
class SubObject(SomeObject):
pass
Las clases de estilo antiguo no:
class SomeObject:
pass
Esto solo se aplica a Python 2; en Python 3 todo lo anterior creará clases de nuevo estilo.
Consulte 9. Clases (tutorial de Python), NewClassVsClassicClass y ¿Cuál es la diferencia entre el estilo antiguo y las clases de estilo nuevo en Python? para detalles.
Las clases de nuevo estilo son las que subclase "objeto" (directa o indirectamente). Tienen un método de clase __new__
además de __init__
y tienen un comportamiento de bajo nivel algo más racional.
Por lo general, querrá anular __getattr__
(si también lo está anulando), de lo contrario, tendrá dificultades para admitir la sintaxis "self.foo" dentro de sus métodos.
Información adicional: http://www.devx.com/opensource/Article/31482/0/page/4
Una diferencia clave entre __getattr__
y __getattribute__
es que __getattr__
solo se invoca si el atributo no se encontró de la manera habitual. Es bueno para implementar un respaldo para los atributos que faltan, y es probablemente uno de los dos que desea.
__getattribute__
se invoca antes de mirar los atributos reales en el objeto, por lo que puede ser difícil de implementar correctamente. Puedes terminar en recursiones infinitas muy fácilmente.
Las clases de estilo nuevo se derivan del object
, las clases de estilo antiguo son aquellas en Python 2.x sin una clase base explícita. Pero la distinción entre clases de estilo antiguo y de estilo nuevo no es lo importante al elegir entre __getattr__
y __getattribute__
.
Es casi seguro que quieres __getattr__
.
Veamos algunos ejemplos simples de los métodos mágicos __getattr__
y __getattribute__
.
__getattr__
Python llamará __getattr__
método __getattr__
cuando solicite un atributo que aún no se ha definido. En el siguiente ejemplo, mi clase Count no tiene ningún método __getattr__
. Ahora, en general, cuando intento acceder a los obj1.mymin
y obj1.mymax
todo funciona bien. Pero cuando intento acceder obj1.mycurrent
atributo obj1.mycurrent
, Python me da AttributeError: ''Count'' object has no attribute ''mycurrent''
class Count():
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent) --> AttributeError: ''Count'' object has no attribute ''mycurrent''
Ahora mi clase Count tiene el método __getattr__
. Ahora cuando intento acceder obj1.mycurrent
atributo obj1.mycurrent
- python me devuelve lo que haya implementado en mi método __getattr__
. En mi ejemplo, cada vez que trato de llamar a un atributo que no existe, python crea ese atributo y lo establece en el valor entero 0.
class Count:
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
def __getattr__(self, item):
self.__dict__[item]=0
return 0
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)
__getattribute__
Ahora veamos el método __getattribute__
. Si tiene el método __getattribute__
en su clase, python invoca este método para cada atributo, independientemente de si existe o no. Entonces, ¿por qué necesitamos el método __getattribute__
? Una buena razón es que puede evitar el acceso a los atributos y hacerlos más seguros como se muestra en el siguiente ejemplo.
Cada vez que alguien intenta acceder a mis atributos que comienza con la subcadena ''cur'', python genera la excepción AttributeError
. De lo contrario, devuelve ese atributo.
class Count:
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattribute__(self, item):
if item.startswith(''cur''):
raise AttributeError
return object.__getattribute__(self,item)
# or you can use ---return super().__getattribute__(item)
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
Importante: para evitar una recursión infinita en el método __getattribute__
, su implementación siempre debe llamar al método de clase base con el mismo nombre para acceder a los atributos que necesita. Por ejemplo: object.__getattribute__(self, name)
o super().__getattribute__(item)
y no self.__dict__[item]
IMPORTANTE
Si su clase contiene los métodos mágicos getattr y getattribute , entonces se llama primero a __getattribute__
. Pero si __getattribute__
genera una excepción AttributeError
, la excepción se ignorará y se __getattr__
método __getattr__
. Vea el siguiente ejemplo:
class Count(object):
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattr__(self, item):
self.__dict__[item]=0
return 0
def __getattribute__(self, item):
if item.startswith(''cur''):
raise AttributeError
return object.__getattribute__(self,item)
# or you can use ---return super().__getattribute__(item)
# note this class subclass object
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)