Diferença entre classe abstrata e interface em Python

Respostas:

618

O que você verá às vezes é o seguinte:

class Abstract1( object ):
    """Some description that tells you it's abstract,
    often listing the methods you're expected to supply."""
    def aMethod( self ):
        raise NotImplementedError( "Should have implemented this" )

Como o Python não possui (e não precisa) um contrato formal de interface, a distinção no estilo Java entre abstração e interface não existe. Se alguém se esforçar para definir uma interface formal, também será uma classe abstrata. As únicas diferenças estariam na intenção declarada na doutrina.

E a diferença entre abstrato e interface é uma coisa complicada quando você digita pato.

Java usa interfaces porque não possui herança múltipla.

Como o Python tem herança múltipla, você também pode ver algo assim

class SomeAbstraction( object ):
    pass # lots of stuff - but missing something

class Mixin1( object ):
    def something( self ):
        pass # one implementation

class Mixin2( object ):
    def something( self ):
        pass # another

class Concrete1( SomeAbstraction, Mixin1 ):
    pass

class Concrete2( SomeAbstraction, Mixin2 ):
    pass

Isso usa um tipo de superclasse abstrata com mixins para criar subclasses de concreto disjuntas.

S.Lott
fonte
5
S. Lott, você quer dizer que, devido à digitação do pato, a distinção entre has-a (interface) e is-a (herança) não é substancial?
Lorenzo
3
A diferença entre abstrato e interface é algo impressionante quando você digita pato. Não sei o que significa "substancial". É "real" - tem substância - da perspectiva do design. Mas da perspectiva da linguagem, pode não haver suporte. Você pode adotar convenções para distinguir entre uma classe abstrata e uma definição de classe de interface no Python.
S.Lott
26
@ L.DeLeo - você tem certeza de que sua noção de has-a vs. is-a está correta? Geralmente, vejo a distinção como variável has-a = member vs. is-a = herança (classe pai ou interface). Pense em Comparável ou Listar em Java; esses são relacionamentos is-a, independentemente de serem interfaces ou classes abstratas.
dimo414
43
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))é a mensagem de erro mais informativa :)
naught101
9
@ Lorenzo, um relacionamento que não tem nada a ver com herança, tipagem de pato, interfaces e classes abstratas (todos os quatro se referem a relacionamentos é um).
Karl Richter
197

Qual é a diferença entre classe abstrata e interface no Python?

Uma interface, para um objeto, é um conjunto de métodos e atributos nesse objeto.

No Python, podemos usar uma classe base abstrata para definir e aplicar uma interface.

Usando uma classe base abstrata

Por exemplo, digamos que desejemos usar uma das classes base abstratas do collectionsmódulo:

import collections
class MySet(collections.Set):
    pass

Se tentarmos usá-lo, obtemos um TypeErrorporque a classe que criamos não suporta o comportamento esperado dos conjuntos:

>>> MySet()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MySet with abstract methods
__contains__, __iter__, __len__

Assim, somos obrigados a implementar pelo menos __contains__ , __iter__e __len__. Vamos usar este exemplo de implementação da documentação :

class ListBasedSet(collections.Set):
    """Alternate set implementation favoring space over speed
    and not requiring the set elements to be hashable. 
    """
    def __init__(self, iterable):
        self.elements = lst = []
        for value in iterable:
            if value not in lst:
                lst.append(value)
    def __iter__(self):
        return iter(self.elements)
    def __contains__(self, value):
        return value in self.elements
    def __len__(self):
        return len(self.elements)

s1 = ListBasedSet('abcdef')
s2 = ListBasedSet('defghi')
overlap = s1 & s2

Implementação: Criando uma classe base abstrata

Podemos criar nossa própria classe base abstrata, definindo a metaclasse abc.ABCMetae usando o abc.abstractmethoddecorador nos métodos relevantes. A metaclasse adicionará as funções decoradas ao __abstractmethods__atributo, impedindo a instanciação até que sejam definidas.

import abc

Por exemplo, "efável" é definido como algo que pode ser expresso em palavras. Digamos que desejamos definir uma classe base abstrata que seja eficaz, no Python 2:

class Effable(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

Ou no Python 3, com a pequena alteração na declaração de metaclasse:

class Effable(object, metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

Agora, se tentarmos criar um objeto eficaz sem implementar a interface:

class MyEffable(Effable): 
    pass

e tente instancia-lo:

>>> MyEffable()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__

Dizem-nos que não terminamos o trabalho.

Agora, se cumprirmos, fornecendo a interface esperada:

class MyEffable(Effable): 
    def __str__(self):
        return 'expressable!'

então podemos usar a versão concreta da classe derivada da abstrata:

>>> me = MyEffable()
>>> print(me)
expressable!

Há outras coisas que poderíamos fazer com isso, como registrar subclasses virtuais que já implementam essas interfaces, mas acho que isso está além do escopo desta questão. Os outros métodos demonstrados aqui teriam que adaptar esse método usando o abcmódulo para fazê-lo, no entanto.

Conclusão

Demonstramos que a criação de uma classe base abstrata define interfaces para objetos personalizados em Python.

Aaron Hall
fonte
101

Python> = 2.6 possui classes base abstratas .

As classes base abstratas (ABCs abreviadas) complementam a digitação de patos, fornecendo uma maneira de definir interfaces quando outras técnicas como hasattr () seriam desajeitadas. O Python vem com muitos ABCs internos para estruturas de dados (no módulo de coleções), números (no módulo de números) e fluxos (no módulo io). Você pode criar seu próprio ABC com o módulo abc.

Há também o módulo Zope Interface , que é usado por projetos fora do zope, como twisted. Eu não estou realmente familiarizado com isso, mas há uma página wiki aqui que pode ajudar.

Em geral, você não precisa do conceito de classes abstratas ou interfaces em python (editado - consulte a resposta de S.Lott para obter detalhes).

JimB
fonte
2
O que você ganha usando ABCs em Python?
CpILL
38

Python realmente não tem nenhum conceito.

Ele usa digitação de pato, o que eliminou a necessidade de interfaces (pelo menos para o computador :-))

Python <= 2.5: As classes base obviamente existem, mas não há uma maneira explícita de marcar um método como 'virtual puro'; portanto, a classe não é realmente abstrata.

Python> = 2.6: As classes base abstratas existem ( http://docs.python.org/library/abc.html ). E permita que você especifique métodos que devem ser implementados nas subclasses. Não gosto muito da sintaxe, mas o recurso está lá. Na maioria das vezes, provavelmente é melhor usar a digitação de pato no lado do cliente 'using'.

Douglas Leeder
fonte
3
O Python 3.0 adiciona classes base abstratas reais. Eles são usados ​​no módulo de coleções e em outros locais. docs.python.org/3.0/library/abc.html
Lara Dougan
Uma referência sobre por que a digitação com patos remove a necessidade de interfaces seria útil. Não me parece óbvio que a digitação de pato, que entendo como a capacidade de "cutucar" qualquer método ou atributo em qualquer objeto, signifique que você não precisa especificar os comportamentos necessários (e pedir ao compilador que o lembre) implementá-las), e é assim que eu entendo as Classes base abstratas.
precisa
É menos a digitação do pato do que o suporte à herança múltipla que remove a linha artificial entre a interface e a classe abstrata que, por exemplo, o Java desenhou.
masi
35

De uma maneira mais básica para explicar: Uma interface é como uma forma vazia de muffin. É um arquivo de classe com um conjunto de definições de métodos que não têm código.

Uma classe abstrata é a mesma coisa, mas nem todas as funções precisam estar vazias. Alguns podem ter código. Não é estritamente vazio.

Por que diferenciar: Não há muita diferença prática no Python, mas no nível de planejamento de um projeto grande, pode ser mais comum falar sobre interfaces, pois não há código. Especialmente se você estiver trabalhando com programadores Java acostumados ao termo.

SilentSteel
fonte
+1 para a distinção onde ABC pode ter implementações de sua própria - o que parece uma maneira muito legal de outsmarting si mesmo
Titou
17

Em geral, as interfaces são usadas apenas em idiomas que usam o modelo de classe de herança única. Nessas linguagens de herança única, as interfaces são normalmente usadas se alguma classe puder usar um método ou conjunto de métodos específico. Também nessas linguagens de herança única, as classes abstratas são usadas para ter variáveis ​​de classe definidas, além de nenhum ou mais métodos, ou para explorar o modelo de herança única para limitar o intervalo de classes que poderia usar um conjunto de métodos.

Os idiomas que suportam o modelo de herança múltipla tendem a usar apenas classes ou classes base abstratas e não interfaces. Como o Python suporta herança múltipla, ele não usa interfaces e você deseja usar classes base ou classes base abstratas.

http://docs.python.org/library/abc.html

Lara Dougan
fonte
2

Classes abstratas são classes que contêm um ou mais métodos abstratos. Juntamente com os métodos abstratos, as classes Abstratas podem ter métodos estáticos, de classe e de instância. Mas no caso da interface, ela só terá métodos abstratos e não outros. Portanto, não é obrigatório herdar a classe abstrata, mas é obrigatório herdar a interface.

Akash Ukarande
fonte
1

Por questões de integridade, devemos mencionar o PEP3119, onde o ABC foi introduzido e comparado com interfaces, e os originais de Talin. comentário .

A classe abstrata não é uma interface perfeita:

  • pertence à hierarquia de herança
  • é mutável

Mas se você pensar em escrever à sua maneira:

def some_function(self):
     raise NotImplementedError()

interface = type(
    'your_interface', (object,),
    {'extra_func': some_function,
     '__slots__': ['extra_func', ...]
     ...
     '__instancecheck__': your_instance_checker,
     '__subclasscheck__': your_subclass_checker
     ...
    }
)

ok, rather as a class
or as a metaclass
and fighting with python to achieve the immutable object
and doing refactoring
...

você perceberá rapidamente que está inventando a roda para finalmente alcançar abc.ABCMeta

abc.ABCMeta foi proposto como uma adição útil da funcionalidade de interface ausente, e isso é justo o suficiente em uma linguagem como python.

Certamente, ele foi aprimorado melhor enquanto escrevia a versão 3 e adicionava nova sintaxe e conceito de interface imutável ...

Conclusão:

The abc.ABCMeta IS "pythonic" interface in python
Sławomir Lenart
fonte