¿Cómo implemento interfaces en Python?
public interface IInterface
{
void show();
}
public class MyClass : IInterface
{
#region IInterface Members
public void show()
{
Console.WriteLine("Hello World!");
}
#endregion
}
¿Cómo implemento el equivalente en Python de 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!'
¿¿Es esta una buena idea?? Por favor dé ejemplos en sus respuestas.
Como lo mencionan otros aquí:
Las interfaces no son necesarias en Python. Esto se debe a que Python tiene herencia múltiple adecuada y también ducktyping, lo que significa que los lugares donde debes tener interfaces en Java, no es necesario que las tengas en Python.
Dicho esto, todavía existen varios usos para las interfaces. Algunos de ellos están cubiertos por las clases base abstractas de Python, introducidas en Python 2.6. Son útiles si desea crear clases base de las que no se puedan crear instancias, pero que proporcionen una interfaz específica o parte de una implementación.
Otro uso es si de alguna manera desea especificar que un objeto implementa una interfaz específica, y también puede usar ABC para eso al crear subclases de ellos. Otra forma es zope.interface, un módulo que forma parte de Zope Component Architecture, un marco de componentes realmente genial. Aquí no crea subclases de las interfaces, sino que marca clases (o incluso instancias) como implementando una interfaz. Esto también se puede utilizar para buscar componentes en un registro de componentes. ¡Super guay!
La implementación de interfaces con clases base abstractas es mucho más sencilla en el Python 3 moderno y sirven como contrato de interfaz para extensiones de complementos.
Cree la interfaz/clase base abstracta:
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)
Cree una subclase normal y anule todos los métodos abstractos:
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, puede tener una implementación común en los métodos abstractos como en create_sale_invoice()
, llamándolo super()
explícitamente en la subclase como se indica arriba.
La creación de instancias de una subclase que no implementa todos los métodos abstractos falla:
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
También puede tener propiedades abstractas, métodos estáticos y de clase combinando las anotaciones correspondientes con @abstractmethod
.
Las clases base abstractas son excelentes para implementar sistemas basados en complementos. Se puede acceder a todas las subclases importadas de una clase a través de __subclasses__()
, por lo que si carga todas las clases desde un directorio de complementos importlib.import_module()
y si subclasifican la clase base, tendrá acceso directo a ellas a través de __subclasses__()
y puede estar seguro de que el contrato de interfaz se aplica para todos. ellos durante la creación de instancias.
Aquí está la implementación de carga del complemento para el AccountingSystem
ejemplo anterior:
...
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
Luego puede acceder al objeto del complemento del sistema de contabilidad a través de la AccountingSystem
clase:
>>> accountingsystem = AccountingSystem.instance()
(Inspirado en esta publicación de PyMOTW-3 ).