Como implementar interfaces em python?

182
public interface IInterface
{
    void show();
}

 public class MyClass : IInterface
{

    #region IInterface Members

    public void show()
    {
        Console.WriteLine("Hello World!");
    }

    #endregion
}

Como eu implemento o Python equivalente a este código C #?

class IInterface(object):
    def __init__(self):
        pass

    def show(self):
        raise Exception("NotImplementedException")


class MyClass(IInterface):
   def __init__(self):
       IInterface.__init__(self)

   def show(self):
       print 'Hello World!'

Isso é uma boa ideia?? Por favor, dê exemplos em suas respostas.

Pratik Deoghare
fonte
Qual seria o propósito de usar uma interface no seu caso?
precisa
23
Sinceramente, nenhum propósito! Eu só quero aprender o que fazer quando você precisar de interfaces em python?
Pratik Deoghare 23/01
18
raise NotImplementedErroré o que showo corpo deveria ser - não faz sentido criar uma abordagem completamente genéricaException quando o Python define um embutido perfeitamente específico!)
Alex Martelli
2
Não deve iniciar a chamada show () no IInterface (ou gerar a própria exceção) para que você não possa instanciar uma interface abstrata?
Viagem Katastic
1
Eu posso ver algum uso para isso ... digamos que você tenha um objeto que deseja garantir que tenha uma assinatura específica. Com a digitação de patos, você não pode garantir que o objeto seja da assinatura que você espera. Às vezes, pode ser útil aplicar alguma digitação nas propriedades digitadas dinamicamente.
Prime By Design

Respostas:

151

Como mencionado por outro aqui:

As interfaces não são necessárias no Python. Isso ocorre porque o Python possui herança múltipla adequada e também tipagem de patinhos, o que significa que nos locais em que você deve ter interfaces em Java, não é necessário tê-las em Python.

Dito isto, ainda existem vários usos para interfaces. Alguns deles são abordados pelas Pythons Abstract Base Classes, introduzidas no Python 2.6. Eles são úteis, se você deseja criar classes base que não podem ser instanciadas, mas fornecem uma interface específica ou parte de uma implementação.

Outro uso é se você, de alguma forma, deseja especificar que um objeto implementa uma interface específica e também pode usar ABCs para isso, subclassificando-os. Outra maneira é o zope.interface, um módulo que faz parte da Arquitetura de Componentes do Zope, uma estrutura de componentes realmente incrível. Aqui você não faz subclasse das interfaces, mas marca classes (ou mesmo instâncias) como implementando uma interface. Isso também pode ser usado para procurar componentes de um registro de componente. Muito legal!

Lennart Regebro
fonte
11
Você poderia elaborar isso? 1. Como se implementa essa interface? 2. Como ele pode ser usado para procurar componentes?
Geoidesic
42
"Interfaces não são necessárias em Python. Exceto quando são."
Baptiste Candellier 9/02/19
8
As interfaces são usadas principalmente para obter um resultado previsível / impor a correção dos membros ao passar objetos. seria ótimo se o python suportasse isso como uma opção. também permitiria que as ferramentas de desenvolvimento tivessem melhor inteligência
Sonic Soul
1
Um exemplo melhoraria bastante esta resposta.
bob
5
"Isso ocorre porque o Python tem herança múltipla apropriada", quem disse que as interfaces são para herança múltipla?
adnanmuttaleb
76

Usar o módulo abc para classes base abstratas parece fazer o truque.

from abc import ABCMeta, abstractmethod

class IInterface:
    __metaclass__ = ABCMeta

    @classmethod
    def version(self): return "1.0"
    @abstractmethod
    def show(self): raise NotImplementedError

class MyServer(IInterface):
    def show(self):
        print 'Hello, World 2!'

class MyBadServer(object):
    def show(self):
        print 'Damn you, world!'


class MyClient(object):

    def __init__(self, server):
        if not isinstance(server, IInterface): raise Exception('Bad interface')
        if not IInterface.version() == '1.0': raise Exception('Bad revision')

        self._server = server


    def client_show(self):
        self._server.show()


# This call will fail with an exception
try:
    x = MyClient(MyBadServer)
except Exception as exc:
    print 'Failed as it should!'

# This will pass with glory
MyClient(MyServer()).client_show()
Peter Torpman
fonte
11
Yugghh, precisando de algum módulo para o que deveria fazer parte da própria linguagem, ou não usar nada, IMO.
Mike de Klerk
você quer dizer if not server.version() == '1.0': raise ...:? Eu realmente não entendo essa linha. Uma explicação seria bem-vinda.
Skandix 18/03/19
1
@MikedeKlerk Não concordo mais. Assim como a resposta do Python para digitação; Eu não deveria ter que importar um módulo para declarar que eu quero que um tipo seja um tipo. A resposta para isso é tipicamente "bem, o Python é digitado dinamicamente", mas isso não é uma desculpa. O Java + Groovy resolve esse problema. Java para o material estático, Groovy para o material dinâmico.
Ubiquibacon 03/07/19
6
@MikedeKlerk, o módulo abc está realmente embutido no python. É um pouco mais trabalhoso configurar alguns desses padrões porque eles são em grande parte desnecessários no Python devido a padrões alternativos que são considerados 'mais pitônicos'. Para a grande maioria dos desenvolvedores, seria como você disse, 'não utilizado'. No entanto, reconhecendo que existem alguns casos muito específicos que realmente exigem esses recursos de interface, os criadores do Python forneceram uma API fácil de usar para tornar isso possível.
David Culbreth
39

A interface suporta Python 2.7 e Python 3.4+.

Para instalar a interface, você precisa

pip install python-interface

Código de exemplo:

from interface import implements, Interface

class MyInterface(Interface):

    def method1(self, x):
        pass

    def method2(self, x, y):
        pass


class MyClass(implements(MyInterface)):

    def method1(self, x):
        return x * 2

    def method2(self, x, y):
        return x + y
raio azul
fonte
7
Uma grande vantagem dessa biblioteca, IMHO, é a falha precoce que ela oferece: se sua classe não implementar corretamente uma interface especificada, você receberá uma exceção assim que a classe for lida - você nem precisa usá-la . Com a própria classe base abstrata do Python, você recebe a exceção quando instancia sua classe, o que pode ser muito mais tarde.
Hans
É desnecessário, o ABC oferece uma funcionalidade integrada semelhante.
Daniel
@DanielCasares O ABC oferece uma interface real ou você quer dizer que classes abstratas sem estado ou implementações são a solução que o ABC oferece?
asaf92 6/03
36

A implementação de interfaces com classes base abstratas é muito mais simples no Python 3 moderno e serve a um propósito como contrato de interface para extensões de plug-in.

Crie a interface / classe base abstrata:

from abc import ABC, abstractmethod

class AccountingSystem(ABC):

    @abstractmethod
    def create_purchase_invoice(self, purchase):
        pass

    @abstractmethod
    def create_sale_invoice(self, sale):
        log.debug('Creating sale invoice', sale)

Crie uma subclasse normal e substitua todos os métodos abstratos:

class GizmoAccountingSystem(AccountingSystem):

    def create_purchase_invoice(self, purchase):
        submit_to_gizmo_purchase_service(purchase)

    def create_sale_invoice(self, sale):
        super().create_sale_invoice(sale)
        submit_to_gizmo_sale_service(sale)

Opcionalmente, você pode ter uma implementação comum nos métodos abstratos como em create_sale_invoice(), chamando-a super()explicitamente na subclasse como acima.

Falha na instanciação de uma subclasse que não implementa todos os métodos abstratos:

class IncompleteAccountingSystem(AccountingSystem):
    pass

>>> accounting = IncompleteAccountingSystem()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class IncompleteAccountingSystem with abstract methods
create_purchase_invoice, create_sale_invoice

Você também pode ter propriedades abstratas, métodos estáticos e de classe combinando anotações correspondentes com @abstractmethod.

As classes base abstratas são ótimas para implementar sistemas baseados em plugins. Todas as subclasses importadas de uma classe são acessíveis via __subclasses__(), portanto, se você carregar todas as classes de um diretório de plug-ins importlib.import_module()e se elas subclassificarem a classe base, você terá acesso direto a elas via __subclasses__()e poderá ter certeza de que o contrato de interface é aplicado a todos eles durante a instanciação.

Aqui está a implementação de carregamento do plug-in para o AccountingSystemexemplo acima:

...
from importlib import import_module

class AccountingSystem(ABC):

    ...
    _instance = None

    @classmethod
    def instance(cls):
        if not cls._instance:
            module_name = settings.ACCOUNTING_SYSTEM_MODULE_NAME
            import_module(module_name)
            subclasses = cls.__subclasses__()
            if len(subclasses) > 1:
                raise InvalidAccountingSystemError('More than one '
                        f'accounting module: {subclasses}')
            if not subclasses or module_name not in str(subclasses[0]):
                raise InvalidAccountingSystemError('Accounting module '
                        f'{module_name} does not exist or does not '
                        'subclass AccountingSystem')
            cls._instance = subclasses[0]()
        return cls._instance

Em seguida, você pode acessar o objeto de plug-in do sistema de contabilidade por meio da AccountingSystemclasse:

>>> accountingsystem = AccountingSystem.instance()

(Inspirado por este post do PyMOTW-3 .)

Mrts
fonte
Pergunta: O que significa o nome do módulo "ABC"?
Sebastian Nielsen
"ABC" significa "Classes base abstratas", consulte documentos oficiais
mrts
31

Existem implementações de interfaces de terceiros para Python (o mais popular é o Zope , também usado no Twisted ), mas mais comumente os codificadores Python preferem usar o conceito mais rico conhecido como "Abstract Base Class" (ABC), que combina uma interface com a possibilidade de ter alguns aspectos de implementação lá também. Os ABCs são particularmente bem suportados no Python 2.6 e versões posteriores, veja o PEP , mas mesmo nas versões anteriores do Python elas são normalmente vistas como "o caminho a percorrer" - basta definir uma classe que alguns de cujos métodos aumentam NotImplementedErrorpara que as subclasses sejam no aviso de que é melhor substituir esses métodos! -)

Alex Martelli
fonte
3
Existem implementações de interfaces de terceiros para Python O que isso significa? Você poderia explicar a ABC?
Pratik Deoghare 23/01
2
Bem, eu discordo que a ABC é "mais rica". ;) Há coisas que o zope.interface pode fazer que o ABC não pode, bem como o contrário. Mas, caso contrário, você está certo como sempre. +1
Lennart Regebro
1
@ Alfred: Significa que módulos como o zope.interface não estão incluídos na biblioteca padrão, mas estão disponíveis no pypi.
Lennart Regebro
Ainda tenho dificuldade para entender o conceito de ABC. Seria possível alguém reescrever twistedmatrix.com/documents/current/core/howto/components.html (IMHO, uma excelente explicação do conceito de interfaces) em termos de ABC. Isso faz algum sentido?
mcepl
21

Algo assim (pode não funcionar, pois não tenho Python por perto):

class IInterface:
    def show(self): raise NotImplementedError

class MyClass(IInterface):
    def show(self): print "Hello World!"
Bandido
fonte
2
O que devo fazer sobre __init__(self)o construtor?
Pratik Deoghare 23/01
1
Você decide. Como não há verificação em tempo de compilação contra a construção de um objeto a partir de uma classe abstrata, você não obteria nenhuma proteção durante a codificação / compilação. Haverá um construtor herdado, de modo que o objeto teria se criado, basta ser "vazio". Cabe a você decidir se seria melhor permitir que isso acontecesse e detectar falhas mais tarde ou interromper explicitamente o programa imediatamente, implementando um construtor semelhante lançando uma exceção.
Bandi-T
1
É por isso que abc.ABCé muito melhor do que aumentar NotImplementedError- a instanciação de uma abc.ABCsubclasse que não implementa todos os métodos abstratos falha mais cedo, portanto, você está protegido contra erros. Veja minha resposta abaixo para saber como é o erro.
Mrts
8

Meu entendimento é que as interfaces não são tão necessárias em linguagens dinâmicas como Python. Em Java (ou C ++ com sua classe base abstrata), as interfaces são meios para garantir que, por exemplo, você esteja passando o parâmetro certo, capaz de executar um conjunto de tarefas.

Por exemplo, se você tem um observador e um observável, o observável está interessado em assinar objetos que suportem a interface IObserver, que por sua vez notify ação. Isso é verificado no momento da compilação.

No Python, não existe, compile timee as pesquisas de método são executadas em tempo de execução. Além disso, pode-se substituir a pesquisa pelos métodos mágicos __getattr __ () ou __getattribute __ (). Em outras palavras, você pode passar, como observador, qualquer objeto que possa retornar para chamar ao acessar o notifyatributo.

Isso me leva à conclusão de que as interfaces no Python existem - é apenas que sua aplicação é adiada para o momento em que são realmente usadas

Tomasz Zieliński
fonte