Squeak.ru - шаблоны программирования

Как я могу перехватывать вызовы магических методов Python в новых классах стиля?

Я пытаюсь перехватить вызовы магических методов двойного подчеркивания Python в новых классах стиля. Это тривиальный пример, но он показывает намерение:

class ShowMeList(object):
    def __init__(self, it):
        self._data = list(it)

    def __getattr__(self, name):
        attr = object.__getattribute__(self._data, name)
        if callable(attr):
            def wrapper(*a, **kw):
                print "before the call"
                result = attr(*a, **kw)
                print "after the call"
                return result
            return wrapper
        return attr

Если я использую этот прокси-объект вокруг списка, я получаю ожидаемое поведение для неволшебных методов, но моя функция-оболочка никогда не вызывается для магических методов.

>>> l = ShowMeList(range(8))

>>> l #call to __repr__
<__main__.ShowMeList object at 0x9640eac>

>>> l.append(9)
before the call
after the call

>> len(l._data)
9

Если я не наследую от объекта (первая строка class ShowMeList:), все работает как положено:

>>> l = ShowMeList(range(8))

>>> l #call to __repr__
before the call
after the call
[0, 1, 2, 3, 4, 5, 6, 7]

>>> l.append(9)
before the call
after the call

>> len(l._data)
9

Как выполнить этот перехват с новыми классами стиля?

29.01.2012

  • Что вы действительно пытаетесь сделать, перехватывая методы двойного подчеркивания? Или это просто для любопытства? 30.01.2012
  • Я всегда храню здесь список всех магических методов: github.com/niccokunzmann/wwp /blob/ (сгенерировано, поэтому работает для python 2 и 3) 19.02.2013
  • На самом деле кажется, что вы хотите перехватить вызовы магических методов экземпляров нового класса стиля - однако, ИМХО, это все еще хороший вопрос. 13.11.2013

Ответы:


1

Из соображений производительности Python всегда ищет магические методы в классе (и родительских классах) __dict__ и не использует обычный механизм поиска атрибутов. Обходной путь — использовать метакласс для автоматического добавления прокси для магических методов во время создания класса; Я использовал эту технику, чтобы избежать написания шаблонных методов сквозного вызова, например, для классов-оболочек.

class Wrapper(object):
    """Wrapper class that provides proxy access to an instance of some
       internal instance."""

    __wraps__  = None
    __ignore__ = "class mro new init setattr getattr getattribute"

    def __init__(self, obj):
        if self.__wraps__ is None:
            raise TypeError("base class Wrapper may not be instantiated")
        elif isinstance(obj, self.__wraps__):
            self._obj = obj
        else:
            raise ValueError("wrapped object must be of %s" % self.__wraps__)

    # provide proxy access to regular attributes of wrapped object
    def __getattr__(self, name):
        return getattr(self._obj, name)

    # create proxies for wrapped object's double-underscore attributes
    class __metaclass__(type):
        def __init__(cls, name, bases, dct):

            def make_proxy(name):
                def proxy(self, *args):
                    return getattr(self._obj, name)
                return proxy

            type.__init__(cls, name, bases, dct)
            if cls.__wraps__:
                ignore = set("__%s__" % n for n in cls.__ignore__.split())
                for name in dir(cls.__wraps__):
                    if name.startswith("__"):
                        if name not in ignore and name not in dct:
                            setattr(cls, name, property(make_proxy(name)))

Применение:

class DictWrapper(Wrapper):
    __wraps__ = dict

wrapped_dict = DictWrapper(dict(a=1, b=2, c=3))

# make sure it worked....
assert "b" in wrapped_dict                        # __contains__
assert wrapped_dict == dict(a=1, b=2, c=3)        # __eq__
assert "'a': 1" in str(wrapped_dict)              # __str__
assert wrapped_dict.__doc__.startswith("dict()")  # __doc__
30.01.2012
  • @kindall Я удалил то, что выглядело как потерянная строка кода, но вы можете перепроверить, не потерял ли ваш пример предполагаемую функциональность. 31.10.2013
  • Видите ли вы какие-либо проблемы с выполнением ignore.update(dct) перед циклическим выполнением dir() и объединением двух последних операторов if в один? Мне кажется чище, но, возможно, есть непредвиденные последствия, которых я по своей неопытности не предвидел. 31.10.2013
  • В Python 3.8 я обнаружил, что этот пример на самом деле не работает как есть. 14.01.2020

  • 2

    Использование __getattr__ и __getattribute__ — это последние ресурсы класса, которые реагируют на получение атрибута.

    Рассмотрим следующее:

    >>> class C:
        x = 1
        def __init__(self):
            self.y = 2
        def __getattr__(self, attr):
            print(attr)
    
    >>> c = C()
    >>> c.x
    1
    >>> c.y
    2
    >>> c.z
    z
    

    Метод __getattr__ вызывается только тогда, когда больше ничего не работает (он не будет работать с операторами, и вы можете прочитать об этом здесь).

    В вашем примере __repr__ и многие другие магические методы уже определены в классе object.

    Одна вещь может быть сделана, подумалось, и это определить эти магические методы и сделать так, чтобы затем вызвать метод __getattr__. Проверьте этот другой вопрос от меня и его ответы (ссылка), чтобы увидеть, как это делает код .

    29.01.2012

    3

    Что касается ответов на Асимметричное поведение для __getattr__, классы нового и старого стиля (см. также документацию Python), изменение доступа к «магическим» методам с помощью __getattr__ или __getattribute__ просто невозможно с классами нового стиля. Это ограничение делает интерпретатор намного быстрее.

    29.01.2012
  • Спасибо. Я нашел этот ответ полезным для конкретизации вашего ответа. 30.01.2012

  • 4

    Вырезать и скопировать из документации:

    Для классов старого стиля специальные методы всегда ищутся точно так же, как и любой другой метод или атрибут.

    Для классов нового стиля неявные вызовы специальных методов гарантированно работают правильно только в том случае, если они определены для типа объекта, а не в словаре экземпляра объекта.

    29.01.2012
  • Этот ответ на 100% правильный, но ничего не объясняет и совершенно бесполезен. Извиняюсь. 30.01.2012
  • Старый анекдот Bell Labs: летчик, потерявшийся в густом тумане, видит офисное здание, кричит, где я?, получает ответ в самолете и приземляется в... Аллентауне. Как он узнал? Ответ 100% правильный... 20.04.2012
  • Новые материалы

    Угловая структура архитектуры
    Обратите внимание, что эта статья устарела, я решил создать новую с лучшей структурой и с учетом автономных компонентов: https://medium.com/@marekpanti/angular-standalone-architecture-b645edd0d54a..

    «Данные, которые большинство людей используют для обучения своих моделей искусственного интеллекта, поставляются со встроенным…
    Первоначально опубликовано HalkTalks: https://hacktown.com.br/blog/blog/os-dados-que-a-maioria-das-pessoas-usa-para-treinar-seus-modelos-de-inteligencia-artificial- ja-vem-com-um-vies-embutido/..

    Сильный ИИ против слабого ИИ: различия парадигм искусственного интеллекта
    В последние годы изучению и развитию искусственного интеллекта (ИИ) уделяется большое внимание и прогресс. Сильный ИИ и Слабый ИИ — две основные парадигмы в области искусственного интеллекта...

    Правильный способ добавить Firebase в ваш проект React с помощью React Hooks
    React + Firebase - это мощная комбинация для быстрого и безопасного создания приложений, от проверки концепции до массового производства. Раньше (знаете, несколько месяцев назад) добавление..

    Создайте API с помощью Python FastAPI
    Создание API с помощью Python становится очень простым при использовании пакета FastAPI. После установки и импорта вы можете создать приложение FastAPI и указать несколько конечных точек. Каждой..

    Веселье с прокси-сервером JavaScript
    Прокси-серверы JavaScript — это чистый сахар, если вы хотите создать некоторую общую логику в своих приложениях, чтобы облегчить себе жизнь. Вот один пример: Связь клиент-сервер Мы..

    Получить бесплатный хостинг для разработчиков | Разместите свой сайт за несколько шагов 🔥
    Статические веб-сайты — это веб-страницы с фиксированным содержанием и его постоянным содержанием. Но теперь статические сайты также обрабатывают динамические данные с помощью API и запросов...