Назначение интерфейсов Zope?
-
22-09-2019 - |
Вопрос
Я начал использовать интерфейсы 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
Интерфейсы также могут использоваться для настройки и тестирования инвариантов.Вы можете найти более подробную информацию здесь:
Другие советы
Там, где я работаю, мы используем интерфейсы, чтобы мы могли использовать ZCA, или Архитектура компонентов Zope, который представляет собой целую платформу для создания компонентов , которые можно заменять и подключать с помощью Interface
s.Мы используем ZCA, чтобы иметь возможность справляться со всевозможными настройками для каждого клиента без необходимости разветвлять наше программное обеспечение или использовать все многочисленные биты для каждого клиента, портящие основное дерево.К сожалению, Zope wiki часто бывает довольно неполной.На его сайте есть хорошее, но краткое объяснение большинства функций ZCA. Страница pypi ZCA в.
Я не использую Interface
s для чего-либо вроде проверки того, что класс реализует все методы для данного 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
ы, обычно зарегистрированные в ZCML с помощьюbrowser:page
директива) ко всем объектам, которые обеспечиваютIBlogPost
.Я мог бы сделатьmy.blog.printable
яйцо.Это яйцо зарегистрировало бы просмотр браузера, называемыйprint
дляIBlogPost
, который отображает сообщение в блоге через Шаблон страницы Zope предназначен для создания HTML-кода, который красиво печатается.ЭтоBrowserView
затем появится по URL-адресу/path/to/blogpost/@@print
.Механизм подписки на события в Zope.Допустим, я хочу публиковать RSS-каналы, и я хочу генерировать их заранее, а не по запросу.Я мог бы создать
my.blog.rss
яйцо.В этом яйце я бы зарегистрировал подписчика на события, которые обеспечивают Iobjectмодифицированный (zope.lifecycleevent.interfaces.IObjectModified
), на объектах, которые обеспечиваютIBlogPost
.Этот подписчик вызывался бы get каждый раз, когда атрибут изменялся для чего-либо, предоставляющегоIBlogPost
, и я мог бы использовать его для обновления всех RSS-каналов, в которых должна появиться запись в блоге.В этом случае, возможно, было бы лучше иметь
IBlogPostModified
событие, которое отправляется в конце каждого изBrowserView
которые изменяют записи в блоге, посколькуIObjectModified
отправляется один раз при каждом изменении атрибута, что может быть слишком часто для повышения производительности.Адаптеры.Адаптеры - это эффективные "слепки" с одного интерфейса на другой.Для любителей языков программирования:Адаптеры Zope реализуют "открытую" множественную отправку на Python (под "открытым" я подразумеваю "вы можете добавить больше обращений из любого egg"), при этом совпадения с более конкретным интерфейсом имеют приоритет над менее конкретными совпадениями (
Interface
классы могут быть подклассами друг друга, и это делает именно то, на что вы надеялись.)Адаптеры из одного
Interface
может быть вызван с очень приятным синтаксисом,ISomething(object_to_adapt)
, или может быть просмотрен с помощью функцииzope.component.getAdapter
.Адаптеры из несколькихInterface
ы должны быть просмотрены с помощью функцииzope.component.getMultiAdapter
, что немного менее красиво.У вас может быть более одного адаптера для данного набора
Interface
s, дифференцируемый строкойname
которые вы предоставляете при регистрации адаптера.Имя по умолчанию равно""
.Например,BrowserView
s на самом деле являются адаптерами, которые адаптируются к интерфейсу, на котором они зарегистрированы, и интерфейсу, реализуемому классом 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 есть функция, с помощью которой он может запускать определенную директиву тогда и только тогда, когда установлено какое-либо egg.Я мог бы использовать это, чтобы иметьmy.blog.rss
зарегистрируйте адаптер изIRSSFeed
ДляIXMLSegment
еслиmy.blog.xmldump
случается быть установленным, без внесенияmy.blog.rss
зависеть отmy.blog.xmldump
.Viewlet
s похожи на маленькихBrowserView
это означает, что вы можете "подписаться" на определенное место внутри страницы.Я не могу вспомнить все детали прямо сейчас, но они очень хороши для таких вещей, как плагины, которые вы хотите разместить на боковой панели.Я не могу сразу вспомнить, являются ли они частью base 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
на нем будут определены 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 у вас нет других вариантов.Либо сделайте шаг "компиляции", который проверяет ваш код, либо динамически проверяйте его во время выполнения.