Respuestas:
Lo que verá a veces es lo siguiente:
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" )
Debido a que Python no tiene (y no necesita) un contrato de interfaz formal, la distinción de estilo Java entre abstracción e interfaz no existe. Si alguien se esfuerza por definir una interfaz formal, también será una clase abstracta. Las únicas diferencias estarían en la intención declarada en la cadena de documentación.
Y la diferencia entre el resumen y la interfaz es una cosa desgarradora cuando tienes que escribir en un pato.
Java usa interfaces porque no tiene herencia múltiple.
Debido a que Python tiene herencia múltiple, también puede ver algo como esto
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
Esto utiliza una especie de superclase abstracta con mixins para crear subclases concretas que son disjuntas.
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))
es un mensaje de error más informativo :)
¿Cuál es la diferencia entre clase abstracta e interfaz en Python?
Una interfaz, para un objeto, es un conjunto de métodos y atributos en ese objeto.
En Python, podemos usar una clase base abstracta para definir y aplicar una interfaz.
Por ejemplo, supongamos que queremos usar una de las clases base abstractas del collections
módulo:
import collections
class MySet(collections.Set):
pass
Si intentamos usarlo, obtenemos un TypeError
porque la clase que creamos no admite el comportamiento esperado de los 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__
Por lo tanto estamos obligados a implementar al menos __contains__
, __iter__
y __len__
. Usemos este ejemplo de implementación de la documentación :
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
Podemos crear nuestra propia Clase base abstracta configurando la metaclase abc.ABCMeta
y utilizando el abc.abstractmethod
decorador en métodos relevantes. La metaclase agregará las funciones decoradas al __abstractmethods__
atributo, evitando la creación de instancias hasta que se definan.
import abc
Por ejemplo, "effable" se define como algo que se puede expresar en palabras. Digamos que queremos definir una clase base abstracta que sea effable, en Python 2:
class Effable(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
O en Python 3, con el ligero cambio en la declaración de metaclase:
class Effable(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def __str__(self):
raise NotImplementedError('users must define __str__ to use this base class')
Ahora, si intentamos crear un objeto effable sin implementar la interfaz:
class MyEffable(Effable):
pass
e intentar instanciarlo:
>>> MyEffable()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__
Nos dicen que no hemos terminado el trabajo.
Ahora si cumplimos proporcionando la interfaz esperada:
class MyEffable(Effable):
def __str__(self):
return 'expressable!'
entonces podemos usar la versión concreta de la clase derivada de la abstracta:
>>> me = MyEffable()
>>> print(me)
expressable!
Hay otras cosas que podríamos hacer con esto, como registrar subclases virtuales que ya implementan estas interfaces, pero creo que eso está más allá del alcance de esta pregunta. abc
Sin embargo, los otros métodos demostrados aquí tendrían que adaptar este método usando el módulo para hacerlo.
Hemos demostrado que la creación de una clase base abstracta define interfaces para objetos personalizados en Python.
Python> = 2.6 tiene clases base abstractas .
Las clases base abstractas (ABC abreviadas) complementan la tipificación de pato al proporcionar una forma de definir interfaces cuando otras técnicas como hasattr () serían torpes. Python viene con muchos ABC integrados para estructuras de datos (en el módulo de colecciones), números (en el módulo de números) y flujos (en el módulo io). Puede crear su propio ABC con el módulo abc.
También está el módulo Zope Interface , que es utilizado por proyectos fuera de zope, como twisted. No estoy realmente familiarizado con eso, pero hay una página wiki aquí que podría ayudar.
En general, no necesita el concepto de clases abstractas o interfaces en python (editado - vea la respuesta de S.Lott para más detalles).
Python realmente no tiene ninguno de los dos conceptos.
Utiliza la escritura de pato, lo que eliminó la necesidad de interfaces (al menos para la computadora :-))
Python <= 2.5: las clases base obviamente existen, pero no existe una forma explícita de marcar un método como 'virtual puro', por lo que la clase no es realmente abstracta.
Python> = 2.6: existen clases base abstractas ( http://docs.python.org/library/abc.html ). Y le permite especificar métodos que deben implementarse en subclases. No me gusta mucho la sintaxis, pero la función está ahí. La mayoría de las veces, probablemente sea mejor usar la escritura de pato desde el lado del cliente 'usar'.
En una forma más básica de explicar: una interfaz es como una bandeja de panecillos vacía. Es un archivo de clase con un conjunto de definiciones de métodos que no tienen código.
Una clase abstracta es lo mismo, pero no todas las funciones deben estar vacías. Algunos pueden tener código. No está estrictamente vacío.
Por qué diferenciar: no hay mucha diferencia práctica en Python, pero a nivel de planificación para un proyecto grande, podría ser más común hablar de interfaces, ya que no hay código. Especialmente si está trabajando con programadores de Java que están acostumbrados al término.
En general, las interfaces se usan solo en idiomas que usan el modelo de clase de herencia única. En estos lenguajes de herencia única, las interfaces se usan típicamente si alguna clase podría usar un método o conjunto de métodos en particular. También en estos lenguajes de herencia única, las clases abstractas se usan para tener variables de clase definidas además de ninguno o más métodos, o para explotar el modelo de herencia única para limitar el rango de clases que podrían usar un conjunto de métodos.
Los lenguajes que admiten el modelo de herencia múltiple tienden a usar solo clases o clases base abstractas y no interfaces. Dado que Python admite la herencia múltiple, no usa interfaces y querrás usar clases base o clases base abstractas.
Las clases abstractas son clases que contienen uno o más métodos abstractos. Junto con los métodos abstractos, las clases abstractas pueden tener métodos estáticos, de clase y de instancia. Pero en el caso de la interfaz, solo tendrá métodos abstractos, no otros. Por lo tanto, no es obligatorio heredar la clase abstracta, pero sí es obligatorio heredar la interfaz.
Para completar, debemos mencionar PEP3119 donde se introdujo ABC y se comparó con las interfaces, y el Talin original comentario .
La clase abstracta no es la interfaz perfecta:
Pero si considera escribirlo a su manera:
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
...
te darás cuenta rápidamente de que estás inventando la rueda para lograr
abc.ABCMeta
abc.ABCMeta
se propuso como una adición útil de la funcionalidad de la interfaz que falta, y eso es bastante justo en un lenguaje como python.
Ciertamente, se pudo mejorar mejor al escribir la versión 3, y agregar una nueva sintaxis y un concepto de interfaz inmutable ...
Conclusión:
The abc.ABCMeta IS "pythonic" interface in python