python - ¿Propósito de las interfaces Zope?
zope.interface (4)
Donde trabajo, utilizamos interfaces para que podamos usar ZCA, o Zope Component Architecture , que es un marco completo para hacer componentes que son intercambiables y conectables usando Interface
s. Usamos ZCA para que podamos hacer frente a todo tipo de personalizaciones por cliente sin tener que forzar nuestro software o tener todos los bits por cliente arruinando el árbol principal. La wiki de Zope a menudo es bastante incompleta, desafortunadamente. Hay una buena explicación de la mayoría de las características de ZCA en la página pypi de su ZCA .
No utilizo Interface
s para nada como verificar que una clase implemente todos los métodos para una Interface
dada. En teoría, eso podría ser útil cuando agrega otro método a una interfaz, para verificar que haya recordado agregar el nuevo método a todas las clases que implementan la interfaz. Personalmente, prefiero crear una nueva Interface
modificar una anterior. La modificación de Interfaces
antiguas suele ser una muy mala idea una vez que están en los huevos que se han lanzado a pypi o al resto de su organización.
Una nota rápida sobre terminología: las clases implementan Interface
s, y los objetos (instancias de clases) proporcionan Interface
s. Si desea buscar una Interface
, puede escribir ISomething.implementedBy(SomeClass)
o ISomething.providedBy(some_object)
.
Entonces, hasta ejemplos de donde ZCA es útil. Imaginemos que estamos escribiendo un blog, usando ZCA para hacerlo modular. Tendremos un objeto BlogPost
para cada publicación, que proporcionará una interfaz IBlogPost
, todo definido en nuestro práctico huevo my.blog
. También almacenaremos la configuración del blog en los objetos de BlogConfiguration
que proporcionan IBlogConfiguration
. Usando esto como punto de partida, podemos implementar nuevas funciones sin tener que tocar my.blog
.
La siguiente es una lista de ejemplos de cosas que podemos hacer al usar ZCA, sin tener que alterar la base my.blog
egg. Yo o mis compañeros de trabajo hemos hecho todas estas cosas (y las hemos encontrado útiles) en proyectos reales para clientes, aunque no estábamos implementando blogs en ese momento. :) Algunos de los casos de uso aquí podrían resolverse mejor por otros medios, como un archivo CSS de impresión.
Agregar vistas adicionales (
BrowserView
s, generalmente registradas en ZCML con la directivabrowser:page
) a todos los objetos que brindanIBlogPost
. Podría hacer un huevomy.blog.printable
. Ese huevo registraría un BrowserView llamadoprint
paraIBlogPost
, que presenta la publicación del blog a través de una plantilla de página Zope diseñada para producir HTML que se imprime muy bien. EseBrowserView
aparecería en URL/path/to/blogpost/@@print
.El mecanismo de suscripción de eventos en Zope. Digamos que quiero publicar feeds RSS, y quiero generarlos con anticipación en lugar de a pedido. Podría crear un huevo
my.blog.rss
. En ese huevo, registraría un suscriptor para eventos que proporcionan IObjectModified (zope.lifecycleevent.interfaces.IObjectModified
), en objetos que proporcionanIBlogPost
. A ese suscriptor se le llamaría cada vez que cambiara un atributo en cualquier cosa que proporcionaraIBlogPost
, y podría usarlo para actualizar todos los canales RSS en los que debería aparecer la publicación del blog.En este caso, podría ser mejor tener un evento
IBlogPostModified
que se envíe al final de cadaBrowserView
que modifique entradas de blog, ya queIObjectModified
se envía una vez en cada cambio de atributo, lo que puede ocurrir con demasiada frecuencia por el rendimiento.Adaptadores Los adaptadores son efectivamente "moldes" de una interfaz a otra. Para geeks de lenguaje de programación: los adaptadores Zope implementan el despacho múltiple "abierto" en Python (con "abierto" me refiero a "se pueden agregar más casos de cualquier huevo"), con coincidencias de interfaz más específicas que tienen prioridad sobre las menos específicas (
Interface
las clases pueden ser subclases entre sí, y esto hace exactamente lo que uno esperaría que hiciera).Los adaptadores de una
Interface
se pueden llamar con una sintaxis muy buena,ISomething(object_to_adapt)
, o se pueden buscar a través de la funciónzope.component.getAdapter
. Los adaptadores de múltiplesInterface
debenzope.component.getMultiAdapter
través de la funciónzope.component.getMultiAdapter
, que es ligeramente menos bonita.Puede tener más de un adaptador para un conjunto determinado de
Interface
, diferenciado por unname
cadena que proporciona al registrar el adaptador. El nombre predeterminado es""
. Por ejemplo,BrowserView
s son en realidad adaptadores que se adaptan desde la interfaz en la que están registrados y una interfaz que implementa la clase HTTPRequest. También puede buscar todos los adaptadores que están registrados desde una secuencia deInterface
s a otraInterface
, utilizandozope.component.getAdapters( (IAdaptFrom,), IAdaptTo )
, que devuelve una secuencia de pares (nombre, adaptador). Esto se puede utilizar como una muy buena manera de proporcionar ganchos para que los complementos se adhieran.Digamos que quería guardar todas las publicaciones y la configuración de mi blog como un gran archivo XML. Creo un huevo
my.blog.xmldump
que define unIXMLSegment
y registra un adaptador deIBlogPost
aIXMLSegment
y un adaptador deIBlogConfiguration
aIXMLSegment
. Ahora puedo llamar a cualquier adaptador que sea apropiado para algún objeto que quiera serializar escribiendoIXMLSegment(object_to_serialize)
.Incluso podría agregar más adaptadores de otras cosas a
IXMLSegment
de eggs que no seanmy.blog.xmldump
. ZCML tiene una función en la que puede ejecutar una directiva particular si y solo si se instala un huevo. Podría usar esto para quemy.blog.rss
registre un adaptador deIRSSFeed
enIXMLSegment
si mimy.blog.xmldump
está instalado, sin hacer quemy.blog.rss
dependa demy.blog.xmldump
.Viewlet
s son como pequeñasBrowserView
que puede tener ''suscribirse'' a un lugar particular dentro de una página. No puedo recordar todos los detalles en este momento, pero estos son muy buenos para cosas como complementos que desea que aparezcan en una barra lateral.No puedo recordar si son parte de la base Zope o Plone. Recomendaría no usar Plone, a menos que el problema que estás tratando de resolver necesite realmente un CMS real, ya que es un software grande y complicado y tiende a ser un poco lento.
En realidad, no necesita necesariamente
Viewlet
s, ya queBrowserView
puede llamarse entre sí, ya sea mediante el uso de ''object / @@ some_browser_view'' en una expresión TAL, o mediantequeryMultiAdapter( (ISomething, IHttpRequest), name=''some_browser_view'' )
, pero a pesar de todo son bastante agradables.Marker
Interface
s. Un marcador LaInterface
es unaInterface
que no proporciona ningún método ni atributo. Puede agregar un marcador a laInterface
cualquier objeto en tiempo de ejecución usandoISomething.alsoProvidedBy
. Esto le permite, por ejemplo, modificar qué adaptadores se utilizarán en un objeto particular y quéBrowserView
s se definirán en él.
Me disculpo por no haber entrado en detalles suficientes para poder implementar cada uno de estos ejemplos de inmediato, pero tomarían aproximadamente una publicación de blog cada uno.
Empecé a usar interfaces Zope en mi código y, a partir de ahora, en realidad son solo documentación. Los uso para especificar qué atributos debe tener la clase, implementarlos explícitamente en las clases apropiadas y buscarlos explícitamente donde lo espero. Esto está bien, pero me gustaría que hagan más si es posible, como verificar que la clase ha implementado la interfaz, en lugar de simplemente verificar que he dicho que la clase implementa la interfaz. He leído el zope wiki un par de veces, pero todavía no puedo ver mucho más uso de interfaces que lo que estoy haciendo actualmente. Entonces, mi pregunta es para qué otra cosa pueden usar estas interfaces, y cómo las usan para más.
Las interfaces Zope pueden proporcionar una forma útil de desacoplar dos piezas de código que no deberían depender entre sí.
Digamos que tenemos un componente que sabe cómo imprimir un saludo en el módulo a.py:
>>> class Greeter(object):
... def greet(self):
... print ''Hello''
Y un código que necesita imprimir un saludo en el módulo b.py:
>>> Greeter().greet()
''Hello''
Esta disposición hace que sea difícil cambiar el código que maneja el saludo sin tocar b.py (que podría distribuirse en un paquete separado). En cambio, podríamos introducir un tercer módulo c.py que define una interfaz IGreeter:
>>> from zope.interface import Interface
>>> class IGreeter(Interface):
... def greet():
... """ Gives a greeting. """
Ahora podemos usar esto para desacoplar a.py y b.py. En lugar de instanciar una clase Greeter, b.py ahora solicitará una utilidad que proporcione la interfaz IGreeter. Y a.py declarará que la clase Greeter implementa esa interfaz:
(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''
Nunca he usado interfaces Zope, pero podría considerar escribir una metaclass , que al inicializar comprueba a los miembros de la clase contra la interfaz y genera una excepción de tiempo de ejecución si no se implementa un método.
Con Python no tienes otras opciones. O tiene un paso de "compilación" que inspecciona su código o lo inspecciona dinámicamente en tiempo de ejecución.
En realidad, puedes probar si tu objeto o clase implementa tu interfaz. Para eso puedes usar el módulo de verify
(normalmente lo usarías en tus pruebas):
>>> 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
Las interfaces también se pueden usar para configurar y probar invariantes. Puedes encontrar más información aquí: