Estoy tratando de entender cuándo usar __getattr__
o __getattribute__
. La documentación mencionada se __getattribute__
aplica a las clases de estilo nuevo. ¿Qué son las clases de estilo nuevo?
python
getattr
getattribute
Yarin
fuente
fuente
Respuestas:
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 una reserva para los atributos faltantes, 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. Puede terminar en infinitas recursiones muy fácilmente.Las clases de estilo nuevo derivan de
object
, las clases de estilo antiguo son aquellas en Python 2.x sin clase base explícita. Pero la distinción entre clases de estilo antiguo y de estilo nuevo no es importante al elegir entre__getattr__
y__getattribute__
.Casi seguro que quieres
__getattr__
.fuente
__getattribute__
será llamado para cada acceso, y__getattr__
será llamado para los tiempos que__getattribute__
plantean unaAttributeError
. ¿Por qué no simplemente mantenerlo todo en uno?__getattribute__
.objec.__getattribute__
invocamyclass.__getattr__
bajo las circunstancias correctas.Veamos algunos ejemplos simples de ambos
__getattr__
y__getattribute__
métodos mágicos.__getattr__
Python llamará al
__getattr__
método cada vez que solicite un atributo que aún no se haya definido. En el siguiente ejemplo, mi clase Count no tiene ningún__getattr__
método. Ahora en main cuando trato de acceder a ambosobj1.mymin
y losobj1.mymax
atributos todo funciona bien. Pero cuando intento acceder alobj1.mycurrent
atributo, Python me daAttributeError: 'Count' object has no attribute 'mycurrent'
Ahora mi clase Count tiene
__getattr__
método. Ahora, cuando intento acceder alobj1.mycurrent
atributo, Python me devuelve lo que haya implementado en mi__getattr__
método. En mi ejemplo, cada vez que intento llamar a un atributo que no existe, python crea ese atributo y lo establece en el valor entero 0.__getattribute__
Ahora veamos el
__getattribute__
método. Si tiene un__getattribute__
método en su clase, python invoca este método para cada atributo, independientemente de si existe o no. Entonces, ¿por qué necesitamos un__getattribute__
método? 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 una
AttributeError
excepción. De lo contrario, devuelve ese atributo.Importante: para evitar una recursión infinita en el
__getattribute__
método, su implementación siempre debe llamar al método de la clase base con el mismo nombre para acceder a los atributos que necesita. Por ejemplo:object.__getattribute__(self, name)
osuper().__getattribute__(item)
y noself.__dict__[item]
IMPORTANTE
Si su clase contiene métodos mágicos getattr y getattribute , entonces
__getattribute__
se llama primero. Pero si__getattribute__
generaAttributeError
una excepción, se ignorará la excepción y__getattr__
se invocará el método. Vea el siguiente ejemplo:fuente
__getattribute__
pero seguramente no lo sea. Porque según su ejemplo, todo lo que está haciendo__getattribute__
es generar unaAttributeError
excepción si el atributo no está presente en__dict__
el objeto; pero realmente no necesita esto porque esta es la implementación predeterminada__getattribute__
y, de hecho,__getattr__
es exactamente lo que necesita como mecanismo de respaldo.current
se define en instancias deCount
(ver__init__
), por lo que simplemente elevarAttributeError
si el atributo no está allí no es exactamente lo que está sucediendo: difiere__getattr__
para todos los nombres que comienzan con 'cur', incluidoscurrent
, pero tambiéncurious
,curly
...Este es solo un ejemplo basado en la explicación de Ned Batchelder .
__getattr__
ejemplo:Y si se usa el mismo ejemplo con
__getattribute__
Obtendría >>>RuntimeError: maximum recursion depth exceeded while calling a Python object
fuente
__getattr__()
implementaciones del mundo real solo aceptan un conjunto finito de nombres de atributos válidos al generarAttributeError
nombres de atributos no válidos, evitando así problemas sutiles y difíciles de depurar . Este ejemplo acepta incondicionalmente todos los nombres de atributos como válidos, un mal uso extraño (y francamente propenso a errores)__getattr__()
. Si desea "control total" sobre la creación de atributos como en este ejemplo, lo desea__getattribute__()
.defaultdict
.__getattr__
se llamará antes de la búsqueda de superclase. Esto está bien para una subclase directa deobject
, ya que los únicos métodos que realmente le interesan son los métodos mágicos que ignoran la instancia de todos modos, pero para cualquier estructura de herencia más compleja, elimina por completo la capacidad de heredar cualquier cosa del padre.Las clases de estilo nuevo heredan de
object
o de otra clase de estilo nueva:Las clases de estilo antiguo no:
Esto solo se aplica a Python 2: en Python 3, todo lo anterior creará nuevas clases de estilo.
Consulte 9. Clases (tutorial de Python), NewClassVsClassicClass y ¿Cuál es la diferencia entre el estilo antiguo y las nuevas clases de estilo en Python? para detalles.
fuente
Las clases de estilo nuevo son aquellas que subclasifican "objeto" (directa o indirectamente). Tienen un
__new__
método de clase además de__init__
un comportamiento de bajo nivel algo más racional.Por lo general, querrá anular
__getattr__
(si 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
fuente
Al leer el PCB de Beazley & Jones, me topé con un caso de uso explícito y práctico
__getattr__
que ayuda a responder la parte del "cuándo" de la pregunta del OP. Del libro:"El
__getattr__()
método es algo así como una búsqueda general de atributos. Es un método que se llama si el código intenta acceder a un atributo que no existe". Sabemos esto por las respuestas anteriores, pero en la receta de PCB 8.15, esta funcionalidad se utiliza para implementar el patrón de diseño de delegación . Si el Objeto A tiene un atributo Objeto B que implementa muchos métodos que el Objeto A desea delegar, en lugar de redefinir todos los métodos del Objeto B en el Objeto A solo para llamar a los métodos del Objeto B, defina un__getattr__()
método de la siguiente manera:donde _b es el nombre del atributo del Objeto A que es un Objeto B. Cuando se llama a un método definido en el Objeto B en el Objeto A, el
__getattr__
método se invocará al final de la cadena de búsqueda. Esto también haría que el código sea más limpio, ya que no tiene una lista de métodos definidos solo para delegar a otro objeto.fuente