抽象基本クラスを使用したインターフェースの実装は、最新のPython 3でははるかに簡単であり、プラグイン拡張のインターフェースコントラクトとしての目的を果たします。
インターフェース/抽象基本クラスを作成します。
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)
通常のサブクラスを作成し、すべての抽象メソッドをオーバーライドします。
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)
オプションで、のように抽象メソッドに共通の実装を持たせ、上記のようにサブクラスcreate_sale_invoice()
でsuper()
明示的に呼び出すことができます。
すべての抽象メソッドを実装していないサブクラスのインスタンス化は失敗します。
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
また、対応するアノテーションを次のように組み合わせることで、抽象プロパティ、静的メソッド、およびクラスメソッドを持つことができます。 @abstractmethod
ます。
抽象基本クラスは、プラグインベースのシステムを実装するのに最適です。クラスのインポートされたすべてのサブクラスにはを介してアクセスできる__subclasses__()
ため、プラグインディレクトリからすべてのクラスをロードしimportlib.import_module()
、それらが基本クラスをサブクラス化する場合は、それらを介して直接アクセスでき、すべてのクラス__subclasses__()
に対してインターフェイスコントラクトが適用されていることを確認できます。インスタンス化中にそれら。
AccountingSystem
上記の例のプラグイン読み込み実装は次のとおりです。
...
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
次に、AccountingSystem
クラスを通じて会計システムプラグインオブジェクトにアクセスできます。
>>> accountingsystem = AccountingSystem.instance()
(このPyMOTW-3投稿に触発されました。)