この記事はニフティグループ Advent Calendar 2019の14日目の記事です。
昨日は@mito1296さんの「Terraformで立てたec2インスタンスをAnsibleで管理してみる」でした。
同様の問題に以前悩まされたことがあったのでタグ付けは非常に綺麗な解決方法だなと感心しました。
機会があれば、うちのチームでも導入したいですね!!
TD;LD
Pythonは型安全で無いので大きめのプロジェクトになると心理的な安全が担保できないので敬遠していました。
しかし、Python3.5以降typingが導入されmypyと組み合わせることで、少し大きめのプロジェクトでもコーティングする際に心理的な安全を得ることができるようになったので中規模の開発に採用できるようになりました。
今回は、python3.8からtyping.Protocol
が正式に追加されたようなので紹介してみます。
typing.Protocolとは
詳しくは、以下を見てください。
https://docs.python.org/ja/3/library/typing.html#typing.Protocol
今までPythonでダック・タイピングをする際はabc
を使っていたのですが。場合によってはtyping.Protocol
ほうが自然に実装できそうです。
https://qiita.com/kaneshin/items/269bc5f156d86f8a91c4
こちらの記事でも紹介されているダックタイピングの問題を解消しているのかな?と思います。
試してみる
Protocolの使い方は簡単で、抽象クラスとなるものに継承させればOKです。
以下の例ですとAnimal
のクラスに該当します。
Animal
の具象クラスにはstr
を返すsound
メソッドが実装されることを期待しています。
Dog
はキチンとダックタイピングされていますが、Book
はsound
が実装されておらず、Cat
はsound
の戻り値が誤って実装されています。
from typing import Protocol
class Animal(Protocol):
def sound(self) -> str:
...
class Dog():
"soundが実装されている"
def sound(self) -> str:
return "Bow-wow"
class Book():
"soundが実装されていない"
def read(self) -> str:
return "hogeeee"
class Cat():
"soundは実装されているが、戻り値が違う"
def sound(self) -> None:
print("Meow")
def func(animal: Animal):
animal.sound()
func(Dog())
func(Book())
func(Cat())
このコードをmypyで解析すると、きちんと解析されてErrorが出ていることが分かります。
Cat
に至っては、詳細なエラーが出ているので誤りが非常に見つけやすくなっています。
$ python -m mypy test.py
test.py:35: error: Argument 1 to "func" has incompatible type "Book"; expected "Animal"
test.py:36: error: Argument 1 to "func" has incompatible type "Cat"; expected "Animal"
test.py:36: note: Following member(s) of "Cat" have conflicts:
test.py:36: note: Expected:
test.py:36: note: def sound(self) -> None
test.py:36: note: Got:
test.py:36: note: def sound(self) -> str
Found 2 errors in 1 file (checked 1 source file)
最後に
typing.Protocol
について、簡単ですが紹介してみました。
abc
でも似たようなことができるのですが、具象化クラスが継承する必要やアノテーションのつけ方が直感的ではないので場合によってはtyping.Protocol
を使ったほうがいいかもしれません。