una sucesion sintaxis reglas recursivo recursividad recursiva potencia ordenar lista ejemplos con python class oop recursion getattr

python - sucesion - ¿Cómo implemento__getattribute__ sin un error de recursión infinito?



sintaxis de recursividad (6)

¿Cómo se __getattribute__ método __getattribute__ ?

Se llama antes de la búsqueda punteada normal. Si aumenta AttributeError , entonces llamamos __getattr__ .

El uso de este método es bastante raro. Solo hay dos definiciones en la biblioteca estándar:

$ grep -Erl "def __getattribute__/(self" cpython/Lib | grep -v "/test/" cpython/Lib/_threading_local.py cpython/Lib/importlib/util.py

Mejores prácticas

La forma correcta de controlar mediante programación el acceso a un solo atributo es con property . La clase D debe escribir de la siguiente manera (con el colocador y el eliminador opcionalmente para reproducir el comportamiento aparente previsto):

class D(object): def __init__(self): self.test2=21 @property def test(self): return 0. @test.setter def test(self, value): ''''''dummy function to avoid AttributeError on setting property'''''' @test.deleter def test(self): ''''''dummy function to avoid AttributeError on deleting property''''''

Y el uso:

>>> o = D() >>> o.test 0.0 >>> o.test = ''foo'' >>> o.test 0.0 >>> del o.test >>> o.test 0.0

Una propiedad es un descriptor de datos, por lo tanto, es lo primero que se busca en el algoritmo de búsqueda de puntos normal.

Opciones para __getattribute__

Tiene varias opciones si necesita implementar la búsqueda de cada atributo mediante __getattribute__ .

  • elevar AttributeError , provocando que se __getattr__ a __getattr__ (si se implementó)
  • devolver algo de ella por
    • usando super para llamar a la implementación principal (probablemente el object )
    • llamando __getattr__
    • implementando su propio algoritmo de búsqueda punteada de alguna manera

Por ejemplo:

class NoisyAttributes(object): def __init__(self): self.test=20 self.test2=21 def __getattribute__(self, name): print(''getting: '' + name) try: return super(NoisyAttributes, self).__getattribute__(name) except AttributeError: print(''oh no, AttributeError caught and reraising'') raise def __getattr__(self, name): """Called if __getattribute__ raises AttributeError""" return ''close but no '' + name >>> n = NoisyAttributes() >>> nfoo = n.foo getting: foo oh no, AttributeError caught and reraising >>> nfoo ''close but no foo'' >>> n.test getting: test 20

Lo que originalmente querías

Y este ejemplo muestra cómo podrías hacer lo que originalmente querías:

class D(object): def __init__(self): self.test=20 self.test2=21 def __getattribute__(self,name): if name==''test'': return 0. else: return super(D, self).__getattribute__(name)

Y se comportará así:

>>> o = D() >>> o.test = ''foo'' >>> o.test 0.0 >>> del o.test >>> o.test 0.0 >>> del o.test Traceback (most recent call last): File "<pyshell#216>", line 1, in <module> del o.test AttributeError: test

Revisión de código

Tu código con comentarios Tiene una búsqueda punteada en self en __getattribute__ . Es por eso que obtienes un error de recursión. Puede verificar si el nombre es "__dict__" y usar super para solucionarlo, pero eso no cubre __slots__ . Lo dejo como un ejercicio para el lector.

class D(object): def __init__(self): self.test=20 self.test2=21 def __getattribute__(self,name): if name==''test'': return 0. else: # v--- Dotted lookup on self in __getattribute__ return self.__dict__[name] >>> print D().test 0.0 >>> print D().test2 ... RuntimeError: maximum recursion depth exceeded in cmp

Quiero anular el acceso a una variable en una clase, pero devolver todos los demás normalmente. ¿Cómo puedo lograr esto con __getattribute__ ?

Intenté lo siguiente (que también debería ilustrar lo que intento hacer) pero aparece un error de recursión:

class D(object): def __init__(self): self.test=20 self.test2=21 def __getattribute__(self,name): if name==''test'': return 0. else: return self.__dict__[name] >>> print D().test 0.0 >>> print D().test2 ... RuntimeError: maximum recursion depth exceeded in cmp


¿Seguro que quieres usar __getattribute__ ? ¿Qué estás realmente tratando de lograr?

La forma más fácil de hacer lo que preguntas es:

class D(object): def __init__(self): self.test = 20 self.test2 = 21 test = 0

o:

class D(object): def __init__(self): self.test = 20 self.test2 = 21 @property def test(self): return 0

Editar: Tenga en cuenta que una instancia de D tendría diferentes valores de test en cada caso. En el primer caso, d.test sería 20, en el segundo sería 0. d.test que te explique por qué.

Edit2: Greg señaló que el ejemplo 2 fallará porque la propiedad es de solo lectura y el método __init__ intentó establecerlo en 20. Un ejemplo más completo sería:

class D(object): def __init__(self): self.test = 20 self.test2 = 21 _test = 0 def get_test(self): return self._test def set_test(self, value): self._test = value test = property(get_test, set_test)

Obviamente, como clase esto es casi completamente inútil, pero te da una idea para moverte.


Aquí hay una versión más confiable:

class D(object): def __init__(self): self.test = 20 self.test2 = 21 def __getattribute__(self, name): if name == ''test'': return 0. else: return super(D, self).__getattribute__(name)

Llama al método __ getattribute__ de la clase padre, eventualmente volviendo al objeto. __ getattribute__ método si otros antepasados ​​no lo anulan.


En realidad, creo que quieres usar el método especial __getattr__ su lugar.

Cita de los documentos de Python:

__getattr__( self, name)

Se invoca cuando una búsqueda de atributo no ha encontrado el atributo en los lugares habituales (es decir, no es un atributo de instancia ni se encuentra en el árbol de clases por sí mismo). nombre es el nombre del atributo. Este método debe devolver el valor del atributo (calculado) o generar una excepción AttributeError.
Tenga en cuenta que si el atributo se encuentra a través del mecanismo normal, __getattr__() no se llama. (Esta es una asimetría intencional entre __getattr__() y __setattr__() .) Esto se hace tanto por razones de eficiencia como porque de lo contrario __setattr__() no tendría forma de acceder a otros atributos de la instancia. Tenga en cuenta que, al menos para las variables de instancia, puede simular el control total al no insertar ningún valor en el diccionario de atributos de la instancia (sino insertarlos en otro objeto). Vea el __getattribute__() continuación para obtener un control total en las clases de nuevo estilo.

Nota: para que esto funcione, la instancia no debe tener un atributo de test , por lo que la línea self.test=20 debe eliminarse.


Obtiene un error de recursividad porque su intento de acceder al atributo self.__dict__ dentro de __getattribute__ invoca nuevamente su __getattribute__ . Si utiliza __getattribute__ object en su lugar, funciona:

class D(object): def __init__(self): self.test=20 self.test2=21 def __getattribute__(self,name): if name==''test'': return 0. else: return object.__getattribute__(self, name)

Esto funciona porque object (en este ejemplo) es la clase base. Al llamar a la versión base de __getattribute__ evitas el infierno recursivo en el que estabas antes.

Salida Ipython con código en foo.py:

In [1]: from foo import * In [2]: d = D() In [3]: d.test Out[3]: 0.0 In [4]: d.test2 Out[4]: 21

Actualizar:

Hay algo en la sección titulada Acceso a más atributos para las clases de nuevo estilo en la documentación actual, donde recomiendan hacer exactamente esto para evitar la recursión infinita.


Referencia de lenguaje Python:

Para evitar recursiones infinitas en este 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, el object.__getattribute__(self, name) .

Sentido:

def __getattribute__(self,name): ... return self.__dict__[name]

__dict__ por un atributo llamado __dict__ . Debido a que es un atributo, se llama a __dict__ en la búsqueda de __dict__ que llama __getattribute__ que llama ... yada yada yada

return object.__getattribute__(self, name)

Usar las clases base __getattribute__ ayuda a encontrar el atributo real.