2010-03-26 25 views
32

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.¿Propósito de las interfaces Zope?

Respuesta

23

En realidad, puede probar si su objeto o clase implementa su interfaz. Para que puede utilizar verify módulo (se suele utilizar en las 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 utilizar para configurar y probar invariantes. Puede encontrar más información aquí:

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

+2

Genial, supongo que mi solución de escribir tus propias pruebas es innecesaria. Aunque todavía podría ser útil tener excepciones de tiempo de ejecución en lugar de confiar en un caso de prueba, esta es una mejor solución. –

+0

Gracias, esto es exactamente lo que estaba buscando. Estaba seguro de que había alguna manera de hacer esto, pero no tenía idea de cómo. – Nikwin

+1

Además, echa un vistazo a zope.esquema: proporciona una forma más detallada de especificar tipos. Y también existe zope.interface.invariant, que se puede usar para definir los invariantes reales que la interfaz debe respetar. –

2

Nunca utilicé las interfaces de Zope, pero podría considerar escribir metaclass, que al inicializar comprueba los miembros de la clase con la interfaz y genera una excepción de tiempo de ejecución si no se implementa un método.

Con Python no tiene otras opciones. O tiene un paso de "compilación" que inspecciona su código o lo inspecciona dinámicamente en tiempo de ejecución.

19

interfaces de Zope pueden proporcionar una forma útil para desacoplar dos piezas de código que no debe depender el uno del otro.

Digamos que tenemos un componente que sabe cómo imprimir un saludo en a.py módulo:

>>> class Greeter(object): 
...  def greet(self): 
...   print 'Hello' 

Y un código que necesita para imprimir un saludo en b.py módulo:

>>> 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 su lugar, se podría introducir una tercera c.py módulo 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' 
+1

para completar, ¿puede agregar un ejemplo para los adaptadores? – sureshvv

+0

¿Qué quiere decir con 'cambiar el código que maneja el saludo'? ¿Cómo se resuelve este problema usando la interfaz como se mostró arriba? –

+1

En 'b.py' él está recibiendo una sorpresa, pero él no ha importado' a'. es decir: no hay 'de un Greeter de importación' – Kurt

47

donde trabajo, que utilizan interfaces de modo que podemos utilizar ZCA, o la Zope Component Architecture, que es todo un marco para la fabricación de componentes que son intercambiables y conectable 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 su ZCA's pypi page.

No uso Interface s para nada como comprobar que una clase implementa todos los métodos para un Interface dado.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 sobre la modificación de una anterior. La modificación de la antigua Interfaces 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 la terminología: clases implementanInterface s, y objetos (instancias de clases) proporcionanInterface s. Si desea verificar un Interface, escriba ISomething.implementedBy(SomeClass) o ISomething.providedBy(some_object).

Por lo tanto, a 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 huevo handy-dandy my.blog. También almacenaremos la configuración del blog en los objetos BlogConfiguration que proporcionan IBlogConfiguration. Usando esto como punto de partida, podemos implementar nuevas características sin tener que tocar el my.blog.

La siguiente es una lista de ejemplos de cosas que podemos hacer al usar ZCA, sin tener que alterar el huevo base my.blog. 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. Adición de puntos de vista adicionales (BrowserView s, por lo general registrados en ZCML con la directiva browser:page) a todos los objetos que proporcionan IBlogPost. Podría hacer un huevo my.blog.printable. Ese huevo registraría un BrowserView llamado print para IBlogPost, que representa la entrada del blog a través de un Zope Page Template diseñado para producir HTML que se imprime muy bien. Ese BrowserView aparecería en la URL /path/to/blogpost/@@print.

  2. 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 IBlogPostModified evento que se envía al final de cada uno de los BrowserView s que modifican publicaciones de blog, ya que IObjectModified se envía una vez en cada cambio de atributo único, 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" quiero decir "puedes 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 de unos a otros, y esto hace exactamente lo que usted esperaría que lo haría.)

    adaptadores de un Interface se pueden llamar con una sintaxis muy agradable, ISomething(object_to_adapt), o se pueden consultar a través de la función zope.component.getAdapter .Los adaptadores de múltiples Interface s deben buscarse a través de la función zope.component.getMultiAdapter, que es ligeramente menos bonita.

    Puede tener más de un adaptador para un conjunto dado de Interface s, diferenciado por una cadena name 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 de los adaptadores que están registrados de una secuencia de Interface a otra Interface, usando 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 al adaptador que sea apropiado para algún objeto que quiera serializar escribiendo IXMLSegment(object_to_serialize).

    Incluso podría agregar más adaptadores de varias otras cosas a IXMLSegment de huevos 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 tener my.blog.rss registrar un adaptador de IRSSFeed a IXMLSegment si se instala my.blog.xmldump, sin que my.blog.rss dependa de my.blog.xmldump.

  4. Viewlet s son como pequeños BrowserView s que puede tener 'suscribirse' a un lugar en 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.

    que no necesariamente necesitan realmente Viewlet s todos modos, ya BrowserView s puedan llamarse entre sí, ya sea mediante el uso de 'objeto/@@ some_browser_view' en una expresión TAL, o mediante el uso de queryMultiAdapter((ISomething, IHttpRequest), name='some_browser_view'), pero son bastante agradable sin tener en cuenta .

  5. Marcador Interface s. Un marcador Interface es un Interface que no proporciona ningún método ni atributo. Puede agregar un marcador Interface a 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 cuáles se definirán en él BrowserView s.

me disculpo que no he entrado en detalles suficientes para poder llevar a cabo cada uno de estos ejemplos de inmediato, sino que tomaría aproximadamente una entrada de blog cada uno.

+1

¡Grandes explicaciones, gracias por esto! –

+2

+1 - es realmente una pena que mucha gente no sepa que los adaptadores son realmente un mecanismo de envío múltiple/multimétodos. –

+0

+1 como un buen ejemplo y cualquiera que sea el 40-50% de las respuestas que pude darles sentido. Tantas terminologías nuevas –