¿Cómo anulo __getattr__ sin romper el comportamiento predeterminado?

4 minutos de lectura

avatar de usuario de sheats
fundas

¿Cómo anulo el __getattr__ método de una clase sin romper el comportamiento predeterminado?

  • “Romper”, pide, no “cambiar”. Eso está bastante claro: los atributos “elegantes” no deben interferir con los atributos integrados y deben comportarse como ellos tanto como sea posible. La respuesta de Michael es correcta y útil.

    – olooney

    9 de marzo de 2010 a las 0:04

Avatar de usuario de Michael Williamson
miguel williamson

Primordial __getattr__ debería estar bien — __getattr__ solo se llama como último recurso, es decir, si no hay atributos en la instancia que coincidan con el nombre. Por ejemplo, si accede foo.bardespués __getattr__ sólo se llamará si foo no tiene atributo llamado bar. Si el atributo es uno que no desea manejar, aumente AttributeError:

class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError

Sin embargo, a diferencia de __getattr__, __getattribute__ se llamará primero (solo funciona para nuevas clases de estilo, es decir, aquellas que heredan del objeto). En este caso, puede conservar el comportamiento predeterminado de la siguiente manera:

class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)

Ver los documentos de Python para más.

  • Bah, tu edición tiene lo mismo que estaba mostrando en mi respuesta, +1.

    Roger paté

    8 de marzo de 2010 a las 23:51

  • Genial, a Python no parece gustarle llamar a los súper __getattr__ — ¿Alguna idea de qué hacer? (AttributeError: 'super' object has no attribute '__getattr__')

    – gatoatigrado

    11 de junio de 2013 a las 23:33


  • Sin ver su código, es difícil saberlo, pero parece que ninguna de sus superclases define obtener.

    – Colin VH

    11/04/2015 a las 16:00

  • Esto funciona con hasattr también porque: “Esto se implementa al llamar a getattr (objeto, nombre) y ver si genera una excepción o no”. docs.python.org/2/library/functions.html#hasattr

    – ShaBANG

    7 de marzo de 2018 a las 17:13


  • -1 esto lo hace modificar el comportamiento predeterminado. Ahora tienes un AttributeError sin el contexto del atributo en los argumentos de excepción.

    – Wim

    26 de noviembre de 2018 a las 19:43

class A(object):
    def __init__(self):
        self.a = 42

    def __getattr__(self, attr):
        if attr in ["b", "c"]:
            return 42
        raise AttributeError("%r object has no attribute %r" %
                             (self.__class__.__name__, attr))

>>> a = A()
>>> a.a
42
>>> a.b
42
>>> a.missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: 'A' object has no attribute 'missing'
>>> hasattr(a, "b")
True
>>> hasattr(a, "missing")
False

  • Gracias por esto. Solo quería asegurarme de que tenía el mensaje predeterminado correcto sin buscar en la fuente.

    – Shawn Fumo

    4 de noviembre de 2013 a las 17:48

  • Creo que self.__class__.__name__ debe usarse en lugar de self.__class__ en caso de que la clase anule __repr__

    –Michael Scott Asato Cuthbert

    11/01/2016 a las 20:46

  • Esta es una respuesta mejor que la aceptada, pero sería bueno no tener que volver a escribir ese código y potencialmente perder los cambios anteriores en caso de que se modifique la redacción o se agregue contexto adicional al objeto de excepción en el futuro.

    – Wim

    26 de noviembre de 2018 a las 19:56


Para extender la respuesta de Michael, si desea mantener el comportamiento predeterminado usando __getattr__puedes hacerlo así:

class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42

        # Default behaviour
        return self.__getattribute__(name)

Ahora el mensaje de excepción es más descriptivo:

>>> foo.something
42
>>> foo.error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Foo' object has no attribute 'error'

  • @fed.pavlo ¿estás seguro? tal vez mezclaste __getattr__ y __getattribute__?

    – José Luis

    16 de septiembre de 2016 a las 7:41


  • culpa mía. Perdí la llamada a un método diferente del propio. ;(

    – fed.pavlo

    19 de septiembre de 2016 a las 10:18

  • De hecho, la respuesta de @Michael está incompleta sin esta respuesta

    –Grijesh Chauhan

    12 de abril de 2018 a las 8:10

  • ¿No debería ser return super().__getattribute__(name) por si acaso también tienes un __getattribute__ método en esta clase.

    – pauleohare

    25 de febrero a las 14:06

¿Ha sido útil esta solución?