Pythonicなダックタイピング(DuckTyping)

概要

もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルである
という説明がよく使われますが、

一言で言うと、「継承してなくてもサブクラスとして見なすための仕組み」といった感じです。

以下、実際のコードで示します。

Pythonicなダックタイピング

例題

  • Hogeクラス(抽象クラス), SubHogeクラス, Hugaクラスを考えます。
  • Hogeクラスを継承していなくても、hogeメソッドとhugaメソッドを所持していればHogeクラスの子クラスとみなします(ダックタイピング)

以下、実際のコードで説明します。

サンプルコード

import abc


def main():
    subhoge_instance = SubHoge()
    print(isinstance(subhoge_instance, Hoge))  # => True
    print(issubclass(SubHoge, Hoge))           # => True

    huga_instance = Huga()
    print(issubclass(Huga, Hoge))              # => False
    print(isinstance(huga_instance, Hoge))     # => False


class Hoge(metaclass=abc.ABCMeta):
    @classmethod
    def __subclasshook__(Class, Subclass):
        print("subclasshook is called")
        attributes = []

        for cls in Subclass.__mro__:
            attributes.extend(cls.__dict__.keys())

        methods = ("hoge", "huga")
        if all(method in attributes for method in methods):
            return True
        return NotImplemented


class SubHoge:
    def hoge():
        pass

    def huga():
        pass


class Huga:
    def piyo():
        pass


if __name__ == "__main__":
    main()

出力

True True False False

確かに、hogeとhugaメソッドを実装しているSubHogeクラスはHogeクラスの子クラスとみなし、
そうでないHugaクラスはHogeクラスの子クラスとみなされていないことがわかると思います。

説明

Subclass.__mro__

Pythonの組み込み型です。
ざっくり言うと、そのクラス自身と親クラス(基底クラス)を配列として返します。 さっくり確認したい場合はprint(SubHoge.__mro__)と打つとSubclassおよびSubclassの親クラスを確認できます。

cls.__dict__

こちらもPythonの組み込み型です。
ざっくり言うと、そのクラスの持つメソッドなどを返してくれます。
さっくり確認したい場合はprint(SubHoge.__dict__.keys())と打つとSubclassの持つメソッドを確認できます。

参考

abcの公式ページ