プロジェクト

全般

プロフィール

5章学習記録 » 履歴 » バージョン 2

Tatsuya ISHIGAKI, 2025/07/25 00:30

1 1 Tatsuya ISHIGAKI
# 第5章 型ヒント
2
3
## 5.1 型ヒント
4
- 前提: Python は動的型付け言語なので、型解決は実行時に行われ、コーディング時には型付けしない
5
- ただし、型ヒントをつけることで実行前に静的型チェックを行ったり、IDEでの補助機能への情報を与えることが可能となる
6
- 型ヒントの書き方
7
  - 基本的な型
8
    ```python
9
    # 変数への型付け
10
    name: str = "smith"
11
    age: int = 41
12
  
13
    # 関数での型付け
14
    # 引数は変数と同様
15
    # 引数にデフォルト値ありの場合は  "変数名: 型名 = 値"
16
    # 返り値は "-> 型名"
17
    def func(name: str, age: int = 1) -> int:
18
        if(name == "smith"):
19
            return age
20
        else:
21
            return 0
22
    ```
23
  - コンテナ型
24
    ```python
25
    # 変数への型付け
26
    listobj: list = [123, "abc", True]
27
    setobj: set = {1, 2, 4, 8, 16}
28
    dictobj: dict = {"one": 1, "two": 2}
29
    tupleobj: tuple = (123, "abc", True)
30
31
    # 変数への型付け (要素の型)
32
    listobj: list[str] = ["123", "abc", "True"]
33
    setobj: set[int] = {1, 2, 4, 8, 16}
34
    dictobj: dict[str, int] = {"one": 1, "two": 2}
35
    tupleobj: tuple[int, str, bool] = (123, "abc", True)  # tuple は全要素の型を指定
36
    tupleobj_err: tuple[str] = ("123", "abc")  # エラー
37
    tupleobj_same: tuple[str, ...] = ("123", "abc", "True")  # 全部同じ型ならこの書き方が可能
38
    ```
39
  - ユーザ定義クラス
40
    - 他のオブジェクト指向言語と同様に、定義済みクラスを型として扱える
41
      ```python
42
      # book_dataclass.py
43
      from dataclasses import dataclass
44
  
45
      @dataclass
46
      class Book:
47
          name: str
48
          author: str
49
          price: int
50
      ```
51
      ```python
52
      # book_typehint.py
53
      from operator import attrgetter
54
      from book_dataclass import Book
55
      
56
      def lowest_price_book(book_list: list[Book]) -> Book:
57
          return sorted(book_list, key=attrgetter("price"))[0]
58
      
59
      books: list[Book] = [
60
          Book("ハッカーガイド", "terapyon", 2992),
61
          Book("ゼロから", "tekanori", 3200),
62
          Book("スタートブック", "shingo", 2750)
63
      ]
64
      
65
      value_book: Book = lowest_price_book(books)
66
      print(value_book)
67
      ```
68
      ```powershell
69
      > python .\book_typehint.py
70
      Book(name='スタートブック', author='shingo', price=2750)
71
      ```
72
- typing モジュールによる型ヒント
73
  - 特殊な型付けが可能
74
75
    |型|説明|
76
    |:--|:--|
77
    |Union|複数指定し、いずれかの型|
78
    |Optional|指定の型か、None|
79
    |Literal|指定したいずれかの値|
80
    |Any|任意の型|
81 2 Tatsuya ISHIGAKI
    |~~TypeValue~~ TypeVar|型変数の定義|
82 1 Tatsuya ISHIGAKI
    |TypedDict|辞書のキー(str)と、値の型を指定|
83
84
    ```python
85
    from typing import Union, Optional
86
87
    uniontype: Union[int, str] = 10
88
    uniontype = "ten"
89
90
    opttype: Optional[bool] = True
91
    opttype = None
92
93
    # Python3.10 以降の新しい書き方 (typing モジュールインポート不要)
94
    uniontype2: int | str = 10
95
    uniontype2 = "ten"
96
97
    opttype2: bool | None = True
98
    opttype2 = None
99
    ```
100
    ```python
101
    from typing import Literal, Any, TypeVar, TypedDict
102
103
    littype: Literal["Shizuoka", "Nagoya"] = "Shizuoka"
104
    littype = "Nagoya"
105
    littype = "Tokyo"  # NG
106
107
    user_input: Any = utilvalud(args)  # 型付け無しの関数返り値を受け取る
108
109
    # TypeVar
110
    T = TypeVar("T")  # mypy で試したところ、変数と TypeVar() の第1引数は揃えないとダメらしい
111
112
    def reflect(arg: T) -> T:
113
        return arg
114
115
    # TypedDict
116
    class BookDict(TypedDict):
117
        name: str
118
        author: str
119
        price: int
120
121
    fav_book: BookDict = {"name": "スタートブック", "author": "shingo", "price": 2750}
122
    ```
123
124
## 5.2 静的型チェックを行う - mypy (※試験範囲外)
125
型ヒントを用いて静的型チェックを行うツール **mypy**
126
- 型ヒントに違反している内容を検知してエラーを出力するツール
127
  - エラーの例
128
    ```python
129
    # mypy_check.py
130
  
131
    # 変数の型NG
132
    name: str = 123
133
    age: int = "42"
134
    favorite: list = {"study": "AWS", "food": "Curry"}
135
    
136
    # 関数の返り値型NG
137
    def greeting(name: str) -> int:
138
        return f"Hi, {name}"
139
    
140
    # 関数の引数型NG
141
    greeting(123)
142
    ```
143
    ```powershell
144
    > mypy .\mypy_check.py
145
    mypy_check.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str")  [assignment]
146
    mypy_check.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int")  [assignment]
147
    mypy_check.py:4: error: Incompatible types in assignment (expression has type "dict[str, str]", variable has type "list[Any]")  [assignment]
148
    mypy_check.py:8: error: Incompatible return value type (got "str", expected "int")  [return-value]
149
    mypy_check.py:11: error: Argument 1 to "greeting" has incompatible type "int"; expected "str"  [arg-type]
150
    Found 5 errors in 1 file (checked 1 source file)
151
    ```
152
  - 修正後
153
    ```python
154
    name: str = "smith"
155
    age: int = 41
156
    favorite: list = ["AWS", "Curry"]
157
    
158
    def greeting(name: str) -> str:
159
        return f"Hi, {name}"
160
    
161
    greeting("Tatsuya")
162
    ```
163
    ```powershell
164
    > mypy .\mypy_check_ok.py
165
    Success: no issues found in 1 source file
166
    ```
167
- オプション指定
168
  - コマンドオプション、設定ファイルのどちらかで指定
169
    - ※コマンドオプションと設定ファイル内の記述が、ハイフンとアンダースコアで異なる
170
    - (例) コマンドオプション `--disallow-any-generics`
171
    - (例) 設定ファイル `disallow_any_generics = True`
172
  - 代表的なオプション
173
    |オプション|説明|
174
    |---|---|
175
    |disallow_any_generics|コンテナーの要素の型が無いことを禁止|
176
    |disallow_untyped_defs|型が無い関数定義を禁止|
177
    |disallow_untyped_calls|「型が無い関数」の呼び出しを禁止|
178
    |warn_unused_ignores|「# type: ignore」がある場合に警告|
179
    |warn_return_any|型が無い変数を返す場合に警告|
180
    |check_untyped_defs|型が無い関数の内部の型をチェック|
181
  - 除外に関するオプション
182
    |オプション|設定値|説明|
183
    |---|---|---|
184
    |follow_imports|normal, <br>silent, <br>skip, <br>error|インポートしたモジュールをチェックするルールの指定|
185
    |ignore_missing_imports|bool|インポートしたモジュールに関するエラーを抑制する(デフォルト False)|
186
    |exclude|正規表現|除外ファイル・ディレクトリを正規表現で指定|
187
  - 設定ファイル
188
    - プロジェクトディレクトリ直下に配置
189
      - `mypy.ini`
190
      - `.mypy.ini`
191
      - `pyproject.toml`
192
      - `setup.cfg`
193
    - ユーザディレクトリ配置
194
      - `$XDG_CONFIG_HOME/mypy/config`
195
      - `~/.config/mypy/config`
196
    - 書き方
197
      ```
198
      [mypy]
199
      follow_imports = silent
200
      disallow_any_generics = True
201
      warn_return_any = True
202
      ```
203
      - 特定モジュールでのみ有効化なオプションの設定は `[mypy-モジュール名]`セクションに記述
204
        ```
205
        [mypy-aiohttp.*]
206
        follow_imports = False
207
208
        [mypy-sqlalchemy.*]
209
        ignore_missing_imports = True
210
        ```
211
- よくあるエラーと対処
212
  - 型ヒントを定義しているスタブファイル (`*.pyi` 形式の外部ファイル) が無い場合にエラーが出る場合がある
213
    - サードパーティのライブラリは、型定義リポジトリの [**typeshed**](https://github.com/python/typeshed) にスタブファイルがあるかも
214
    - **typeshed** のスタブファイルインストールは `mypy --install-types` で行える
215
    - **typeshed** にスタブファイルが無い場合
216
      1. ライブラリのアップデート
217
      2. ライブラリのスタブファイルの確認
218
      3. ライブラリを mypy チェックから除外する
219
         ```python
220
         import example  # type: ignore
221
         ```