clases - como hacer una clase abstracta en python
Diferencia entre clase abstracta e interfaz en Python (6)
¿Cuál es la diferencia entre la clase abstracta y la 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.
Usando una clase base abstracta
Por ejemplo, digamos que queremos usar una de las clases base abstractas del módulo de collections
:
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, debemos implementar al menos __contains__
, __iter__
y __len__
. Usemos este ejemplo de implementación de la documentation :
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
Implementación: Creación de una clase base abstracta
Podemos crear nuestra propia Clase Base Abstracta estableciendo la metaclase en abc.ABCMeta
y usando el decorador de métodos abc.abstractmethod
en métodos relevantes. La metaclase agregará las funciones decoradas al atributo __abstractmethods__
, impidiendo la __abstractmethods__
instancias hasta que se definan.
import abc
Por ejemplo, "effable" se define como algo que se puede expresar en palabras. Digamos que queríamos 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
y tratar de 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 del resumen:
>>> 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á fuera del alcance de esta pregunta. Sin embargo, los otros métodos demostrados aquí tendrían que adaptar este método utilizando el módulo abc
para hacerlo.
Conclusión
Hemos demostrado que la creación de una clase base abstracta define interfaces para objetos personalizados en Python.
¿Cuál es la diferencia entre la clase abstracta y la interfaz en Python?
En general, las interfaces se usan solo en idiomas que usan el modelo de clase de herencia simple. En estos lenguajes de herencia única, las interfaces se usan normalmente si alguna clase puede 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 idiomas que admiten el modelo de herencia múltiple tienden a usar solo clases o clases base abstractas y no interfaces. Como Python admite herencia múltiple, no usa interfaces y querría usar clases base o clases base abstractas.
En una forma más básica de explicar: una interfaz es algo así como una bandeja para muffins 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 es estrictamente vacío.
Por qué diferenciar: no hay mucha diferencia práctica en Python, pero en el 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ás trabajando con programadores de Java que están acostumbrados al término.
Lo que verás 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 cuestión de peinado cuando tienes que escribir pato.
Java usa interfaces porque no tiene herencia múltiple.
Como Python tiene herencia múltiple, también puedes 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 clase de superclase abstracta con mixins para crear subclases concretas que son desunidas.
Python realmente no tiene ninguno de los dos conceptos.
Utiliza la escritura de pato, que elimina la necesidad de interfaces (al menos para la computadora :-))
Python <= 2.5: las clases base obviamente existen, pero no hay una forma explícita de marcar un método como ''virtual puro'', por lo que la clase no es realmente abstracta.
Python> = 2.6: exist 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 característica está ahí. En la mayoría de los casos, probablemente sea mejor usar la escritura de pato desde el lado del cliente que utiliza.
Python> = 2.6 tiene clases base abstractas .
Las clases base abstractas (ABCs abreviadas) complementan la tipificación duck al proporcionar una manera de definir interfaces cuando otras técnicas como hasattr () serían torpes. Python viene con muchos ABCs incorporados 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). Puedes crear tu propio ABC con el módulo abc.
También está el módulo de interfaz Zope , que es utilizado por proyectos fuera de zope, como retorcido. No estoy realmente familiarizado con esto, pero hay una página wiki here 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).