ダックタイピングとは、オブジェクトがクラス階層への属し方ではなく、その振る舞いによって評価されるというPythonの基本原則です。
この用語は、"何かがアヒルのように見え、アヒルのように泳ぎ、アヒルのように鳴くなら、それはアヒルです"ということわざに由来します。Pythonでは、オブジェクトの振る舞い(そのインターフェース)が、所属するクラスよりも重要です。これが「ダックタイピング」という原則を実現します—振る舞いによる型付け(構造的型付け)。
ダックタイピングは最大の柔軟性を提供するように思えます。しかし、これにより隠れたバグの数が増えます:オブジェクトが必要なインターフェースをサポートしていない場合、プログラムは実行時にのみ「壊れる」のです。
型チェック(isinstanceやtypeを介して)を行う代わりに、オブジェクトに必要なメソッドを呼び出そうとする関数を書くべきです。それが成功するなら、オブジェクトはそれをサポートしていると考えます。最悪の場合は、AttributeErrorやTypeErrorをキャッチして、予期しないオブジェクトを処理します。
例:
def quack_and_walk(duck): duck.quack() duck.walk() class Robot: def quack(self): print("I can quack!") def walk(self): print("I am walking") quack_and_walk(Robot()) # 全てが正常に動作します!
主な特徴:
Pythonでisinstanceを用いて型を確認することはでき、これはダックタイピングにおいて正しいと言えますか?
いいえ。ダックタイピングは、厳格な型チェックに反するものです。オブジェクトの血統ではなく、その振る舞いに基づいて操作する方が正確です。
抽象基底クラス(ABC)を使ってダックタイピングを実現できますか?
部分的に。抽象クラスは静的な構造の要素を導入します。ダックタイピングは親族を宣言する必要がなく—必要なメソッドを実装するだけで済むのです。しかし、Python 3.8以降、typing.Protocolモジュールが登場し、構造的型付けに近づきました。
ダックタイピングはマジックメソッド(len, __getitem__など)と一緒に機能しますか?
はい。オブジェクトが必要なメソッド(len)を実装している場合、それをlen(obj)のような関数に渡すことができ、これはダックタイピングとして機能します。
開発者がrun()メソッドを持つすべてのオブジェクトを受け入れ、チェックなしで関数を書く。コード内にこのメソッドを持たないオブジェクトが登場し、エラーは実行時にのみ発生します。
長所:
短所:
ダックタイピングを使用しますが、try/except文とインターフェースのドキュメンテーションを含めます:
def run_task(obj): try: obj.run() except AttributeError: print("オブジェクトはタスクの実行をサポートしていません!")
長所:
短所: