Как переопределить getattr в Python без нарушения поведения по умолчанию?
Я хочу, чтобы переопределить __getattr__ метод класса, чтобы сделать что-то необычное, но я не хочу нарушать поведение по умолчанию.
Как правильно это сделать?
3 ответов
переопределение __getattr__ должно быть прекрасно -- __getattr__ вызывается только в крайнем случае, т. е. если в экземпляре нет атрибутов, соответствующих имени. Например, если вы получаете доступ к foo.bar, потом __getattr__ будет вызываться только если foo не имеет атрибутаbar. Если атрибут является тем, который вы не хотите обрабатывать, raise AttributeError:
class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError
однако, в отличие от __getattr__, __getattribute__ будет вызываться первым (работает только для новых классов стилей, т. е. тех, которые наследуются от объект.) В этом случае вы можете сохранить поведение по умолчанию следующим образом:
class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)
посмотреть документы Python для большего.
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
чтобы расширить ответ Майкла, если вы хотите сохранить поведение по умолчанию, используя __getattr__, вы можете сделать это вот так: 
class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42
        # Default behaviour
        return self.__getattribute__(name)
Теперь сообщение об исключении более описательно:
>>> 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'
