duck typing(ダックタイピング) とは
duck typing(ダックタイピング) とは、クラスが別でも、同じ名前のメソッドを使用することができるスタイル。共通のメソッドを持ちさえすれば、継承を使わなくても、どのようなオブジェクトでも、これらのメソッドを実行できる。「型」より「ふるまい」に注目したスタイルである。
Luciano Ramalho 著の、Fluent Python には次のように書かれている。
ダックタイピングは、クラスやインターフェースの明示的な宣言にかかわりなく、適切なメソッドを実装するすべてのオブジェクトで関数が動作するポリモーフィズムの一形態。
ダックタイピングとは無理に訳せば「アヒル流型付け」。オブジェクトは定義された型によって規定されるのではなくて、そのメソッドが何をするかで決まるという考え方を指します。100% アヒルであってもなくても(その型でなくても)、「ガーガー」鳴けばよいということです。そういった機能さえあればよい。
例えば、異なる 3 種類の says() メソッドが 3 つのクラスの異なる動作を行うのがポリモーフィズム(多態性)だが、ダックタイピングは、次のコードのように同じメソッドを持てば、どんなオブジェクトでもメソッドの同じ処理を実行できるようになる。
# 1. 血縁関係のある家族クラスを作る
class Parent:
def __init__(self, words):
self.words = words
def says(self):
return self.words
class Child(Parent):
def says(self):
return self.words
class Grandchild(Parent):
def says(self):
return self.words
parent = Parent('親')
child = Child('子供')
grandchild = Grandchild('孫')
# 2. 上の家族とは血縁関係のない友人クラスを作る
class Frend:
def says(self):
return '友人'
frend = Frend()
# 3. says() を実行する関数を作る
def who_says(obj):
print('私は、', obj.says(), 'です。')
# 4. 血縁に関係なく同じふるまいをする
who_says(parent)
#私は、 親 です。
who_says(child)
#私は、 子供 です。
who_says(grandchild)
#私は、 孫 です。
who_says(frend)
#私は、 友人 です。
コードの詳説
- 最初に、 Parent クラスを継承したサブクラスを 2 つ作り、インスタンス化している。
- 次に、Parent クラスとは無関係の Frend クラスを作る。
- そして、共通のメソッドである says() を実行する関数を作り呼び出すと、Parent を継承したオブジェクトと、無関係のクラス Frend のオブジェクトのどちらも同じ動きが実行される。
このように共通のメソッド says() を持ちさえすれば、継承を利用しなくても様々なオブジェクトで同じ操作を切り替えることができる。つまり、ダックタイピングは「型」より「ふるまい」に注目したスタイルなのである。
これらのことを理解すると、公式ドキュメントに書かれていることも理解できる。
あるオブジェクトが正しいインターフェースを持っているかを決定するのにオブジェクトの型を見ないプログラミングスタイルです。代わりに、単純にオブジェクトのメソッドや属性が呼ばれたり使われたりします。
(「アヒルのように見えて、アヒルのように鳴けば、それはアヒルである。」)
インターフェースを型より重視することで、上手くデザインされたコードは、ポリモーフィックな代替を許して柔軟性を向上させます。ダックタイピングは type() や isinstance() による判定を避けます。 (ただし、ダックタイピングを 抽象基底クラス で補完することもできます。) その代わり、典型的に hasattr() 判定や EAFP プログラミングを利用します。