Як я можу замінити __getattr__ в Python, не порушуючи поведінку за замовчуванням?


185

Я хочу змінити __getattr__метод у класі, щоб зробити щось фантазійне, але я не хочу порушувати поведінку за замовчуванням.

Який правильний спосіб це зробити?


20
"Перерва", - запитує він, а не "міняється". Це достатньо зрозуміло: атрибути "фантазії" не повинні перешкоджати вбудованим атрибутам і повинні поводитись настільки, наскільки вони схожі. Відповідь Майкла є і правильною, і корисною.
olooney

Відповіді:


268

Переосмислення __getattr__має бути нормальним - __getattr__називається лише в крайньому випадку, тобто якщо в екземплярі немає атрибутів, які б відповідали імені. Наприклад, якщо ви маєте доступ foo.bar, __getattr__виклик буде лише у тому випадку, якщо fooатрибут не викликається bar. Якщо атрибут один з яким ви не хочете обробляти, підніміть 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 .


Ба, у вашій редакції є те саме, що я показав у своїй відповіді +1.

12
Класно, Python, схоже, не любить дзвонити супер __getattr__- це ідеї, що робити? ( AttributeError: 'super' object has no attribute '__getattr__')
gatoatigrado

1
Не бачачи свого коду, важко сказати, але це виглядає так, що жоден із ваших суперкласів не визначає getattr .
Колін vH

Це працює з hasattr ще й тому, що: "Це реалізується за допомогою виклику getattr (об'єкт, ім'я) та побачення, чи викликає він виняток чи ні". docs.python.org/2/library/functions.html#hasattr
ShaBANG

1
-1 Це дійсно змінити поведінку по замовчуванню. Тепер у вас є AttributeErrorконтекст атрибута без контексту в аргументах виключень.
wim

34
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

Дякую за це Просто хотів переконатися, що я мав правильне повідомлення за замовчуванням, не копаючись у джерелі.
ShawnFumo

4
Я думаю, що це self.__class__.__name__слід використовувати замість того, self.__class__щоб клас переосмислив__repr__
Майкл Скотт Катберт

3
Це краще, ніж прийнята відповідь, але було б непогано не переписувати цей код і потенційно пропускати зміни вгору за течією, якщо формулювання буде змінено або в майбутньому буде доданий додатковий контекст до об'єкта винятку.
Вім

13

Щоб розширити відповідь Майкла, якщо ви хочете підтримувати поведінку за замовчуванням за допомогою __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'

2
@ fed.pavlo Ви впевнені? Може, ви змішали __getattr__і __getattribute__?
Хосе Луїс

моє ліжко. Я пропустив дзвінок на інший метод від себе. ; (
fed.pavlo

2
Справді @ відповідь Майкла неповна без цієї відповіді
Гріеш Чаухан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.