型アノテーション（Type Annotation）は、Pythonのコードで変数、関数の引数や返り値、クラスの属性などに対して型情報を記述する方法です。これは、静的型チェッカーやIDEのための情報提供や、コードの可読性向上に役立ちます。
Pythonでは型アノテーションは以下のように使います：
x: int = 5
ここでの: intが型アノテーションで、変数xの型を整数(int)であると示しています。
def add(a: int, b: int) -> int:
    return a + b
add関数の引数aとb、そして返り値に対してそれぞれの型を示しています。引数の型アノテーションは引数名の後ろに:を使い、返り値の型アノテーションは関数の最後の->の後ろに指定します。
class Person:
    def __init__(self, name: str, age: int) -> None:
        self.name = name  # name属性はstr型
        self.age = age    # age属性はint型
Personクラスの__init__メソッド内で、name属性とage属性の型をそれぞれstrとintとして指定しています。
型アノテーションはPythonの実行時には影響を与えず、実行時の型チェックを行わないため、静的型付け言語のように完全な型安全性を提供するものではありません。しかし、IDEや静的解析ツール（mypyなど）などが型情報を利用してコードを理解し、検証や自動補完を行うことができます。
[https://paiza.io/projects/rUWKfipgtECXJeJe1lIlSg?language=python3 合計のはずが連結に]:
def total(*args):
    if len(args) == 0:
        return 0
    it = iter(args)
    result = next(it)
    for i in it:
        result += i
    return result
print(f"""\
{total()=}
{total(1,2,3)=}
{total(*(i for i in range(10)))=}
{total(*(1.0*i for i in range(10)))=}
{total(False, True)=}
{total("abc","def","ghi")=}
{total([0,1,2],[3,4,5],[6,7,8])=}""")
実行結果:
total()=0
total(1,2,3)=6
total(*(i for i in range(10)))=45
total(*(1.0*i for i in range(10)))=45.0
total(False, True)=1
total("abc","def","ghi")='abcdefghi'
total([0,1,2],[3,4,5],[6,7,8])=[0, 1, 2, 3, 4, 5, 6, 7, 8]
[https://paiza.io/projects/FR95fADEQRx-IQNUKJcdKw?language=python3 合計のはずが連結に（オンライン実行）]
[https://mypy-play.net/?mypy=latest&python=3.10&gist=51981c0ad5393fa5d0ec5a6914216319 合計のはずが連結に（mypyで検査）]
from typing import Union
number = Union[int, float]
def total(*args: number) -> number:
    if len(args) == 0:
        return 0
    it = iter(args)
    result = next(it)
    for i in it:
        result += i
    return result
print(f"""\
{total()=}
{total(1,2,3)=}
{total(*(i for i in range(10)))=}
{total(*(1.0*i for i in range(10)))=}
{total(False, True)=}
{total("abc","def","ghi")=}
{total([0,1,2],[3,4,5],[6,7,8])=}""")
実行結果:上に同じ
MyPyの検査結果:
main.py:13: error: Argument 1 to "total" has incompatible type "str"; expected "Union[int, float]"
main.py:13: error: Argument 1 to "total" has incompatible type "List[int]"; expected "Union[int, float]"
main.py:13: error: Argument 2 to "total" has incompatible type "str"; expected "Union[int, float]"
main.py:13: error: Argument 2 to "total" has incompatible type "List[int]"; expected "Union[int, float]"
main.py:13: error: Argument 3 to "total" has incompatible type "str"; expected "Union[int, float]"
main.py:13: error: Argument 3 to "total" has incompatible type "List[int]"; expected "Union[int, float]"
Found 6 errors in 1 file (checked 1 source file)
型アノテーションを追加しても実行結果は同じですが、MyPyは6つの型の不整合を発見しています。
from typing import Union
number = Union[int, float, None]
number = int | float | None
try:
    number = int | float | None
except TypeError as e:
    """ Fallback for < 3.9 """
    from typing import List
    number = Union[int, float, None]
の様に実行環境によって動作を変える様にできます。
が、ここ書き方だと MyPy が2行目と6行目で型定義が重複していると警告します。
from typing import Union
number = Union[int, float, None]
のままにしました。
型ヒント・型アノテーションは、まだ新しい機能で typing モジュールを import しない方向に進化しつつあり、今後の動向が注目されます。
オーブジェクトの型アノテーションについては、__annotations__ 属性によって Python プログラム中からアクセスできます。
[https://paiza.io/projects/oylJ0esG5lZRH6a09Yl-Tg?language=python3 __annotations__ 属性]:
import sys
print(f"{sys.version=}")
v: int
v = 0
print(f"{__annotations__=}")
def f(a: int, b: int) -> int:
    _anon = f.__annotations__
    assert isinstance(a,_anon["a"])
    assert isinstance(b,_anon["b"])
    print(f"{f.__annotations__=}")
    result = a + b
    assert isinstance(result,_anon["return"])
    return result
class Point(dict):
    x: int
    y: int
    x = 10
    y = 20
    print(f"{__qualname__}: {__annotations__=}")
try:
    f(2,3)
    f("","")
except AssertionError as err:
    print(f"{err=}")
print("done!")
実行結果:
sys.version='3.8.10 (default, Jun  2 2021, 10:49:15) \n[GCC 9.4.0]'
__annotations__={'v': }
Point: __annotations__={'x': , 'y': }
f.__annotations__={'a': , 'b': , 'return': }
err=AssertionError()
done!
型アノテーションは、名前の通り原則的には名前通り注釈なのですが、特殊属性 __annotations__ を介して実行中のスクリプト自身からアクセスできます。
これを利用すると、例えば関数が実引数が仮引数の型アノテーションと一致しているかをisinstance関数で確認できます。
isinstance関数で確認し、不一致があった場合 assert 文で AssertError をあげ、try:except で捕捉することができます。
このように、__annotations__属性を使用することで、コードの可読性や保守性を向上させることができます。ただし、__annotations__属性はPythonのランタイムには影響を与えないため、必ずしも正確である必要はありません。また、__annotations__属性を使って型チェックを行うためには、外部ライブラリを使用する必要があります。