Вопрос

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

Это было полезно?

Решение

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Интерфейсы 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 у вас нет других вариантов.Либо сделайте шаг "компиляции", который проверяет ваш код, либо динамически проверяйте его во время выполнения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top