Как работает функция, которая вызывается внутри объявления класса?

Иметь этот код:

>>> class Foo:
...     zope.interface.implements(IFoo)
...
...     def __init__(self, x=None):
...         self.x = x
...
...     def bar(self, q, r=None):
...         return q, r, self.x
...
...     def __repr__(self):
...         return "Foo(%s)" % self.x

Очевидно, зов zope.interface.implements каким-то образом изменяет свойства и поведение класса Foo,

Как это произошло? Как мне использовать этот подход в моем коде?

Пример кода является частью модуля zope.interface.

2 ответа

Решение

Подробное "что происходит"

zope.interface.implements() Функция проверяет стек кадров и изменяет locals() пространство имен (питон dict) из класса в строительстве. Все в пределах class Оператор в Python выполняется в этом пространстве имен, и результат формирует тело класса.

Функция добавляет дополнительное значение в пространство имен класса, __implements_advice_data__ с некоторыми данными (интерфейсы, которые вы передали в функцию, и classImplements вызываемый, то, что будет использовано позже.

Затем он добавляет или цепочки в метаклассе для рассматриваемого класса, добавляя (или изменяя ранее существовавший) __metaclass__ введите пространство имен. Это гарантирует, что в будущем, каждый раз, когда вы создаете экземпляр класса, теперь будет вызываться установленный метакласс.

На самом деле, этот метакласс (советник по классам) немного обманчив; он удаляет себя снова после первого создания экземпляра. Он просто вызывает обратный вызов, указанный в __implements_advice_data__ вместе с интерфейсами, которые вы передали в оригинал implements() Функция сразу после удаления __metaclass__ ключ от класса, или заменяет его оригиналом __metaclass__ (который вызывается для создания первого экземпляра класса). Обратный вызов очищает после себя, он удаляет __implements_advice_data__ атрибут из класса.

Короткая версия

В итоге все работы zope.interface.implements() делает это:

  • Добавьте переданные интерфейсы вместе с обратным вызовом к специальному атрибуту в классе (__implements_advice_data__).
  • Гарантирует, что обратный вызов вызывается при первом создании экземпляра с использованием специального метакласса.

В конце концов, это моральный эквивалент определения ваших интерфейсов следующим образом:

class Foo:
    def __init__(self, x=None):
        self.x = x

    def bar(self, q, r=None):
        return q, r, self.x

    def __repr__(self):
        return "Foo(%s)" % self.x

zope.interface.classImplements(Foo, IFoo)

за исключением того, что последний вызов откладывается до тех пор, пока вы сначала не создадите экземпляр Foo,

Но зачем идти на такие меры?

когда zope.interface был впервые разработан, Python еще не имел декораторов классов.

zope.interface.classImplements() должен быть вызван отдельно, как функция, после того, как класс был создан, и zope.interface.implements() Вызов в теле класса обеспечивает лучшую документацию о том, какие интерфейсы реализует класс. Вы можете поместить его прямо вверху объявления класса, и каждый сможет увидеть эту важную информацию, глядя на класс. Иметь classImplements() Вызов, расположенный после объявления класса, не так хорошо виден и ясен, а для длинных определений классов его легко будет вообще пропустить.

PEP 3129, наконец, добавил к языку декораторы классов, и они были добавлены в python 2.6 и 3.0; zope.interface Впервые был разработан еще во времена Python 2.3 (IIRC).

Теперь, когда у нас есть декораторы классов, zope.interface.implements() устарела, и вы можете использовать zope.interface.implementer вместо декоратора класса:

@zope.interface.implementer(IFoo)
class Foo:
    def __init__(self, x=None):
        self.x = x

    def bar(self, q, r=None):
        return q, r, self.x

    def __repr__(self):
        return "Foo(%s)" % self.x

Прочитайте источник, Люк:

http://svn.zope.org/zope.interface/trunk/src/zope/interface/declarations.py?rev=124816&view=markup

def _implements(name, interfaces, classImplements):
    frame = sys._getframe(2)
    locals = frame.f_locals

    # Try to make sure we were called from a class def. In 2.2.0 we can't
    # check for __module__ since it doesn't seem to be added to the locals
    # until later on.
    if (locals is frame.f_globals) or (
        ('__module__' not in locals) and sys.version_info[:3] > (2, 2, 0)):
        raise TypeError(name+" can be used only from a class definition.")

    if '__implements_advice_data__' in locals:
        raise TypeError(name+" can be used only once in a class definition.")

    locals['__implements_advice_data__'] = interfaces, classImplements
    addClassAdvisor(_implements_advice, depth=3)
Другие вопросы по тегам