Назначение интерфейсов Zope?

Я начал использовать интерфейсы Zope в своем коде, и на данный момент они на самом деле являются только документацией. Я использую их, чтобы указать, какими атрибутами должен обладать класс, явным образом реализовать их в соответствующих классах и явно проверить их там, где я ожидаю. Это нормально, но я бы хотел, чтобы они делали больше, если это возможно, например, фактически проверяли, что класс реализовал интерфейс, а не просто проверяли, что я сказал, что класс реализует интерфейс. Я прочитал вики zope пару раз, но все еще не вижу гораздо большего использования интерфейсов, чем то, что я сейчас делаю. Итак, мой вопрос в том, для чего еще вы можете использовать эти интерфейсы, и как вы можете использовать их для большего.

4 ответа

Решение

Вы можете на самом деле проверить, реализует ли ваш объект или класс ваш интерфейс. Для этого вы можете использовать verify модуль (вы обычно используете его в своих тестах):

>>> from zope.interface import Interface, Attribute, implements
>>> class IFoo(Interface):
...     x = Attribute("The X attribute")
...     y = Attribute("The Y attribute")

>>> class Foo(object):
...     implements(IFoo)
...     x = 1
...     def __init__(self):
...         self.y = 2

>>> from zope.interface.verify import verifyObject
>>> verifyObject(IFoo, Foo())
True

>>> from zope.interface.verify import verifyClass
>>> verifyClass(IFoo, Foo)
True

Интерфейсы также могут быть использованы для установки и тестирования инвариантов. Вы можете найти больше информации здесь:

http://www.muthukadan.net/docs/zca.html

Там, где я работаю, мы используем интерфейсы, чтобы мы могли использовать ZCA или Zope Component Architecture, которая представляет собой целостную структуру для создания компонентов, которые можно заменять и подключать с использованием Interfaces. Мы используем ZCA, так что мы можем справиться со всеми способами индивидуальной настройки для каждого клиента без необходимости разветвлять наше программное обеспечение или иметь все биты для каждого клиента, портящие основное дерево. К сожалению, вики Zope часто бывают довольно неполными. Есть хорошее, но краткое объяснение большинства функций ZCA на странице Pypi ZCA.

Я не пользуюсь Interfaces для чего-либо вроде проверки, что класс реализует все методы для данного Interface, Теоретически, это может быть полезно, когда вы добавляете в интерфейс другой метод, чтобы убедиться, что вы запомнили добавление нового метода ко всем классам, которые реализуют интерфейс. Лично я настоятельно предпочитаю создавать новые Interface за изменение старого. Модификация старого Interfaces Обычно это очень плохая идея, когда они находятся в яйцах, выпущенных для pypi или для остальной части вашей организации.

Краткое замечание по терминологии: классы реализуют Interfaces, и объекты (экземпляры классов) предоставляют Interfaces. Если вы хотите проверить Interfaceвы бы либо написали ISomething.implementedBy(SomeClass) или же ISomething.providedBy(some_object),

Итак, до примеров, где ZCA полезен. Давайте представим, что мы пишем блог, используя ZCA, чтобы сделать его модульным. У нас будет BlogPost объект для каждого поста, который обеспечит IBlogPost интерфейс, все определено в нашем удобном денди my.blog яйцо. Мы также сохраним конфигурацию блога в BlogConfiguration объекты, которые обеспечивают IBlogConfiguration, Используя это в качестве отправной точки, мы можем реализовать новые функции без необходимости касаться my.blog совсем.

Ниже приведен список примеров того, что мы можем сделать, используя ZCA, без необходимости изменять базу my.blog яйцо. Я или мои коллеги сделали все эти вещи (и сочли их полезными) в реальных проектах для клиентов, хотя в то время мы не создавали блоги.:) Некоторые из вариантов использования здесь могут быть лучше решены с помощью других средств, таких как файл CSS для печати.

  1. Добавление дополнительных просмотров (BrowserViews, обычно регистрируется в ZCML с browser:page директива) ко всем объектам, которые обеспечивают IBlogPost, Я мог бы сделать my.blog.printable яйцо. Это яйцо будет регистрировать BrowserView с именем print за IBlogPost, который отображает сообщение в блоге через шаблон страницы Zope, предназначенный для создания HTML, который хорошо печатается. Тот BrowserView затем появится в URL /path/to/blogpost/@@print,

  2. Механизм подписки на события в Zope. Скажем, я хочу публиковать RSS-каналы и создавать их заранее, а не по запросу. Я мог бы создать my.blog.rss яйцо. В этом яйце я бы зарегистрировал подписчика на события, которые предоставляют IObjectModified (zope.lifecycleevent.interfaces.IObjectModified), на объектах, которые обеспечивают IBlogPost, Этот подписчик будет вызываться каждый раз, когда атрибут меняется на что-либо IBlogPost, и я мог бы использовать его для обновления всех RSS-каналов, в которых должно появляться сообщение в блоге.

    В этом случае может быть лучше иметь IBlogPostModified событие, которое отправляется в конце каждого из BrowserViewкоторые изменяют сообщения в блоге, так как IObjectModified отправляется один раз при каждом изменении атрибута - что может быть слишком часто для производительности.

  3. Адаптеры. Адаптеры эффективно "забрасывают" из одного интерфейса в другой. Для фанатов языка программирования: Адаптеры Zope реализуют "открытую" множественную диспетчеризацию в Python (под "открытым" я подразумеваю "вы можете добавить больше случаев из любого яйца"), причем более специфические совпадения интерфейса имеют приоритет над менее специфичными совпадениями (Interface классы могут быть подклассами друг друга, и это делает именно то, на что вы надеетесь.)

    Адаптеры от одного Interface может быть вызван с очень хорошим синтаксисом, ISomething(object_to_adapt)или можно посмотреть с помощью функции zope.component.getAdapter, Адаптеры из нескольких Interfaceнужно искать через функцию zope.component.getMultiAdapter, что немного менее красиво.

    Вы можете иметь более одного адаптера для данного набора Interfaces, дифференцированные строкой name что вы предоставляете при регистрации адаптера. Имя по умолчанию "", Например, BrowserViewФактически, это адаптеры, которые адаптируются от интерфейса, на котором они зарегистрированы, и интерфейса, который реализует класс HTTPRequest. Вы также можете посмотреть все адаптеры, которые зарегистрированы из одной последовательности Interfaceс другой Interface, с помощью zope.component.getAdapters( (IAdaptFrom,), IAdaptTo ), который возвращает последовательность пар (имя, адаптер). Это может быть использовано как очень хороший способ предоставления хуков для подключаемых модулей.

    Скажем, я хотел сохранить все посты и настройки моего блога в виде одного большого файла XML. Я создаю my.blog.xmldump яйцо, которое определяет IXMLSegmentи регистрирует адаптер из IBlogPost в IXMLSegment и адаптер от IBlogConfiguration в IXMLSegment, Теперь я могу вызвать тот адаптер, который подходит для какого-либо объекта, который я хочу сериализовать, написав IXMLSegment(object_to_serialize),

    Я мог бы даже добавить больше адаптеров от разных других вещей к IXMLSegment из яиц, кроме my.blog.xmldump, ZCML имеет функцию, позволяющую запускать определенную директиву тогда и только тогда, когда установлено какое-либо яйцо. Я мог бы использовать это, чтобы иметь my.blog.rss зарегистрировать адаптер от IRSSFeed в IXMLSegment тогда и только тогда my.blog.xmldump случается быть установленным, не делая my.blog.rss зависит от my.blog.xmldump,

  4. Viewletони как маленькие BrowserViewЭто означает, что вы можете подписаться на определенное место на странице. Я не могу вспомнить все детали прямо сейчас, но они очень хороши для таких вещей, как плагины, которые вы хотите разместить на боковой панели.

    Я не могу вспомнить, являются ли они частью базовой Zope или Plone. Я бы рекомендовал не использовать Plone, если проблема, которую вы пытаетесь решить, на самом деле не нуждается в реальной CMS, так как это большая и сложная часть программного обеспечения, и она имеет тенденцию быть довольно медленной.

    Вам не обязательно на самом деле нужно Viewletв любом случае, так как BrowserViews могут вызывать друг друга, используя 'object/@@some_browser_view' в выражении TAL, или используя queryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' )но они довольно милые

  5. маркер Interfaces. Маркер Interface является Interface это не обеспечивает никаких методов и никаких атрибутов. Вы можете добавить маркер Interface любой объект во время выполнения, используя ISomething.alsoProvidedBy, Это позволяет вам, например, изменять, какие адаптеры будут использоваться на конкретном объекте, а какие BrowserViews будет определено на нем.

Я прошу прощения за то, что я не вдавался в достаточно подробности, чтобы иметь возможность сразу реализовать каждый из этих примеров, но каждый занял примерно пост в блоге.

Интерфейсы Zope могут предоставить полезный способ отделить два фрагмента кода, которые не должны зависеть друг от друга.

Скажем, у нас есть компонент, который знает, как напечатать приветствие в модуле a.py:

>>> class Greeter(object):
...     def greet(self):
...         print 'Hello'

И некоторый код, который должен напечатать приветствие в модуле b.py:

>>> Greeter().greet()
'Hello'

Такое расположение затрудняет обмен кода, который обрабатывает приветствие, не затрагивая b.py (который может распространяться в отдельном пакете). Вместо этого мы могли бы ввести третий модуль c.py, который определяет интерфейс IGreeter:

>>> from zope.interface import Interface
>>> class IGreeter(Interface):
...     def greet():
...         """ Gives a greeting. """

Теперь мы можем использовать это для разделения a.py и b.py. Вместо того, чтобы создавать экземпляр класса Greeter, b.py теперь запросит утилиту, предоставляющую интерфейс IGreeter. И a.py объявит, что класс Greeter реализует этот интерфейс:

(a.py)
>>> from zope.interface import implementer
>>> from zope.component import provideUtility
>>> from c import IGreeter

>>> @implementer(IGreeter)
... class Greeter(object):
...     def greet(self):
...         print 'Hello'
>>> provideUtility(Greeter(), IGreeter)

(b.py)
>>> from zope.component import getUtility
>>> from c import IGreeter

>>> greeter = getUtility(IGreeter)
>>> greeter.greet()
'Hello'

Я никогда не использовал интерфейсы Zope, но вы можете подумать о написании метакласса, который при инициализации проверяет членов класса на соответствие интерфейсу и вызывает исключение времени выполнения, если метод не реализован.

С Python у вас нет других вариантов. Либо выполните шаг "компиляции", который проверяет ваш код, либо динамически проверяйте его во время выполнения.

Другие вопросы по тегам