Go の Interface を Python でどう書く¶

2021-12-29 Python 駿河 Unagi.py 2021紅白LT合戦

おまえだれよ? 中田順也です。 WEB アプリケーション開発の仕事をしています。 だいたい Python、たまに Go です。

Interface とは¶

GoのインターフェースはJavaのインターフェースと同じくメソッドの型だけを記述した型となります。 インターフェイスはメソッドのまとまりなので、インターフェイスを通してオブジェクトの振る舞いを定義することが可能です。 https://dev.classmethod.jp/articles/golang-6/

どんなときにつかいたい?¶

例えば、本番はクラウドストレージを使うが、ローカル開発環境では開発マシンのファイルを使いたいとき。

In [1]:
class CloudStorageFileReader:
    """本番用に Cloud Storage のファイルを読む。"""
    def read_file(self) -> str:
        """決まったパスのテキストファイルの中身を返す。"""
        return "I'm in Cloud Storage."
    
class LocalFileReader:
    """開発用にローカルファイルを読む。"""
    def read_file(self) -> str:
        """決まったパスのテキストファイルの中身を返す。"""
        return "I'm in localhost."
In [2]:
def read_file() -> str:
    """ファイルを読んでテキストを返す。"""
    if IS_LOCAL:
        reader = LocalFileReader()
    else:
        reader = CloudStorageFileReader()
    return reader.read_file()
In [3]:
IS_LOCAL = False
text = read_file()
print(text)
I'm in Cloud Storage.
In [4]:
IS_LOCAL = True
text = read_file()
print(text)
I'm in localhost.

Interface 要らない¶

ドキュメントを書いておけば良い。

きちんと書けるか?読まれるか?

ドキュメントでしかないから IDE で補完などの機能が使えない。

Mypy してみよう¶

In [5]:
%reload_ext mypy_ipython
%mypy
note: In function "read_file":
            reader = CloudStorageFileReader()
error: Incompatible types in assignment (expression has type "CloudStorageFileReader", variable has type "LocalFileReader")
Found 1 error in 1 file (checked 1 source file)
Type checking failed

これでどうだ¶

In [6]:
import typing

def read_file() -> str:
    """ファイルを読んでテキストを返す。"""
    
    reader: typing.Union[LocalFileReader, CloudStorageFileReader]
        
    if IS_LOCAL:
        reader = LocalFileReader()
    else:
        reader = CloudStorageFileReader()
    return reader.read_file()
In [7]:
%reload_ext mypy_ipython
%mypy
Success: no issues found in 1 source file
Type checking successful

typing.Protocol¶

Python 3.8 から。

In [8]:
import typing

@typing.runtime_checkable  # isinstance を使う場合は要る。
class FileReader(typing.Protocol):
    def read_file(self) -> str:
        pass
In [9]:
def read_file() -> str:
    """ファイルを読んでテキストを返す。"""

    reader: FileReader

    if IS_LOCAL:
        reader = LocalFileReader()
    else:
        reader = CloudStorageFileReader()
    return reader.read_file()

もういちど Mypy¶

In [10]:
%reload_ext mypy_ipython
%mypy
Success: no issues found in 1 source file
Type checking successful

こんなときつかえそう¶

  • xlsx ファイルを読み書きしたい。Excel の Web アプリからも読み書きしたい。
  • データベースが変わる可能性がある。Datastore から Firestore へ。
  • データベースまわりはそれが得意な人に任せて、ロジックは別の人が書く。

おしまい¶

詳しいことは

  • https://docs.python.org/3/library/typing.html#typing.Protocol
  • https://www.python.org/dev/peps/pep-0544/
In [ ]: