リテラル

Literal

(元々 PEP 586 で指定されています。)

コアセマンティクス

このセクションでは、リテラル型の基本的な動作を概説します。

コアの動作

リテラル型は、変数が特定の具体的な値を持つことを示します。 たとえば、変数 foo の型を Literal[3] と定義する場合、foo は正確に 3 と等しくなければならず、他の値は許可されません。

T のメンバーである値 v が与えられた場合、型 Literal[v]T のサブタイプです。 たとえば、Literal[3]int のサブタイプです。

親型のすべてのメソッドはリテラル型によって直接継承されます。 したがって、型 Literal[3] の変数 foo がある場合、fooint__add__ メソッドを継承するため、foo + 5 のような操作を行うことは安全です。 foo + 5 の結果の型は int です。

この「継承」動作は、NewTypes を処理する方法と同じです。

2 つのリテラルの同値性

2 つの型 Literal[v1]Literal[v2] は、次の 2 つの条件が両方とも真である場合に同値です。

  1. type(v1) == type(v2)

  2. v1 == v2

たとえば、Literal[20]Literal[0x14] は同値です。 ただし、Literal[0]Literal[False] は同値ではありません。これは、ランタイムで 0 == False が 'true' と評価されるにもかかわらず、0int 型であり、Falsebool 型であるためです。

リテラルの共用体の短縮

リテラルは 1 つ以上の値でパラメータ化されます。 リテラルが複数の値でパラメータ化される場合、それはこれらの型の共用体とまったく同等に扱われます。 つまり、Literal[v1, v2, v3]Literal[v1] | Literal[v2] | Literal[v3] と同等です。

このショートカットは、多くの異なるリテラルを受け入れる関数のシグネチャを記述する際に、より使いやすくするのに役立ちます。 たとえば、open(...) のような関数:

# 注: これは実際の型シグネチャの簡略化です。
_PathType = str | bytes | int

@overload
def open(path: _PathType,
         mode: Literal["r", "w", "a", "x", "r+", "w+", "a+", "x+"],
         ) -> IO[str]: ...
@overload
def open(path: _PathType,
         mode: Literal["rb", "wb", "ab", "xb", "r+b", "w+b", "a+b", "x+b"],
         ) -> IO[bytes]: ...

# ユーザーがリテラル型を使用していない場合のフォールバックオーバーロード
@overload
def open(path: _PathType, mode: str) -> IO[Any]: ...

提供される値はすべて同じ型のメンバーである必要はありません。 たとえば、Literal[42, "foo", True] は合法な型です。

ただし、リテラルは少なくとも 1 つの型でパラメータ化する必要があります。 Literal[]Literal のような型は違法です。

合法および違法なパラメータ化

このセクションでは、合法な Literal[...] 型を正確に構成するもの、つまりどの値がパラメータとして使用できるか、使用できないかについて説明します。

簡単に言えば、Literal[...] 型は 1 つ以上のリテラル式でパラメータ化され、それ以外は何もできません。

型チェック時の Literal の違法なパラメータ

次のパラメータは設計上意図的に禁止されています。

  • Literal[3 + 4]Literal["foo".replace("o", "b")] のような任意の式。

    • 理由: リテラル型は型付けエコシステムへの最小限の拡張を意図しており、型チェッカーが型内の潜在的な式を解釈することを要求することは複雑さを増しすぎます。

    • この結果として、Literal[4 + 3j]Literal[-4 + 2j] のような複素数も禁止されています。 一貫性のために、単一の複素数を含む Literal[4j] のようなリテラルも禁止されています。

    • このルールの唯一の例外は、整数に対する単項 -``(マイナス)および単項 ``+``(プラス)です。 ``Literal[-5] および Literal[+1] のような型は受け入れられます。

  • 有効なリテラル型を含むタプル(例: Literal[(1, "foo", "bar")])。 ユーザーはこの型を tuple[Literal[1], Literal["foo"], Literal["bar"]] として表現することができます。 また、タプルは Literal[1, 2, 3] のショートカットと混同される可能性があります。

  • ミュータブルなリテラルデータ構造(例: 辞書リテラル、リストリテラル、またはセットリテラル)。 リテラルは常に暗黙的に最終的であり、変更不可能です。 したがって、Literal[{"a": "b", "c": "d"}] は違法です。

  • その他の型(例: Literal[Path]Literal[some_object_instance])は違法です。 これには型変数が含まれます。 T が型変数である場合、Literal[T] は許可されません。 型変数は値ではなく型に対してのみ変化します。

次のものは簡単のために暫定的に禁止されています。 将来的に許可することを検討できます。

  • 浮動小数点数(例: Literal[3.14])。 無限大や NaN のリテラルをクリーンに表現することは難しく、実際の API は浮動小数点数のパラメータに基づいて動作を変えることはほとんどありません。

  • Any``(例: ``Literal[Any])。 Any は型であり、Literal[...] は値のみを含むことを意図しています。 また、Literal[Any] が実際に何を意味するのかも不明です。

ランタイムでのパラメータ

型チェック時に Literal[...] が含むことができるパラメータのセットは非常に小さいですが、typing.Literal の実際の実装はランタイムでチェックを行いません。 たとえば:

def my_function(x: Literal[1 + 2]) -> int:
    return x * 3

x: Literal = 3
y: Literal[my_function] = my_function

型チェッカーはこのプログラムを拒否する必要があります。 3 つの Literal の使用はすべてこの仕様に従って無効です。 ただし、Python 自体はこのプログラムをエラーなしで実行する必要があります。

これは、将来的に Literal の使用範囲を拡大する場合の柔軟性を保持するための一部であり、部分的にはランタイムで違法なパラメータをすべて検出することが不可能であるためです。 たとえば、ランタイムで Literal[1 + 2]Literal[3] を区別することは不可能です。

リテラル、列挙、および前方参照

1 つの潜在的な曖昧さは、リテラル文字列とリテラル列挙メンバーへの前方参照の間にあります。 たとえば、Literal["Color.RED"] 型があるとします。 このリテラル型には文字列リテラルが含まれていますか、それとも Color.RED 列挙メンバーへの前方参照ですか?

このような場合、常にユーザーがリテラル文字列を構築しようとしたと仮定します。 ユーザーが前方参照を望む場合、リテラル型全体を文字列でラップする必要があります。 例: "Literal[Color.RED]"

型推論

このセクションでは、リテラルと型推論に関するいくつかのルールと例を説明します。

後方互換性

型チェッカーがリテラルのサポートを追加する場合、後方互換性を最大化する方法で行うことが重要です。 型チェッカーは、リテラルのサポートが追加された後も、以前に型チェックされていたコードが引き続き型チェックされることを最善の努力で保証する必要があります。

これは特に型推論を行う場合に重要です。 たとえば、x = "blue" という文が与えられた場合、x の推論された型は str ですか、それとも Literal["blue"] ですか?

1 つの単純な戦略は、常に式がリテラル型であると仮定することです。 したがって、上記の例では、x は常に Literal["blue"] の型を持つことになります。 この単純な戦略はほぼ確実に破壊的すぎます。 以前は型チェックに合格していたプログラムが失敗する原因となります。 例:

# 型チェッカーが 'var' の型を Literal[3] と推論し、
# my_list の型を List[Literal[3]] と推論する場合...
var = 3
my_list = [var]

# ...この呼び出しは型エラーになります。
my_list.append(4)

この戦略が失敗するもう 1 つの例は、オブジェクトのフィールドを設定する場合です:

class MyObject:
    def __init__(self) -> None:
        # 型チェッカーが MyObject.field の型を Literal[3] と推論する場合...
        self.field = 3

m = MyObject()

# ...この代入はもはや型チェックに合格しません
m.field = 4

すべてのケースで互換性を維持する別の戦略は、明示的に注釈されない限り、式がリテラル型でないと常に仮定することです。 この戦略を使用する型チェッカーは、上記の最初の例では常に xstr 型であると推論します。

これは唯一の実行可能な戦略ではありません。 型チェッカーは、より洗練された推論技術を試すことができます。 特定の戦略は義務付けられていませんが、型チェッカーは後方互換性の重要性を念頭に置く必要があります。

リテラルコンテキストでの非リテラルの使用

リテラル型は、追加の特別なケースなしで既存のサブタイピングルールに従います。 たとえば、次のようなプログラムは型安全です:

def expects_str(x: str) -> None: ...
var: Literal["foo"] = "foo"

# 合法: Literal["foo"] は str のサブタイプです
expects_str(var)

これにより、一般的に非リテラル型はリテラル型に assignable ではありません。 たとえば:

def expects_literal(x: Literal["foo"]) -> None: ...

def runner(my_str: str) -> None:
    # 違法: str は Literal["foo"] に代入できません
    expects_literal(my_str)

注: ユーザーが API がリテラルと元の型の両方を受け入れることをサポートしたい場合(おそらくレガシーの目的で)、フォールバックオーバーロードを実装する必要があります。 オーバーロードとの相互作用 を参照してください。

他の型および機能との相互作用

このセクションでは、リテラル型が他の既存の型とどのように相互作用するかについて説明します。

構造化データのインテリジェントなインデックス付け

リテラルは、タプル、NamedTuple、およびクラスのような構造化型に「インテリジェントにインデックス付け」するために使用できます。 (注: これは包括的なリストではありません。)

たとえば、型チェッカーは、整数キーを使用してタプルにインデックスを付けるときに正しい値の型を推論する必要があります。:

a: Literal[0] = 0
b: Literal[5] = 5

some_tuple: tuple[int, str, List[bool]] = (3, "abc", [True, False])
reveal_type(some_tuple[a])   # 明らかにされた型は 'int' です
some_tuple[b]                # エラー: 5 はタプルの有効なインデックスではありません

getattr のような関数を使用する場合も同様の動作が期待されます。:

class Test:
    def __init__(self, param: int) -> None:
        self.myfield = param

    def mymethod(self, val: int) -> str: ...

a: Literal["myfield"]  = "myfield"
b: Literal["mymethod"] = "mymethod"
c: Literal["blah"]     = "blah"

t = Test()
reveal_type(getattr(t, a))  # 明らかにされた型は 'int' です
reveal_type(getattr(t, b))  # 明らかにされた型は 'Callable[[int], str]' です
getattr(t, c)               # エラー: Test に 'blah' という名前の属性はありません

注: 上記の変数宣言をよりコンパクトに表現する方法については、 Final との相互作用 を参照してください。

オーバーロードとの相互作用

リテラル型とオーバーロードは特別な方法で相互作用する必要はありません。 既存のルールで問題ありません。

ただし、型チェッカーがサポートする必要がある重要なユースケースの 1 つは、ユーザーがリテラル型を使用していない場合にフォールバックを使用する機能です。 たとえば、open を考えてみましょう。:

_PathType = str | bytes | int

@overload
def open(path: _PathType,
         mode: Literal["r", "w", "a", "x", "r+", "w+", "a+", "x+"],
         ) -> IO[str]: ...
@overload
def open(path: _PathType,
         mode: Literal["rb", "wb", "ab", "xb", "r+b", "w+b", "a+b", "x+b"],
         ) -> IO[bytes]: ...

# ユーザーがリテラル型を使用していない場合のフォールバックオーバーロード
@overload
def open(path: _PathType, mode: str) -> IO[Any]: ...

open のシグネチャを最初の 2 つのオーバーロードだけを使用するように変更すると、リテラル文字列式を渡さないコードが壊れます。 たとえば、次のようなコードが壊れます。:

mode: str = pick_file_mode(...)
with open(path, mode) as f:
    # f はここで IO[Any] 型である必要があります

もう少し広く言えば、typeshed の既存の API にリテラル型を追加するたびに、後方互換性を維持するために常にフォールバックオーバーロードを含めることを義務付けます。

ジェネリックとの相互作用

リテラル型は型であり、型が期待される場所ならどこでも使用できます。

たとえば、ジェネリック関数やクラスをリテラル型でパラメータ化することは合法です。:

A = TypeVar('A', bound=int)
B = TypeVar('B', bound=int)
C = TypeVar('C', bound=int)

# Matrix[row, column] の簡略化された定義
class Matrix(Generic[A, B]):
    def __add__(self, other: Matrix[A, B]) -> Matrix[A, B]: ...
    def __matmul__(self, other: Matrix[B, C]) -> Matrix[A, C]: ...
    def transpose(self) -> Matrix[B, A]: ...

foo: Matrix[Literal[2], Literal[3]] = Matrix(...)
bar: Matrix[Literal[3], Literal[7]] = Matrix(...)

baz = foo @ bar
reveal_type(baz)  # 明らかにされた型は 'Matrix[Literal[2], Literal[7]]' です

同様に、リテラル型を含む制限や境界を持つ型変数を構築することも合法です。:

T = TypeVar('T', Literal["a"], Literal["b"], Literal["c"])
S = TypeVar('S', bound=Literal["foo"])

...ただし、リテラルの上限を持つ型変数を構築することがいつ役立つかは不明です。 たとえば、上記の例の S 型変数は本質的に無意味です。 S = Literal["foo"] を使用することで同等の動作を得ることができます。

注: リテラル型とジェネリックは意図的に非常に基本的で限定的な方法でのみ相互作用します。 特に、数値操作や numpy スタイルの操作を大量に含むコードを型チェックしたいライブラリは、ここで説明するリテラル型がニーズに対して不十分であるとほぼ確実に感じるでしょう。

列挙と網羅性チェックとの相互作用

型チェッカーは、列挙のような限られた数のバリアントを持つリテラル型を扱う場合に網羅性チェックを実行できる必要があります。 たとえば、型チェッカーは、Status 列挙の 3 つの値がすべて使い果たされたため、最後の else 文が str 型である必要があることを推論できる必要があります。:

class Status(Enum):
    SUCCESS = 0
    INVALID_DATA = 1
    FATAL_ERROR = 2

def parse_status(s: str | Status) -> None:
    if s is Status.SUCCESS:
        print("Success!")
    elif s is Status.INVALID_DATA:
        print("The given data is invalid because...")
    elif s is Status.FATAL_ERROR:
        print("Unexpected fatal error...")
    else:
        # 's' はすべての他のオプションが使い果たされたため 'str' 型である必要があります
        print("Got custom status: " + s)

ここで、Status 列挙は Literal[Status.SUCCESS, Status.INVALID_DATA, Status.FATAL_ERROR] とほぼ同等と見なされ、s の型がそれに応じて絞り込まれます。

絞り込みとの相互作用

型チェッカーは、上記のセクションで説明されているものを超えて、列挙および非列挙リテラル型に対して追加の分析をオプションで実行する場合があります。

たとえば、包含や等価性チェックに基づいて絞り込みを行うことが有用な場合があります。:

def parse_status(status: str) -> None:
    if status in ("MALFORMED", "ABORTED"):
        # 型チェッカーは 'status' を Literal["MALFORMED", "ABORTED"] 型に絞り込むことができます。
        return expects_bad_status(status)

    # 同様に、型チェッカーは 'status' を Literal["PENDING"] 型に絞り込むことができます。
    if status == "PENDING":
        expects_pending_status(status)

リテラルブールを含む式を考慮して絞り込みを行うことも有用です。 たとえば、Literal[True]Literal[False]、およびオーバーロードを組み合わせて「カスタム型ガード」を構築できます。:

@overload
def is_int_like(x: int | list[int]) -> Literal[True]: ...
@overload
def is_int_like(x: object) -> bool: ...
def is_int_like(x): ...

vector: list[int] = [1, 2, 3]
if is_int_like(vector):
    vector.append(3)
else:
    vector.append("bad")   # このブランチは到達不可能であると推論されます

scalar: int | str
if is_int_like(scalar):
    scalar += 3      # 型チェック: 'scalar' の型は 'int' に絞り込まれます
else:
    scalar += "foo"  # 型チェック: 'scalar' の型は 'str' に絞り込まれます

Final との相互作用

Final 修飾子を使用して、変数や属性が再代入できないことを宣言できます。:

foo: Final = 3
foo = 4           # エラー: 'foo' は Final と宣言されています

上記の例では、foo が常に正確に 3 と等しいことがわかります。 型チェッカーはこの情報を使用して、fooLiteral[3] を期待するコンテキストで使用することが有効であると推論できます。:

def expects_three(x: Literal[3]) -> None: ...

expects_three(foo)  # 型チェックに合格します。'foo' は Final であり、3 と等しいためです。

Final 修飾子は、変数が*実質的にリテラル*であることを宣言するための省略形として機能します。

型チェッカーはこのショートカットをサポートすることが期待されます。 具体的には、var: Final = value の形式の変数または属性の代入があり、valueLiteral[...] の有効なパラメータである場合、型チェッカーは varLiteral[value] を期待するコンテキストで使用できることを理解する必要があります。

型チェッカーは、Final の他の使用法を理解する義務はありません。 たとえば、次のプログラムが型チェックに合格するかどうかは指定されていません。:

# 注: 代入は正確に 'var: Final = value' の形式と一致しません。
bar1: Final[int] = 3
expects_three(bar1)  # 型チェッカーによって受け入れられる場合と受け入れられない場合があります。

# 注: "Literal[1 + 2]" は合法な型ではありません。
bar2: Final = 1 + 2
expects_three(bar2)  # 型チェッカーによって受け入れられる場合と受け入れられない場合があります。

LiteralString

(元々 PEP 675 で指定されています。)

LiteralString の有効な場所

LiteralString は他の型が使用できる場所で使用できます。:

variable_annotation: LiteralString

def my_function(literal_string: LiteralString) -> LiteralString: ...

class Foo:
    my_attribute: LiteralString

type_argument: List[LiteralString]

T = TypeVar("T", bound=LiteralString)

共用体の Literal 型内にネストすることはできません。:

bad_union: Literal["hello", LiteralString]  # 許可されません
bad_nesting: Literal[LiteralString]  # 許可されません

型推論

LiteralString の推論

任意のリテラル文字列型は LiteralString に代入できます。 たとえば、x: LiteralString = "foo" は有効です。なぜなら、"foo"Literal["foo"] 型と推論されるためです。

次の場合にも LiteralString を推論します。

  • 加算: xy の両方の型が LiteralString に代入できる場合、x + y の型は LiteralString です。

  • 結合: sep の型が LiteralString に代入でき、xs の型が Iterable[LiteralString] に代入できる場合、sep.join(xs) の型は LiteralString です。

  • インプレース加算: s の型が LiteralString であり、x の型が LiteralString に代入できる場合、s += xs の型を LiteralString として保持します。

  • 文字列フォーマット: f 文字列の型は、その構成要素の式がリテラル文字列である場合にのみ LiteralString です。 s.format(...) は、s と引数の型が LiteralString に代入できる場合にのみ LiteralString に代入できます。

他のすべての場合、構成される値の 1 つ以上が非リテラル型 str を持つ場合、型の構成は str 型を持ちます。 たとえば、s の型が str である場合、"hello" + s の型は str です。 これは型チェッカーの既存の動作と一致します。

LiteralStringstr 型に代入できます。 それは str からすべてのメソッドを継承します。 したがって、LiteralString 型の変数 s がある場合、s.startswith("hello") と書くことは安全です。

一部の型チェッカーは、等価性チェックを行うときに文字列の型を絞り込みます。:

def foo(s: str) -> None:
    if s == "bar":
        reveal_type(s)  # => Literal["bar"]

if ブロック内でのこのような絞り込まれた型も LiteralString に代入できます。なぜなら、その型は Literal["bar"] だからです。

以下の例を参照して、上記のルールを明確にしてください。:

literal_string: LiteralString
s: str = literal_string  # OK

literal_string: LiteralString = s  # エラー: LiteralString が期待されましたが、str が得られました。
literal_string: LiteralString = "hello"  # OK

リテラル文字列の加算:

def expect_literal_string(s: LiteralString) -> None: ...

expect_literal_string("foo" + "bar")  # OK
expect_literal_string(literal_string + "bar")  # OK

literal_string2: LiteralString
expect_literal_string(literal_string + literal_string2)  # OK

plain_string: str
expect_literal_string(literal_string + plain_string)  # 許可されません。

リテラル文字列を使用した結合:

expect_literal_string(",".join(["foo", "bar"]))  # OK
expect_literal_string(literal_string.join(["foo", "bar"]))  # OK
expect_literal_string(literal_string.join([literal_string, literal_string2]))  # OK

xs: List[LiteralString]
expect_literal_string(literal_string.join(xs)) # OK
expect_literal_string(plain_string.join([literal_string, literal_string2]))
# 区切り文字の型が 'str' であるため許可されません。

リテラル文字列を使用したインプレース加算:

literal_string += "foo"  # OK
literal_string += literal_string2  # OK
literal_string += plain_string # 許可されません

リテラル文字列を使用したフォーマット文字列:

literal_name: LiteralString
expect_literal_string(f"hello {literal_name}")
# それがリテラル文字列から構成されているため OK です。

expect_literal_string("hello {}".format(literal_name))  # OK

expect_literal_string(f"hello")  # OK

username: str
expect_literal_string(f"hello {username}")
# 許可されません。フォーマット文字列は 'username' から構成されており、
# その型は 'str' です。

expect_literal_string("hello {}".format(username))  # 許可されません

リテラル整数などの他のリテラル型は LiteralString に代入できません。:

some_int: int
expect_literal_string(some_int)  # エラー: LiteralString が期待されましたが、int が得られました。

literal_one: Literal[1] = 1
expect_literal_string(literal_one)  # エラー: LiteralString が期待されましたが、Literal[1] が得られました。

リテラル文字列に関数を呼び出すことができます。:

def add_limit(query: LiteralString) -> LiteralString:
    return query + " LIMIT = 1"

def my_query(query: LiteralString, user_id: str) -> None:
    sql_connection().execute(add_limit(query), (user_id,))  # OK

条件文と式は期待通りに動作します。:

def return_literal_string() -> LiteralString:
    return "foo" if condition1() else "bar"  # OK

def return_literal_str2(literal_string: LiteralString) -> LiteralString:
    return "foo" if condition1() else literal_string  # OK

def return_literal_str3() -> LiteralString:
    if condition1():
        result: Literal["foo"] = "foo"
    else:
        result: LiteralString = "bar"

    return result  # OK

型変数とジェネリックとの相互作用

型変数は LiteralString にバインドできます。:

from typing import Literal, LiteralString, TypeVar

TLiteral = TypeVar("TLiteral", bound=LiteralString)

def literal_identity(s: TLiteral) -> TLiteral:
    return s

hello: Literal["hello"] = "hello"
y = literal_identity(hello)
reveal_type(y)  # => Literal["hello"]

s: LiteralString
y2 = literal_identity(s)
reveal_type(y2)  # => LiteralString

s_error: str
literal_identity(s_error)
# エラー: LiteralString にバインドされた TLiteral が期待されましたが、str が得られました。

LiteralString はジェネリッククラスの型引数として使用できます。:

class Container(Generic[T]):
    def __init__(self, value: T) -> None:
        self.value = value

literal_string: LiteralString = "hello"
x: Container[LiteralString] = Container(literal_string)  # OK

s: str
x_error: Container[LiteralString] = Container(s)  # 許可されません

List のような標準コンテナは期待通りに動作します。:

xs: List[LiteralString] = ["foo", "bar", "baz"]

オーバーロードとの相互作用

リテラル文字列とオーバーロードは特別な方法で相互作用する必要はありません。 既存のルールで問題ありません。 LiteralString は、特定の Literal["foo"] 型が一致しない場合のフォールバックオーバーロードとして使用できます。:

@overload
def foo(x: Literal["foo"]) -> int: ...
@overload
def foo(x: LiteralString) -> bool: ...
@overload
def foo(x: str) -> str: ...

x1: int = foo("foo")  # 最初のオーバーロード。
x2: bool = foo("bar")  # 2 番目のオーバーロード。
s: str
x3: str = foo(s)  # 3 番目のオーバーロード。