Objetivo das interfaces do Zope?
-
22-09-2019 - |
Pergunta
Eu comecei a usar interfaces Zope em meu código, e a partir de agora, elas são apenas documentação.Eu os uso para especificar quais atributos a classe deve possuir, implementá-los explicitamente nas classes apropriadas e verificá-los explicitamente onde espero que existam.Tudo bem, mas eu gostaria que eles fizessem mais, se possível, como realmente verificar se a classe implementou a interface, em vez de apenas verificar se eu disse que a classe implementa a interface.Eu li o wiki do zope algumas vezes, mas ainda não consigo ver muito mais utilidade para interfaces do que estou fazendo atualmente.Então, minha pergunta é para que mais você pode usar essas interfaces e como usá-las para mais.
Solução
Você pode realmente testar se o seu objeto ou classe implementa sua interface. Para isso, você pode usar verify
Módulo (você normalmente o usaria em seus testes):
>>> 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
As interfaces também podem ser usadas para definir e testar invariantes. Você pode encontrar mais informações aqui:
Outras dicas
Onde trabalho, usamos Interfaces para podermos usar ZCA, ou o Arquitetura de Componentes Zope, que é uma estrutura completa para criar componentes que podem ser trocados e plugados usando Interface
S.Usamos ZCA para que possamos lidar com todos os tipos de personalizações por cliente sem necessariamente ter que bifurcar nosso software ou ter todos os muitos bits por cliente bagunçando a árvore principal.O wiki do Zope é muitas vezes bastante incompleto, infelizmente.Há uma explicação boa, mas concisa, da maioria dos recursos do ZCA em seu Página pypi da ZCA.
eu não uso Interface
s para qualquer coisa como verificar se uma classe implementa todos os métodos para um determinado Interface
.Em teoria, isso pode ser útil quando você adiciona outro método a uma interface, para verificar se você se lembrou de adicionar o novo método a todas as classes que implementam a interface.Pessoalmente, prefiro fortemente criar um novo Interface
sobre a modificação de um antigo.Modificando antigo Interfaces
geralmente é uma péssima ideia, uma vez que estão em ovos que foram liberados para o pypi ou para o resto da sua organização.
Uma nota rápida sobre a terminologia:Aulas implemento Interface
s e objetos (instâncias de classes) fornecer Interface
S.Se você quiser verificar se há um Interface
, você escreveria ISomething.implementedBy(SomeClass)
ou ISomething.providedBy(some_object)
.
Então, vamos a exemplos de onde o ZCA é útil.Vamos fingir que estamos escrevendo um blog, usando o ZCA para torná-lo modular.Teremos um BlogPost
objeto para cada postagem, o que fornecerá um IBlogPost
interface, tudo definido em nosso prático my.blog
ovo.Também armazenaremos a configuração do blog em BlogConfiguration
objetos que fornecem IBlogConfiguration
.Usando isso como ponto de partida, podemos implementar novos recursos sem necessariamente ter que tocar my.blog
de forma alguma.
A seguir está uma lista de exemplos de coisas que podemos fazer usando ZCA, sem ter que alterar a base my.blog
ovo.Eu ou meus colegas de trabalho fizemos todas essas coisas (e as consideramos úteis) em projetos reais para clientes, embora não estivéssemos implementando blogs na época.:) Alguns dos casos de uso aqui poderiam ser melhor resolvidos por outros meios, como um arquivo CSS impresso.
Adicionando visualizações extras (
BrowserView
s, geralmente registrado em ZCML com obrowser:page
diretiva) a todos os objetos que fornecemIBlogPost
.eu poderia fazer ummy.blog.printable
ovo.Esse ovo registraria um BrowserView chamadoprint
paraIBlogPost
, que renderiza a postagem do blog por meio de um Modelo de página Zope projetado para produzir HTML que imprima bem.QueBrowserView
apareceria então no URL/path/to/blogpost/@@print
.O mecanismo de assinatura de eventos no Zope.Digamos que eu queira publicar feeds RSS e gerá-los antecipadamente, e não mediante solicitação.Eu poderia criar um
my.blog.rss
ovo.Nesse ovo, eu registraria um assinante para eventos que fornecessem IObjectModificado (zope.lifecycleevent.interfaces.IObjectModified
), em objetos que fornecemIBlogPost
.Esse assinante seria chamado toda vez que um atributo fosse alterado em qualquer coisa que fornecesseIBlogPost
, e eu poderia usá-lo para atualizar todos os feeds RSS nos quais a postagem do blog deveria aparecer.Neste caso, talvez seja melhor ter um
IBlogPostModified
evento que é enviado no final de cada um dosBrowserView
s que modificam postagens de blog, já queIObjectModified
é enviado uma vez em cada alteração de atributo - o que pode ser muito frequente por uma questão de desempenho.Adaptadores.Os adaptadores são efetivamente "lançados" de uma interface para outra.Para geeks de linguagem de programação:Adaptadores Zope implementam despacho múltiplo "aberto" em Python (por "aberto" quero dizer "você pode adicionar mais casos de qualquer ovo"), com correspondências de interface mais específicas tendo prioridade sobre correspondências menos específicas (
Interface
classes podem ser subclasses umas das outras, e isso faz exatamente o que você espera que faça.)Adaptadores de um
Interface
pode ser chamado com uma sintaxe muito boa,ISomething(object_to_adapt)
, ou pode ser consultado através da funçãozope.component.getAdapter
.Adaptadores de váriosInterface
s devem ser consultados através da funçãozope.component.getMultiAdapter
, o que é um pouco menos bonito.Você pode ter mais de um adaptador para um determinado conjunto de
Interface
s, diferenciado por uma stringname
que você fornece ao registrar o adaptador.O nome padrão é""
.Por exemplo,BrowserView
s são, na verdade, adaptadores que se adaptam à interface na qual estão registrados e a uma interface que a classe HTTPRequest implementa.Você também pode procurar todos dos adaptadores que são registrados a partir de uma sequência deInterface
é para outroInterface
, usandozope.component.getAdapters( (IAdaptFrom,), IAdaptTo )
, que retorna uma sequência de pares (nome, adaptador).Isso pode ser usado como uma maneira muito boa de fornecer ganchos aos quais os plug-ins podem se conectar.Digamos que eu queira salvar todas as postagens e configurações do meu blog como um grande arquivo XML.Eu crio um
my.blog.xmldump
ovo que define umIXMLSegment
, e registra um adaptador deIBlogPost
paraIXMLSegment
e um adaptador deIBlogConfiguration
paraIXMLSegment
.Agora posso chamar qualquer adaptador apropriado para algum objeto que desejo serializar escrevendoIXMLSegment(object_to_serialize)
.Eu poderia até adicionar mais adaptadores de várias outras coisas para
IXMLSegment
de ovos que não sejammy.blog.xmldump
.ZCML possui um recurso onde pode executar uma diretiva específica se e somente se algum ovo estiver instalado.Eu poderia usar isso para termy.blog.rss
registrar um adaptador deIRSSFeed
paraIXMLSegment
semy.blog.xmldump
acontece de ser instalado, sem fazermy.blog.rss
depende demy.blog.xmldump
.Viewlet
são como pequenosBrowserView
É que você pode 'inscrever-se' em um determinado local dentro de uma página.Não consigo me lembrar de todos os detalhes agora, mas eles são muito bons para coisas como plug-ins que você deseja que apareçam em uma barra lateral.Não me lembro de imediato se eles fazem parte da base Zope ou Plone.Eu não recomendaria usar o Plone, a menos que o problema que você está tentando resolver realmente precise de um CMS real, já que é um software grande e complicado e tende a ser um pouco lento.
Você não precisa necessariamente
Viewlet
de qualquer maneira, já queBrowserView
s podem chamar um ao outro, usando 'object/@@some_browser_view' em uma expressão TAL ou usandoqueryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' )
, mas eles são muito legais de qualquer maneira.Marcador
Interface
S.Um marcadorInterface
é umInterface
que não fornece métodos nem atributos.Você pode adicionar um marcadorInterface
qualquer objeto em tempo de execução usandoISomething.alsoProvidedBy
.Isso permite, por exemplo, alterar quais adaptadores serão usados em um determinado objeto e quaisBrowserView
s será definido nele.
Peço desculpas por não ter entrado em detalhes suficientes para poder implementar cada um desses exemplos imediatamente, mas cada um deles levaria aproximadamente uma postagem no blog.
As interfaces de zope podem fornecer uma maneira útil de dissociar duas peças de código que não devem depender uma da outra.
Digamos que temos um componente que saiba imprimir uma saudação no módulo A.Py:
>>> class Greeter(object):
... def greet(self):
... print 'Hello'
E algum código que precisa imprimir uma saudação no módulo B.Py:
>>> Greeter().greet()
'Hello'
Esse arranjo dificulta a troca do código que lida com a saudação sem tocar B.Py (que pode ser distribuída em um pacote separado). Em vez disso, poderíamos introduzir um terceiro módulo C.Py, que define uma interface do iGreeter:
>>> from zope.interface import Interface
>>> class IGreeter(Interface):
... def greet():
... """ Gives a greeting. """
Agora podemos usar isso para dissociar A.Py e B.Py. Em vez de instantar uma classe cumprimentadora, o B.Py agora solicitará um utilitário que fornece a interface do iGreeter. E A.Py declarará que a classe Greeter implementa essa interface:
(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'
Eu nunca usei interfaces de zope, mas você pode considerar escrever um metaclasse, que na inicialização verifica os membros da classe em relação à interface e aumenta uma exceção de tempo de execução se um método não for implementado.
Com Python, você não tem outras opções. Tenha uma etapa de "compilação" que inspeciona seu código ou inspecione -o dinamicamente em tempo de execução.