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

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

34
задан Martijn Pieters 23 August 2012 в 17:06
поделиться

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#interfaces

23
ответ дан 27 November 2019 в 16:11
поделиться

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

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

2
ответ дан 27 November 2019 в 16:11
поделиться

Интерфейсы 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'
19
ответ дан 27 November 2019 в 16:11
поделиться

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

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

Небольшое замечание по терминологии: классы реализуют интерфейс и объекты (экземпляры классов) предоставляют интерфейс и. Если вы хотите проверить наличие интерфейса, вы либо напишите 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 Page Template, предназначенный для создания 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, что несколько менее красиво.

    Вы можете иметь более одного адаптера для данного набора интерфейсов, различающихся строкой name, которую вы указываете при регистрации адаптера. По умолчанию имя имеет значение """. Например, BrowserView на самом деле являются адаптерами, которые адаптируются из интерфейса, в котором они зарегистрированы, и интерфейса, который реализует класс HTTPRequest. Вы также можете найти все адаптеры, которые зарегистрированы от одной последовательности интерфейсовк другой интерфейсам, используя 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ы, поскольку BrowserViewы могут вызывать друг друга, либо используя 'object/@@some_browser_view' в выражении TAL, либо используя queryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' ), но они довольно хороши, независимо от этого.

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

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

52
ответ дан 27 November 2019 в 16:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: