2021年5月19日水曜日

Python で型入門

Python で型入門

概要

Python にはタイプヒントという機能がありこれを使ってある程度の型指定を行うことができます 今回は型の指定方法や静的チェックを試してみました

環境

  • macOS 11.3.1
  • Python 3.8.7
    • mypy 0.812

Type Hints

あくまでもヒントとして型を記述することができます 例えば以下のような感じで引数や返り値に型を指定することができます

class User():
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def change_name(self, name: str):
        self.name = name

    def add_age(self, age: int) -> int:
        return self.age + age

    def show_profile(self):
        print(self.name)
        print(self.age)

user = User("hawk", 10)
user.change_name("snowlog")
user.add_age(2)
user.show_profile()

しかしあくまでもヒントでしかないので定義した型以外の型でも実行できてしまいます

user = User(-1, 10)
user.change_name(-2)
print(user.add_age("error"))
Traceback (most recent call last):
  File "test.py", line 23, in <module>
    print(user.add_age("error"))
  File "test.py", line 10, in add_age
    return self.age + age
TypeError: unsupported operand type(s) for +: 'int' and 'str'

事前に型チェックする

mypy というツールを使うと事前に型チェックできるため上記のように型が間違っている場合にエラーとなっている箇所を見つけることができます

  • pip3 install mypy

これで先程のコードに対してチェックすると型が違っているというエラーを確認できます

test.py:21: error: Argument 1 to "User" has incompatible type "int"; expected "str"
test.py:22: error: Argument 1 to "change_name" of "User" has incompatible type "int"; expected "str"
test.py:23: error: Argument 1 to "add_age" of "User" has incompatible type "str"; expected "int"
Found 3 errors in 1 file (checked 1 source file)

型エイリアスを使う

プリミティブな型を独自の型名として使うことができます あくまでも元の型として扱われます コードのリファクタなどに使えます

my_int = int

class User():
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def change_name(self, name: str):
        self.name = name

    def add_age(self, age: int) -> my_int:
        return self.age + age

    def show_profile(self):
        print(self.name)
        print(self.age)

user = User("hawk", 10)
print(user.add_age(2).__class__)

NewType を使う

新しい型を定義します エイリアスとは異なり実際に型チェックに引っかかります

from typing import NewType

my_int = int
Name = NewType('Name', str)
Age = NewType('Age', int)

class User():
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def change_name(self, name: Name):
        self.name = name

    def add_age(self, age: Age) -> my_int:
        return self.age + age

    def show_profile(self):
        print(self.name)
        print(self.age)

user = User("hawk", 10)
name = Name("snowlog")
user.change_name(name)
age = Age(2)
print(user.add_age(age))
user.show_profile()

もし以下のようにちゃんと型を生成してから指定していない場合は型チェックでエラーになります

user = User("hawk", 10)
# name = Name("snowlog")
user.change_name("snowlog")
age = Age(2)
print(user.add_age(age))
user.show_profile()
test2.py:24: error: Argument 1 to "change_name" of "User" has incompatible type "str"; expected "Name"
Found 1 error in 1 file (checked 1 source file)

その他

ジェネリクス、Any 型、Optional 型などもあるのでかなり正確に型付けを行うことができます

参考サイト

0 件のコメント:

コメントを投稿