python interface zope zope.interface

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.

  1. Agregar vistas adicionales ( BrowserView s, generalmente registradas en ZCML con la directiva browser:page ) a todos los objetos que brindan IBlogPost . Podría hacer un huevo my.blog.printable . Ese huevo registraría un BrowserView llamado print para IBlogPost , 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. Ese BrowserView aparecería en URL /path/to/blogpost/@@print .

  2. 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 proporcionan IBlogPost . A ese suscriptor se le llamaría cada vez que cambiara un atributo en cualquier cosa que proporcionara IBlogPost , 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 cada BrowserView que modifique entradas de blog, ya que IObjectModified se envía una vez en cada cambio de atributo, lo que puede ocurrir con demasiada frecuencia por el rendimiento.

  3. 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ón zope.component.getAdapter . Los adaptadores de múltiples Interface deben zope.component.getMultiAdapter través de la función zope.component.getMultiAdapter , que es ligeramente menos bonita.

    Puede tener más de un adaptador para un conjunto determinado de Interface , diferenciado por un name 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 de Interface s a otra Interface , utilizando zope.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 un IXMLSegment y registra un adaptador de IBlogPost a IXMLSegment y un adaptador de IBlogConfiguration a IXMLSegment . Ahora puedo llamar a cualquier adaptador que sea apropiado para algún objeto que quiera serializar escribiendo IXMLSegment(object_to_serialize) .

    Incluso podría agregar más adaptadores de otras cosas a IXMLSegment de eggs que no sean my.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 que my.blog.rss registre un adaptador de IRSSFeed en IXMLSegment si mi my.blog.xmldump está instalado, sin hacer que my.blog.rss dependa de my.blog.xmldump .

  4. Viewlet s son como pequeñas BrowserView 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 que BrowserView puede llamarse entre sí, ya sea mediante el uso de ''object / @@ some_browser_view'' en una expresión TAL, o mediante queryMultiAdapter( (ISomething, IHttpRequest), name=''some_browser_view'' ) , pero a pesar de todo son bastante agradables.

  5. Marker Interface s. Un marcador La Interface es una Interface que no proporciona ningún método ni atributo. Puede agregar un marcador a la Interface cualquier objeto en tiempo de ejecución usando ISomething.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í:

http://www.muthukadan.net/docs/zca.html#interfaces