2010-03-08 13 views
152

Quiero reemplazar el método __getattr__ en una clase para hacer algo elegante pero no quiero romper el comportamiento predeterminado.¿Cómo anulo __getattr__ en Python sin romper el comportamiento predeterminado?

¿Cuál es la forma correcta de hacerlo?

+20

"Romper", pregunta, no "cambiar". Eso es suficientemente claro: los atributos "sofisticados" no deberían interferir con los atributos integrados y deberían comportarse tanto como les sea posible. La respuesta de Michael es correcta y útil. – olooney

Respuesta

228

Reemplazar __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 tiene acceso a foo.bar, solo se llamará a __getattr__ si foo no tiene ningún atributo llamado bar. Si el atributo es uno que no desea manejar, subir 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 (sólo funciona para las nuevas clases de estilos es decir, aquellos que heredan de objetos). En este caso, puede conservar el comportamiento por defecto de este modo:

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

Ver the Python docs for more.

+0

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

+7

Genial, a Python no parece gustarle llamar a super en '__getattr__' - ¿Alguna idea de qué hacer? (El objeto 'AttributeError:' super 'no tiene ningún atributo' __getattr __ '') – gatoatigrado

+1

Sin ver su código es difícil de decir, pero parece que ninguna de sus superclases define __getattr__. –

26
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__, attr)) 
    # exception text copied from Python2.6 

>>> 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 
+0

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

+2

Creo que 'self .__ class __.__ name__' debe usarse en lugar de' self .__ class__' en caso de que la clase anule '__repr__' –

2

Extender Michael respuesta, si se quiere mantener el comportamiento por defecto usando __getattr__, puede hacerlo de esta manera:

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' 
+1

@ fed.pavlo ¿estás seguro? ¿Tal vez mezcló '__getattr__' y' __getattribute__'? –

+0

mi mal. Me perdí la llamada a un método diferente de mí mismo. ; ( –

Cuestiones relacionadas