24
16

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.

ニフティグループAdvent Calendar 2019

Day 14

PythonでProtocolを使って静的ダック・タイピング

Last updated at Posted at 2019-12-15

この記事はニフティグループ 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はキチンとダックタイピングされていますが、Booksoundが実装されておらず、Catsoundの戻り値が誤って実装されています。

test.py
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を使ったほうがいいかもしれません。

24
16
0

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
24
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?