コンテンツにスキップ

Pythonの型の紹介

🌐 Translation by AI and humans

This translation was made by AI guided by humans. 🤝

It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. 🤖

You can improve this translation by helping us guide the AI LLM better.

English version

Pythonではオプションの「型ヒント」(「型アノテーション」とも呼ばれます)がサポートされています。

これらの 「型ヒント」 またはアノテーションは、変数のを宣言できる特別な構文です。

変数に型を宣言することで、エディターやツールがより良いサポートを提供できます。

これはPythonの型ヒントについての クイックチュートリアル/リフレッシュ にすぎません。FastAPI で使用するために必要な最低限のことだけをカバーしています。...実際には本当に少ないです。

FastAPI はすべてこれらの型ヒントに基づいており、多くの強みと利点を与えてくれます。

しかし、たとえ FastAPI をまったく使用しない場合でも、それらについて少し学ぶことで利点を得られます。

備考

もしあなたがPythonの専門家で、すでに型ヒントについてすべて知っているのであれば、次の章まで読み飛ばしてください。

動機

簡単な例から始めてみましょう:

def get_full_name(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

このプログラムを呼び出すと、以下が出力されます:

John Doe

この関数は以下のようなことを行います:

  • first_namelast_nameを取得します。
  • title()を用いて、それぞれの最初の文字を大文字に変換します。
  • 真ん中にスペースを入れて連結します。
def get_full_name(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

編集

これはとても簡単なプログラムです。

しかし、今、あなたがそれを一から書いていたと想像してみてください。

パラメータの準備ができていたら、そのとき、関数の定義を始めていたことでしょう...

しかし、そうすると「最初の文字を大文字に変換するあのメソッド」を呼び出す必要があります。

それはupperでしたか?uppercaseでしたか?first_uppercasecapitalize

そして、古くからプログラマーの友人であるエディタで自動補完を試してみます。

関数の最初のパラメータfirst_nameを入力し、ドット(.)を入力してから、Ctrl+Spaceを押すと補完が実行されます。

しかし、悲しいことに、これはなんの役にも立ちません:

型の追加

先ほどのコードから一行変更してみましょう。

関数のパラメータである次の断片を、以下から:

    first_name, last_name

以下へ変更します:

    first_name: str, last_name: str

これだけです。

それが「型ヒント」です:

def get_full_name(first_name: str, last_name: str):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

これは、以下のようにデフォルト値を宣言するのと同じではありません:

    first_name="john", last_name="doe"

それとは別物です。

イコール(=)ではなく、コロン(:)を使用します。

そして、通常、型ヒントを追加しても、それらがない状態と起こることは何も変わりません。

しかし今、あなたが再びその関数を作成している最中に、型ヒントを使っていると想像してみてください。

同じタイミングでCtrl+Spaceで自動補完を実行すると、以下のようになります:

これであれば、あなたは「ベルを鳴らす」ものを見つけるまで、オプションを見てスクロールできます:

より強い動機

この関数を見てください。すでに型ヒントを持っています:

def get_name_with_age(name: str, age: int):
    name_with_age = name + " is this old: " + age
    return name_with_age

エディタは変数の型を知っているので、補完だけでなく、エラーチェックをすることもできます:

これでagestr(age)で文字列に変換して修正する必要があることがわかります:

def get_name_with_age(name: str, age: int):
    name_with_age = name + " is this old: " + str(age)
    return name_with_age

型の宣言

型ヒントを宣言する主な場所を見てきました。関数のパラメータです。

これは FastAPI で使用する主な場所でもあります。

単純な型

strだけでなく、Pythonの標準的な型すべてを宣言できます。

例えば、以下を使用可能です:

  • int
  • float
  • bool
  • bytes
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
    return item_a, item_b, item_c, item_d, item_e

型パラメータを持つジェネリック型

データ構造の中には、dictlistsettupleのように他の値を含むことができるものがあります。また内部の値も独自の型を持つことができます。

内部の型を持つこれらの型は「generic」型と呼ばれます。そして、内部の型も含めて宣言することが可能です。

これらの型や内部の型を宣言するには、Pythonの標準モジュールtypingを使用できます。これらの型ヒントをサポートするために特別に存在しています。

新しいPythonバージョン

typingを使う構文は、Python 3.6から最新バージョンまで(Python 3.9、Python 3.10などを含む)すべてのバージョンと 互換性 があります。

Pythonが進化するにつれ、新しいバージョン ではこれらの型アノテーションへのサポートが改善され、多くの場合、型アノテーションを宣言するためにtypingモジュールをインポートして使う必要すらなくなります。

プロジェクトでより新しいPythonバージョンを選べるなら、その追加のシンプルさを活用できます。

ドキュメント全体で、Pythonの各バージョンと互換性のある例(差分がある場合)を示しています。

例えば「Python 3.6+」はPython 3.6以上(3.7、3.8、3.9、3.10などを含む)と互換性があることを意味します。また「Python 3.9+」はPython 3.9以上(3.10などを含む)と互換性があることを意味します。

最新のPythonバージョン を使えるなら、最新バージョン向けの例を使ってください。例えば「Python 3.10+」のように、それらは 最良かつ最もシンプルな構文 になります。

List

例えば、strlistの変数を定義してみましょう。

同じコロン(:)の構文で変数を宣言します。

型として、listを指定します。

リストはいくつかの内部の型を含む型なので、それらを角括弧で囲みます:

def process_items(items: list[str]):
    for item in items:
        print(item)

情報

角括弧内の内部の型は「型パラメータ」と呼ばれています。

この場合、strlistに渡される型パラメータです。

つまり: 変数itemslistであり、このリストの各項目はstrです。

そうすることで、エディタはリストの項目を処理している間にもサポートを提供できます。

型がなければ、それはほぼ不可能です。

変数itemはリストitemsの要素の一つであることに注意してください。

それでも、エディタはそれがstrであることを知っていて、そのためのサポートを提供しています。

Tuple と Set

tuplesetの宣言も同様です:

def process_items(items_t: tuple[int, int, str], items_s: set[bytes]):
    return items_t, items_s

つまり:

  • 変数items_tint、別のintstrの3つの項目を持つtupleです。
  • 変数items_ssetであり、その各項目はbytes型です。

Dict

dictを定義するには、カンマ区切りで2つの型パラメータを渡します。

最初の型パラメータはdictのキーです。

2番目の型パラメータはdictの値です:

def process_items(prices: dict[str, float]):
    for item_name, item_price in prices.items():
        print(item_name)
        print(item_price)

つまり:

  • 変数pricesdictです:
    • このdictのキーはstr型です(例えば、各項目の名前)。
    • このdictの値はfloat型です(例えば、各項目の価格)。

Union

変数が複数の型のいずれかになり得ることを宣言できます。例えば、intまたはstrです。

Python 3.6以上(Python 3.10を含む)では、typingUnion型を使い、角括弧の中に受け付ける可能性のある型を入れられます。

Python 3.10では、受け付ける可能性のある型を縦棒(|で区切って書ける 新しい構文 もあります。

def process_item(item: int | str):
    print(item)
from typing import Union


def process_item(item: Union[int, str]):
    print(item)

どちらの場合も、itemintまたはstrになり得ることを意味します。

Noneの可能性

値がstrのような型を持つ可能性がある一方で、Noneにもなり得ることを宣言できます。

Python 3.6以上(Python 3.10を含む)では、typingモジュールからOptionalをインポートして使うことで宣言できます。

from typing import Optional


def say_hi(name: Optional[str] = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")

ただのstrの代わりにOptional[str]を使用することで、値が常にstrであると仮定しているときに、実際にはNoneである可能性もあるというエラーをエディタが検出するのに役立ちます。

Optional[Something]は実際にはUnion[Something, None]のショートカットで、両者は等価です。

これは、Python 3.10ではSomething | Noneも使えることを意味します:

def say_hi(name: str | None = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")
from typing import Optional


def say_hi(name: Optional[str] = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")
from typing import Union


def say_hi(name: Union[str, None] = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")

UnionまたはOptionalの使用

Python 3.10未満のバージョンを使っている場合、これは私のとても 主観的 な観点からのヒントです:

  • 🚨 Optional[SomeType]は避けてください
  • 代わりに ✨ Union[SomeType, None]を使ってください

どちらも等価で、内部的には同じですが、OptionalよりUnionをおすすめします。というのも「optional」という単語は値がオプションであることを示唆するように見えますが、実際には「Noneになり得る」という意味であり、オプションではなく必須である場合でもそうだからです。

Union[SomeType, None]のほうが意味がより明示的だと思います。

これは言葉や名前の話にすぎません。しかし、その言葉はあなたやチームメイトがコードをどう考えるかに影響し得ます。

例として、この関数を見てみましょう:

from typing import Optional


def say_hi(name: Optional[str]):
    print(f"Hey {name}!")
🤓 Other versions and variants
def say_hi(name: str | None):
    print(f"Hey {name}!")

パラメータnameOptional[str]として定義されていますが、オプションではありません。そのパラメータなしで関数を呼び出せません:

say_hi()  # Oh, no, this throws an error! 😱

nameパラメータはデフォルト値がないため、依然として必須optionalではない)です。それでも、nameは値としてNoneを受け付けます:

say_hi(name=None)  # This works, None is valid 🎉

良い知らせとして、Python 3.10になればその心配は不要です。型のユニオンを定義するために|を単純に使えるからです:

def say_hi(name: str | None):
    print(f"Hey {name}!")
🤓 Other versions and variants
from typing import Optional


def say_hi(name: Optional[str]):
    print(f"Hey {name}!")

そして、OptionalUnionのような名前について心配する必要もなくなります。😎

ジェネリック型

角括弧で型パラメータを取るこれらの型は、例えば次のように Generic types または Generics と呼ばれます:

同じ組み込み型をジェネリクスとして(角括弧と内部の型で)使えます:

  • list
  • tuple
  • set
  • dict

また、これまでのPythonバージョンと同様に、typingモジュールから:

  • Union
  • Optional
  • ...and others.

Python 3.10では、ジェネリクスのUnionOptionalを使う代替として、型のユニオンを宣言するために縦棒(|を使えます。これはずっと良く、よりシンプルです。

同じ組み込み型をジェネリクスとして(角括弧と内部の型で)使えます:

  • list
  • tuple
  • set
  • dict

そしてtypingモジュールのジェネリクス:

  • Union
  • Optional
  • ...and others.

型としてのクラス

変数の型としてクラスを宣言することもできます。

名前を持つPersonクラスがあるとしましょう:

class Person:
    def __init__(self, name: str):
        self.name = name


def get_person_name(one_person: Person):
    return one_person.name

変数をPerson型として宣言できます:

class Person:
    def __init__(self, name: str):
        self.name = name


def get_person_name(one_person: Person):
    return one_person.name

そして、再び、すべてのエディタのサポートを得ることができます:

これは「one_personはクラスPersonインスタンスである」ことを意味します。

one_personPersonという名前のクラスである」という意味ではありません。

Pydanticのモデル

Pydantic はデータ検証を行うためのPythonライブラリです。

データの「形」を属性付きのクラスとして宣言します。

そして、それぞれの属性は型を持ちます。

さらに、いくつかの値を持つクラスのインスタンスを作成すると、その値を検証し、適切な型に変換して(もしそうであれば)すべてのデータを持つオブジェクトを提供してくれます。

また、その結果のオブジェクトですべてのエディタのサポートを受けることができます。

Pydanticの公式ドキュメントからの例:

from datetime import datetime

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name: str = "John Doe"
    signup_ts: datetime | None = None
    friends: list[int] = []


external_data = {
    "id": "123",
    "signup_ts": "2017-06-01 12:22",
    "friends": [1, "2", b"3"],
}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123
🤓 Other versions and variants
from datetime import datetime
from typing import Union

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name: str = "John Doe"
    signup_ts: Union[datetime, None] = None
    friends: list[int] = []


external_data = {
    "id": "123",
    "signup_ts": "2017-06-01 12:22",
    "friends": [1, "2", b"3"],
}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123

FastAPI はすべてPydanticをベースにしています。

すべてのことはチュートリアル - ユーザーガイドで実際に見ることができます。

豆知識

Pydanticには、デフォルト値なしでOptionalまたはUnion[Something, None]を使った場合の特別な挙動があります。詳細はPydanticドキュメントのRequired Optional fieldsを参照してください。

メタデータアノテーション付き型ヒント

Pythonには、Annotatedを使って型ヒントに追加のメタデータを付与できる機能もあります。

Python 3.9以降、Annotatedは標準ライブラリの一部なので、typingからインポートできます。

from typing import Annotated


def say_hello(name: Annotated[str, "this is just metadata"]) -> str:
    return f"Hello {name}"

Python自体は、このAnnotatedで何かをするわけではありません。また、エディタや他のツールにとっても、型は依然としてstrです。

しかし、Annotated内のこのスペースを使って、アプリケーションをどのように動作させたいかについての追加メタデータを FastAPI に提供できます。

覚えておくべき重要な点は、Annotatedに渡す最初の型パラメータ実際の型であることです。残りは、他のツール向けのメタデータにすぎません。

今のところは、Annotatedが存在し、それが標準のPythonであることを知っておけば十分です。😎

後で、これがどれほど強力になり得るかを見ることになります。

豆知識

これが 標準のPython であるという事実は、エディタで、使用しているツール(コードの解析やリファクタリングなど)とともに、可能な限り最高の開発体験が得られることを意味します。 ✨

また、あなたのコードが他の多くのPythonツールやライブラリとも非常に互換性が高いことも意味します。 🚀

FastAPIでの型ヒント

FastAPI はこれらの型ヒントを利用していくつかのことを行います。

FastAPI では型ヒントを使ってパラメータを宣言すると以下のものが得られます:

  • エディタサポート
  • 型チェック

...そして FastAPI は同じ宣言を使って、以下のことを行います:

  • 要件の定義: リクエストのパスパラメータ、クエリパラメータ、ヘッダー、ボディ、依存関係などから要件を定義します。
  • データの変換: リクエストから必要な型にデータを変換します。
  • データの検証: 各リクエストから来るデータについて:
    • データが無効な場合にクライアントに返される 自動エラー を生成します。
  • OpenAPIを使用してAPIをドキュメント化します:
    • これは自動の対話型ドキュメントのユーザーインターフェイスで使われます。

すべてが抽象的に聞こえるかもしれません。心配しないでください。 この全ての動作は チュートリアル - ユーザーガイドで見ることができます。

重要なのは、Pythonの標準的な型を使うことで、(クラスやデコレータなどを追加するのではなく)1つの場所で FastAPI が多くの作業を代わりにやってくれているということです。

情報

すでにすべてのチュートリアルを終えて、型についての詳細を見るためにこのページに戻ってきた場合は、良いリソースとしてmypyの「チートシート」があります。