プロジェクト

全般

プロフィール

操作

第5章 型ヒント

5.1 型ヒント

  • 前提: Python は動的型付け言語なので、型解決は実行時に行われ、コーディング時には型付けしない
  • ただし、型ヒントをつけることで実行前に静的型チェックを行ったり、IDEでの補助機能への情報を与えることが可能となる
  • 型ヒントの書き方
    • 基本的な型
      # 変数への型付け
      name: str = "smith"
      age: int = 41
      
      # 関数での型付け
      # 引数は変数と同様
      # 引数にデフォルト値ありの場合は  "変数名: 型名 = 値"
      # 返り値は "-> 型名"
      def func(name: str, age: int = 1) -> int:
          if(name == "smith"):
              return age
          else:
              return 0
      
    • コンテナ型
      # 変数への型付け
      listobj: list = [123, "abc", True]
      setobj: set = {1, 2, 4, 8, 16}
      dictobj: dict = {"one": 1, "two": 2}
      tupleobj: tuple = (123, "abc", True)
      
      # 変数への型付け (要素の型)
      listobj: list[str] = ["123", "abc", "True"]
      setobj: set[int] = {1, 2, 4, 8, 16}
      dictobj: dict[str, int] = {"one": 1, "two": 2}
      tupleobj: tuple[int, str, bool] = (123, "abc", True)  # tuple は全要素の型を指定
      tupleobj_err: tuple[str] = ("123", "abc")  # エラー
      tupleobj_same: tuple[str, ...] = ("123", "abc", "True")  # 全部同じ型ならこの書き方が可能
      
    • ユーザ定義クラス
      • 他のオブジェクト指向言語と同様に、定義済みクラスを型として扱える
        # book_dataclass.py
        from dataclasses import dataclass
        
        @dataclass
        class Book:
            name: str
            author: str
            price: int
        
        # book_typehint.py
        from operator import attrgetter
        from book_dataclass import Book
        
        def lowest_price_book(book_list: list[Book]) -> Book:
            return sorted(book_list, key=attrgetter("price"))[0]
        
        books: list[Book] = [
            Book("ハッカーガイド", "terapyon", 2992),
            Book("ゼロから", "tekanori", 3200),
            Book("スタートブック", "shingo", 2750)
        ]
        
        value_book: Book = lowest_price_book(books)
        print(value_book)
        
        > python .\book_typehint.py
        Book(name='スタートブック', author='shingo', price=2750)
        
  • typing モジュールによる型ヒント
    • 特殊な型付けが可能

      説明
      Union 複数指定し、いずれかの型
      Optional 指定の型か、None
      Literal 指定したいずれかの値
      Any 任意の型
      TypeValue TypeVar 型変数の定義
      TypedDict 辞書のキー(str)と、値の型を指定
      from typing import Union, Optional
      
      uniontype: Union[int, str] = 10
      uniontype = "ten"
      
      opttype: Optional[bool] = True
      opttype = None
      
      # Python3.10 以降の新しい書き方 (typing モジュールインポート不要)
      uniontype2: int | str = 10
      uniontype2 = "ten"
      
      opttype2: bool | None = True
      opttype2 = None
      
      from typing import Literal, Any, TypeVar, TypedDict
      
      littype: Literal["Shizuoka", "Nagoya"] = "Shizuoka"
      littype = "Nagoya"
      littype = "Tokyo"  # NG
      
      user_input: Any = utilvalud(args)  # 型付け無しの関数返り値を受け取る
      
      # TypeVar
      T = TypeVar("T")  # mypy で試したところ、変数と TypeVar() の第1引数は揃えないとダメらしい
      
      def reflect(arg: T) -> T:
          return arg
      
      # TypedDict
      class BookDict(TypedDict):
          name: str
          author: str
          price: int
      
      fav_book: BookDict = {"name": "スタートブック", "author": "shingo", "price": 2750}
      

5.2 静的型チェックを行う - mypy (※試験範囲外)

型ヒントを用いて静的型チェックを行うツール mypy

  • 型ヒントに違反している内容を検知してエラーを出力するツール
    • エラーの例
      # mypy_check.py
      
      # 変数の型NG
      name: str = 123
      age: int = "42"
      favorite: list = {"study": "AWS", "food": "Curry"}
      
      # 関数の返り値型NG
      def greeting(name: str) -> int:
          return f"Hi, {name}"
      
      # 関数の引数型NG
      greeting(123)
      
      > mypy .\mypy_check.py
      mypy_check.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str")  [assignment]
      mypy_check.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int")  [assignment]
      mypy_check.py:4: error: Incompatible types in assignment (expression has type "dict[str, str]", variable has type "list[Any]")  [assignment]
      mypy_check.py:8: error: Incompatible return value type (got "str", expected "int")  [return-value]
      mypy_check.py:11: error: Argument 1 to "greeting" has incompatible type "int"; expected "str"  [arg-type]
      Found 5 errors in 1 file (checked 1 source file)
      
    • 修正後
      name: str = "smith"
      age: int = 41
      favorite: list = ["AWS", "Curry"]
      
      def greeting(name: str) -> str:
          return f"Hi, {name}"
      
      greeting("Tatsuya")
      
      > mypy .\mypy_check_ok.py
      Success: no issues found in 1 source file
      
  • オプション指定
    • コマンドオプション、設定ファイルのどちらかで指定
      • ※コマンドオプションと設定ファイル内の記述が、ハイフンとアンダースコアで異なる
      • (例) コマンドオプション --disallow-any-generics
      • (例) 設定ファイル disallow_any_generics = True
    • 代表的なオプション
      オプション 説明
      disallow_any_generics コンテナーの要素の型が無いことを禁止
      disallow_untyped_defs 型が無い関数定義を禁止
      disallow_untyped_calls 「型が無い関数」の呼び出しを禁止
      warn_unused_ignores 「# type: ignore」がある場合に警告
      warn_return_any 型が無い変数を返す場合に警告
      check_untyped_defs 型が無い関数の内部の型をチェック
    • 除外に関するオプション
      オプション 設定値 説明
      follow_imports normal,
      silent,
      skip,
      error
      インポートしたモジュールをチェックするルールの指定
      ignore_missing_imports bool インポートしたモジュールに関するエラーを抑制する(デフォルト False)
      exclude 正規表現 除外ファイル・ディレクトリを正規表現で指定
    • 設定ファイル
      • プロジェクトディレクトリ直下に配置
        • mypy.ini
        • .mypy.ini
        • pyproject.toml
        • setup.cfg
      • ユーザディレクトリ配置
        • $XDG_CONFIG_HOME/mypy/config
        • ~/.config/mypy/config
      • 書き方
        [mypy]
        follow_imports = silent
        disallow_any_generics = True
        warn_return_any = True
        
        • 特定モジュールでのみ有効化なオプションの設定は [mypy-モジュール名]セクションに記述
          [mypy-aiohttp.*]
          follow_imports = False
          
          [mypy-sqlalchemy.*]
          ignore_missing_imports = True
          
  • よくあるエラーと対処
    • 型ヒントを定義しているスタブファイル (*.pyi 形式の外部ファイル) が無い場合にエラーが出る場合がある
      • サードパーティのライブラリは、型定義リポジトリの typeshed にスタブファイルがあるかも
      • typeshed のスタブファイルインストールは mypy --install-types で行える
      • typeshed にスタブファイルが無い場合
        1. ライブラリのアップデート
        2. ライブラリのスタブファイルの確認
        3. ライブラリを mypy チェックから除外する
          import example  # type: ignore
          

Tatsuya ISHIGAKI さんが5ヶ月前に更新 · 2件の履歴