Назначение интерфейсов Zope? - PullRequest
33 голосов
/ 26 марта 2010

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

Ответы [ 4 ]

51 голосов
/ 15 мая 2010

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

есть краткое объяснение большинства функций ZCA.

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

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

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

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

  1. Добавление дополнительных представлений (BrowserView s, обычно регистрируемых в ZCML с директивой browser:page) ко всем объектам, которые предоставляют IBlogPost. Я мог бы сделать my.blog.printable яйцо. Это яйцо будет регистрировать BrowserView с именем print для IBlogPost, который отображает сообщение в блоге с помощью Zope Page Template , предназначенного для создания HTML, который хорошо печатается. Затем BrowserView появится по адресу /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, которая немного менее привлекательна.

    Вы можете иметь более одного адаптера для данного набора Interface с, различаясь строкой name, которую вы предоставляете при регистрации адаптера. Имя по умолчанию "". Например, BrowserView на самом деле являются адаптерами, которые адаптируются от интерфейса, на котором они зарегистрированы, и интерфейса, который реализует класс HTTPRequest. Вы также можете найти всех адаптеров, которые зарегистрированы из одной последовательности Interface s в другую 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 s похожи на маленькие BrowserView s, которые можно «подписать» на определенное место на странице. Сейчас я не могу вспомнить все детали, но они очень хороши для таких вещей, как плагины, которые вы хотите разместить на боковой панели.

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

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

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

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

23 голосов
/ 26 марта 2010

Вы можете проверить, реализует ли ваш объект или класс ваш интерфейс. Для этого вы можете использовать модуль 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#interfaces

19 голосов
/ 15 апреля 2010

Интерфейсы 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'
2 голосов
/ 26 марта 2010

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

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

...