0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Pythonの型アノテーションを使ってInterfaceクラスをそれっぽく扱ってみる

Posted at

概要

  • 依存関係逆転の原則(DIP)等の場面、つまり、抽象に依存した実装を行いたい場合に、Pythonでそれっぽく実装してみる
    • Python3.5から利用可能なTypeHintsの型アノテーションを利用してみる。
    • Pythonは言語機能としてInterfaceがないため、abcライブラリを使う。
    • 型アノテーションは型が違っていても怒らないので、あくまでそれっぽく書くというだけ。。。

前提

  • 環境
    • python: v.3.6.8

作ってみるもの

  • animalモジュール
    • AnimalというInterfaceクラスと、それを継承したCatクラスを実装する。
    • Animalクラスはcry()methodを持つ。
    • Catなどの具象クラスでは、cry()methodにて、それぞれの具象化された動物泣き声をprintする。というポリモーフィズムを説明する際に出てくるヤツです。
    • Dogクラスも用意するが、このクラスはAnimalを継承しない。
  • myclassモジュール
    • Animalの具象クラスを受け取り、cry()methodを叩くMyClassクラスを実装する。ここでMyClassでは
      1. Catクラスに依存しないようにしたい。(具象クラスの修正や、別の具象クラスが渡ってきても影響を受けないようにしたい
      2. AnimalというInterfaceクラスを継承していることは知っておきたい
  • mainモジュール
    • CatDogクラスを具象化し、MyClassのmethodに渡す。

animalモジュールを用意

Interfaceクラスを用意

  • abcライブラリを使って、継承クラスにてInterfaceクラスを作ってみる
class Animal(object, metaclass=abc.ABCMeta):
    """動物を表すInterfaceクラス。
    """
    @abc.abstractmethod
    def cry(self):
        """動物の鳴き声のInterface
        """
        pass # 具体的な実装はなし

具象クラスを用意

  • Animalクラスを継承して具象クラスを作ってみる
class Cat(Animal):
    """猫を表すクラス
    Args:
        Animal: インターフェースクラス
    """

    def cry(self):
        """猫の鳴き声の具象method
        """
        print('meow')
  • AnimalクラスとCatクラスの動きを確認してみる

    • Catクラスをインスタンス化して、cry()を実行してみる

cat = Cat()
cat.cry()
meow
````

- Interfaceクラスをそのままインスタンス化しようとすると怒られる

```shell
>>> animal = Animal()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Animal with abstract methods cry
```

- 具象クラスでmethodを実装しなかった場合も、怒られる

```shell

class Cat(Animal):
... """猫を表すクラス
... Args:
... Animal: インターフェースクラス
... """
... def nocry(self):
... """検証用method
... """
... print('???')
...
cat = Cat()
Traceback (most recent call last):
File "", line 1, in
TypeError: Can't instantiate abstract class Cat with abstract methods cry
```

Interfaceクラスと関係ないクラスを実装してみる

  • ぱっと見は似てるが、Animalクラスを継承して いない Dogクラスを作ってみる
class Dog(object):
    """犬を表すクラス。インターフェースは継承してない
    """
    def cry(self):
        """犬の鳴き声の具象method
        """
        print('bow wow')

myclassモジュールを用意

具象クラスを受け取って、cry()methodを実行するクラスを用意する

  • 型アノテーションを使って、引数がAnimal Interfaceを継承したクラスであることを明示
from animal import Animal

class MyClass(object):
    def run(self, animal: Animal) -> None:
        animal.cry()

mainモジュールを用意

  • 実際の具象クラスをインスタンス化し、処理はMyClassに委譲するロジックを用意
from animal import Cat, Dog
from myclass import MyClass

myclass = MyClass()
cat = Cat()
dog = Dog()

myclass.run(cat)
myclass.run(dog)
  • 結果は
$python main.py 
meow
bow wow
  • やっぱり犬も吠えてるね(´・ω・`)

考察

  • 型アノテーションはあくまで注釈なので、Interfaceクラスの派生クラスでなくても、動いてしまう
  • とりあえず、それっぽいことはできてる(意味があるかはおいておいて)
    • 簡単な実験のメモ程度の情報だけど何かしらの参考になれば幸いです。。。
    • 「複数人で実装するときに、Interfaceクラスに注目してね」くらいの意味は与えられるのかな?
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?