Подтвердить что ты не робот

Цель интерфейсов Zope?

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

    У вас может быть несколько адаптеров для заданного набора Interface s, отличающихся строкой 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, если iff my.blog.xmldump будет установлен, не делая my.blog.rss зависеть от my.blog.xmldump.

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

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

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

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

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

Ответ 3

Интерфейсы 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'

Ответ 4

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

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