Open10

Pythonの型を理解する

ikeponikepon

1. Pythonの型の基本

Pythonは動的型付けの言語であり、変数に値を代入する際に型を明示的に指定する必要はありません。代入された値に応じて、変数の型が自動的に決定されます。

x = 10        # int型
y = 3.14      # float型
name = "Alice"  # str型
is_active = True  # bool型

型を確認するには、type()関数を使用します。

print(type(x))  # <class 'int'>

Python 3.5以降では、 型ヒント(type hints) を用いて関数や変数の型を明示することができます。

def greet(name: str) -> str:
    return f"Hello, {name}"

🔗 公式ドキュメント - typing


2. Pythonの型の種類

数値型(Numeric Types)

  • int:整数

  • float:浮動小数点数

  • complex:複素数

    x = 42
    pi = 3.14159
    z = 3 + 4j

🔗 公式ドキュメント - 数値型

文字列型(Text Type)

  • str:文字列

    message = "Hello, World!"

🔗 公式ドキュメント - str型

ikeponikepon

シーケンス型(Sequence Types)

Pythonのシーケンス型は、順序を持つ要素のコレクションです。各要素にはインデックス(0から始まる)でアクセスできます。代表的なシーケンス型には次のようなものがあります。


list(リスト)

可変長の順序付きのコレクションです。異なる型の要素を混在させることもできます。

    fruits = ["apple", "banana", "cherry"]
    fruits.append("orange")
    print(fruits[1])  # banana

主な操作:

  • .append(x):末尾に追加
  • .remove(x):指定した要素を削除
  • .sort():要素を並べ替え(in-place)

tuple(タプル)

リストと似ていますが、**不変(immutable)**である点が異なります。関数の戻り値として複数の値を返すときなどによく使われます。

    point = (3, 4)
    x, y = point
    print(x + y)  # 7

不変のため、変更や追加はできません。ハッシュ化可能なので辞書のキーにも使えます。


range

整数の連続したシーケンスを生成します。主に for ループで使用されます。

    for i in range(3):
        print(i)  # 0 1 2

    list(range(2, 10, 2))  # [2, 4, 6, 8]

共通の特徴

すべてのシーケンス型は以下の操作をサポートしています(一部例外あり):

  • len(s):要素数を返す
  • x in s:要素の存在を確認
  • s[i]:インデックスアクセス
  • s[i:j]:スライス
    colors = ["red", "green", "blue"]
    print(colors[0])       # red
    print(colors[1:3])     # ['green', 'blue']
    print("green" in colors)  # True

🔗 公式ドキュメント - list/tuple/range

ikeponikepon

マッピング型(Mapping Type)

Pythonのマッピング型は、キーと値(key-value)のペアを保持するデータ構造です。最も一般的なマッピング型は dict(辞書)です。


dict(辞書)

dict可変で順序付き(Python 3.7以降) のデータ構造です。キーはハッシュ可能(__hash__() を持ち、変更不可能)である必要があります。

※ ハッシュ可能: 一意に識別できて、変更されない(イミュータブル)

    user = {
        "name": "Alice",
        "age": 30,
        "is_active": True
    }

    print(user["name"])      # Alice
    user["age"] = 31         # 値を更新
    user["email"] = "a@example.com"  # 新しいキーと値を追加

辞書の操作

  • アクセスd[key] または d.get(key)
  • 追加/更新d[key] = value
  • 削除del d[key] または d.pop(key)
  • キー・値・ペアの取得
    • d.keys():すべてのキー
    • d.values():すべての値
    • d.items():キーと値のペアのタプル
    settings = {"theme": "dark", "volume": 50}

    print(settings.get("theme"))     # dark
    print("volume" in settings)      # True
    print(settings.items())          # dict_items([("theme", "dark"), ("volume", 50)])

辞書内包表記(dictionary comprehension)

辞書内包表記とは、1行で辞書を生成する構文のことです。for文と条件式を使って、柔軟かつ簡潔に辞書を作ることができます。


基本構文
{key_expr: value_expr for item in iterable}

たとえば、0〜4までの数値をキーとして、その2乗を値とする辞書を作成するには:

    squares = {x: x * x for x in range(5)}
    # 結果: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

条件付き内包表記(if文を使う)

特定の条件を満たす要素だけを辞書に含めたい場合は、if を後ろに書きます。

    # 偶数だけ
    even_squares = {x: x * x for x in range(10) if x % 2 == 0}
    # 結果: {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

enumerate()と組み合わせる

リストのインデックスをキーに、値を辞書に変換できます。

fruits = ["apple", "banana", "cherry"]
fruit_dict = {i: fruit for i, fruit in enumerate(fruits)}
# 結果: {0: "apple", 1: "banana", 2: "cherry"}

zip()と組み合わせる

2つのリストを組み合わせて辞書に変換することも可能です。

keys = ["name", "age", "city"]
values = ["Alice", 30, "Tokyo"]
user = {k: v for k, v in zip(keys, values)}
# 結果: {"name": "Alice", "age": 30, "city": "Tokyo"}

ネストした辞書(応用)

辞書内包表記の中にさらに内包表記を使えば、入れ子の辞書を作ることもできます。

nested = {x: {y: x * y for y in range(3)} for x in range(2)}
# 結果:
# {
#   0: {0: 0, 1: 0, 2: 0},
#   1: {0: 0, 1: 1, 2: 2}
# }

注意点
  • 同じキーが複数回出現した場合、最後の値が有効になります。
  • パフォーマンス的にも通常のforループとほぼ同等です(読みやすさを優先)。

ikeponikepon

ネストされた辞書

辞書の値として他の辞書を持つことも可能です(ネスト)。

    users = {
        "alice": {"age": 30, "email": "a@example.com"},
        "bob": {"age": 25, "email": "b@example.com"}
    }

    print(users["bob"]["email"])  # b@example.com

注意点

  • 辞書のキーには listdict などの可変型は使用できません。
  • 値にはどのような型でも使用できます。

🔗 公式ドキュメント - dict

集合型(Set Types)

Pythonの集合(set / frozenset)は、重複のない要素の集まりを扱うためのデータ型です。数学的な集合と同じく、和・積・差・対称差などの集合演算が可能です。


set(変更可能な集合)

  • 中括弧 {} または set() で作成します。
  • 同じ要素が複数あっても自動的に重複が除かれます
  • 要素の順序は保持されません(順不同)。
    fruits = {"apple", "banana", "apple", "orange"}
    print(fruits)  # {'apple', 'banana', 'orange'}

    empty_set = set()  # 空集合({}はdictになるので注意)
主な操作:
  • .add(x):要素xを追加
  • .remove(x):要素xを削除(存在しないとエラー)
  • .discard(x):存在すれば削除、なくてもエラーにならない
  • .clear():すべての要素を削除
  • .pop():任意の要素を1つ取り出して削除
    numbers = {1, 2, 3}
    numbers.add(4)       # {1, 2, 3, 4}
    numbers.discard(2)   # {1, 3, 4}
    numbers.remove(99)   # KeyError になる(存在しない場合)

frozenset(変更不可能な集合)

  • frozenset() で作成するイミュータブルな集合
  • 集合自体の内容を変更できないが、辞書のキーなどに使える
    frozen = frozenset(["apple", "banana"])
    print("banana" in frozen)  # True

    # frozenset は .add() や .remove() が使えない

集合演算

集合同士の演算も簡単にできます。

    a = {1, 2, 3}
    b = {2, 3, 4}

    print(a | b)   # 和集合: {1, 2, 3, 4}
    print(a & b)   # 積集合: {2, 3}
    print(a - b)   # 差集合: {1}
    print(a ^ b)   # 対称差集合: {1, 4}
メソッド形式でも使える:
    a.union(b)
    a.intersection(b)
    a.difference(b)
    a.symmetric_difference(b)

比較演算(部分集合など)

    small = {1, 2}
    big = {1, 2, 3, 4}

    print(small.issubset(big))     # True
    print(big.issuperset(small))   # True
    print(small.isdisjoint({3, 4}))  # True(共通要素なし)

応用例:重複の削除

    data = ["apple", "banana", "apple", "orange"]
    unique = list(set(data))  # ['orange', 'apple', 'banana'](順序は保証されない)

注意点

  • 集合の要素にはハッシュ可能な(イミュータブルな)オブジェクトのみ使用可。
    • 例:int, str, tuple(内容もイミュータブル)など
    • list, dict, set は使えない
    invalid = { [1, 2, 3] }  # TypeError: unhashable type: 'list'

🔗 公式ドキュメント - set/frozenset

ikeponikepon

真偽値型(Boolean Type)

  • bool:真 (True) または偽 (False)
    is_valid = True

🔗 公式ドキュメント - bool型

NoneType

  • None:値が存在しないことを表す特殊な型
    result = None

🔗 公式ドキュメント - None


3. Pythonの型の応用例

型ヒントを使った関数定義

Pythonでは、関数の引数や戻り値に 型ヒント(type hints) を付けることで、コードの可読性・保守性・静的解析の精度を高めることができます。


基本構文

def 関数名(引数名: 型) -> 戻り値の型:
    処理

例:

    def greet(name: str) -> str:
        return f"Hello, {name}"

この例では:

  • namestr 型であるべき
  • 戻り値は str 型であることを表しています

複数の引数を持つ関数

    def add(x: int, y: int) -> int:
        return x + y

    def area(width: float, height: float) -> float:
        return width * height

戻り値がない関数

戻り値がない場合は -> None を指定します。

    def log_message(message: str) -> None:
        print(f"[LOG] {message}")

オプショナル型(Optional)

引数が None になる可能性がある場合は、Optional[...] を使います。

    from typing import Optional

    def get_username(user_id: int) -> Optional[str]:
        if user_id == 0:
            return None
        return "user" + str(user_id)

Optional[X]Union[X, None] の別名です


複数の型を許容する(Union)

複数の型を受け取る可能性がある場合は Union を使います。

    from typing import Union

    def stringify(value: Union[int, float]) -> str:
        return str(value)

デフォルト引数にも型ヒント

    def repeat(message: str, times: int = 1) -> str:
        return message * times

コレクション型の型ヒント

    from typing import List, Dict, Tuple

    # 整数のリスト(例:`[1, 2, 3]`)を受け取る
    def total(scores: List[int]) -> int:
        return sum(scores)

    # キーも値も文字列である辞書(例:`{"name": "Alice"}`)を返す
    def get_user() -> Dict[str, str]:
        return {"name": "Alice", "email": "a@example.com"}

    # 整数が2つ入ったタプル(例:`(10, 20)`)を返す
    def get_point() -> Tuple[int, int]:
        return (10, 20)

可変長引数の型ヒント

  • *args: タプルとして型指定
  • **kwargs: 辞書として型指定
    # 複数の整数を受け取る「位置引数」の可変長リスト
    # 0個以上の `int` 型の引数を受け取る
    def sum_all(*args: int) -> int:
        return sum(args)

    # 複数の名前付き引数(キーワード引数)を受け取り、すべての「値」が文字列
    # 名前付き引数(キーと値のペア)を0個以上受け取る
    def show_attrs(**kwargs: str) -> None:
        for key, value in kwargs.items():
            print(f"{key} = {value}")

ikeponikepon

Callable(関数を引数にする)

Pythonでは関数も「第一級オブジェクト」なので、他の関数の引数として関数を渡すことができます。

このような場合、型ヒントとして Callable を使います。


基本構文

from typing import Callable

Callable[[引数の型1, 引数の型2, ...], 戻り値の型]

例:

    def apply_twice(f: Callable[[int], int], x: int) -> int:
        return f(f(x))
  • apply_twice の引数
    • f: int を引数に取り、int を返す関数
    • x: int
    • 戻り値: int

使用例:

    def double(n: int) -> int:
        return n * 2

    print(apply_twice(double, 3))  # 12(double(double(3)))

複数の引数を取る関数の型

    def operate(a: int, b: int) -> int:
        return a + b

    f: Callable[[int, int], int] = operate
    result = f(2, 3)  # 5

戻り値がない関数

戻り値がない(None を返す)関数の指定もできます。

    def run_twice(action: Callable[[], None]) -> None:
        action()
        action()

    def say_hello() -> None:
        print("Hello!")

    run_twice(say_hello)

任意の引数を許容する場合

引数の数や型がさまざまな関数を扱いたいときは、以下のように書きます。

    from typing import Callable, Any

    def call_it(f: Callable[..., Any]) -> None:
        result = f()
        print(result)
  • Callable[..., Any]: 引数の数・型はなんでもよい、戻り値の型も何でもよい

注意点

  • Callable の第一引数は「リストではなく中括弧(角カッコ)」で複数の引数型を指定します。
  • Python 3.10 以降では collections.abc.Callable も使用可能ですが、typing.Callable の方が型チェッカーとの相性が良いです。

型ヒントを強制するには?

Pythonは動的型付けなので実行時には型チェックされません。型チェックには mypypyright などのツールを使います。

🔗 公式ドキュメント - 型ヒント

ikeponikepon

Optional型(Noneを許容)

Optional[X] は、「型 X の値 または None が来る可能性がある」ということを示す型ヒントです。

これは次のように書いたものと同義です:

Optional[X]  ≡  Union[X, None]

基本構文

    from typing import Optional

    def get_username(user_id: int) -> Optional[str]:
        if user_id == 0:
            return None
        return f"user{user_id}"
  • user_id が 0 の場合は None を返し、それ以外は文字列を返す関数
  • 戻り値の型ヒントとして Optional[str] を使うことで、戻り値が str または None であることが明示されます

使用例

    name: Optional[str] = "Alice"

    if name is not None:
        print(name.upper())
    else:
        print("名前が未設定です")

どんなときに使う?

  • 関数が正常時は値を返すが、失敗時や空のときは None を返す可能性があるとき
  • オブジェクトのプロパティが「設定されていない場合がある」とき
  • デフォルト引数として None を使うとき
    def greet(name: Optional[str] = None) -> str:
        if name is None:
            return "Hello, Guest!"
        return f"Hello, {name}!"

Optional は引数にも使える

    def send_email(to: Optional[str]) -> None:
        if to is None:
            raise ValueError("送信先が必要です")
        print(f"Sending email to {to}")

補足:is None でのチェックが基本

Optional な値を扱う場合は、is None で明示的にチェックするのが基本です。Pythonには null安全演算子 のようなものはありません(ただし :=if val: のような記法で簡略化は可能です)。

ikeponikepon

Union型(複数の型を許容)

Union[X, Y] は、「X型またはY型のいずれかであればOK」という意味の型ヒントです。
1つの引数や戻り値が複数の型に対応する場面で使います。


基本構文

    from typing import Union

    def stringify(value: Union[int, float]) -> str:
        return str(value)
  • valueint または float のどちらでもよい
  • 戻り値は str

使用例

    def length(x: Union[str, list]) -> int:
        return len(x)

    print(length("hello"))     # 5
    print(length([1, 2, 3]))   # 3

Unionのネスト使用(Noneを含める)

    def handle(data: Union[None, int, str]) -> str:
        if data is None:
            return "No data"
        return str(data)

Python 3.10 以降の短縮構文(|

    def stringify(value: int | float) -> str:
        return str(value)

int | floatUnion[int, float] と同じ意味
※ Python 3.10 以降のみ使用可能


注意点

  • Union型を使っても、関数内での型チェック(isinstanceなど)は必要になる場合がある

    • Union[X, Y] は「X型またはY型のどちらかである」という型ヒントです。
    • しかし、関数の中では実際に どちらの型が渡ってきたのかはコードを実行してみないとわかりません
    • そのため、関数内で 具体的な型に応じた処理を行う場合には、isinstance() などを使って型を判定する必要があります。
  • Unionに渡す型は2つ以上でもOK

  • 実行時には型が強制されるわけではない(型チェックは静的解析用)


よくある実用例

    from typing import Union

    def parse_price(price: Union[str, float]) -> float:
        if isinstance(price, str):
            return float(price.replace("¥", ""))
        return price

🔗 公式ドキュメント - typing.Union

ikeponikepon

ジェネリクス(例:List, Dict)

ジェネリクス(Generics)は、型の中の「要素の型」まで指定できる仕組みです。

たとえば、List[int] と書くと、 「整数のみを要素に持つリスト」という意味になります。

    from typing import List, Dict, Tuple

    scores: List[int] = [80, 90, 100]
    user: Dict[str, str] = {"name": "Alice", "email": "a@example.com"}

これにより、型チェッカーが各要素の型まで検査できるようになります。


よく使うコレクション型とジェネリクスの例

  • List[X]:X型の要素を持つリスト
  • Dict[K, V]:キーがK型、値がV型の辞書
  • Tuple[X, Y]:X型とY型の要素を持つ固定長タプル
  • Set[X]:X型の要素を持つ集合

✅ 具体例

    from typing import List, Dict, Tuple

    def total(scores: List[int]) -> int:
        return sum(scores)

    def get_user() -> Dict[str, str]:
        return {
            "name": "Alice",
            "email": "a@example.com"
        }

    def get_point() -> Tuple[int, int]:
        return (10, 20)

🧠 ジェネリクスを使う理由

  • 要素の型まで明示されることで、IDEの補完が正確になる
  • 静的型チェッカーがエラーを早期に検出できる
  • ドキュメントとしてもわかりやすい

例:次の2つの型ヒントの違い

    def f1(items: list) -> None:
        ...

    def f2(items: List[str]) -> None:
        ...
  • f1 はどんな型のリストでも受け取れる(list
  • f2 は「文字列のリストしか受け取れない」と明示されている(List[str]

🧪 ジェネリクスを使った関数の設計(応用)

独自の型引数を使って、型の再利用性を高めることもできます(TypeVar を使う)。

    from typing import TypeVar, List

    T = TypeVar("T")

    def first_item(items: List[T]) -> T:
        return items[0]
  • T は任意の型(int, str, User, など)
  • List[T] を受け取って、T を返す関数(同じ型で入出力される)

互換性と注意点

  • Python 3.9 以降では list[int], dict[str, int] のように書けます(組み込み型が直接ジェネリックに対応)。
  • それ以前のバージョンでは from typing import List, Dict を使う必要があります。

まとめ

  • ジェネリクスは、コレクションの中身の型まで記述できる型ヒント
  • よく使う:List, Dict, Tuple, Set
  • 応用:TypeVar を使って汎用的な関数やクラスを定義できる

🔗 公式ドキュメント - typing.Generic

ikeponikepon

4. まとめ

Pythonの型ヒントは、静的型チェック・コード補完・ドキュメント性の向上を目的とした機能です。
特に、複雑な関数やデータ構造を扱う際に、エラーを未然に防ぐための強力なサポートになります。


✅ 基本型のヒント

関数や変数に明示的な型を付けることができます。

def greet(name: str) -> str:
    return f"Hello, {name}"
  • name: str → 引数 name は文字列型
  • -> str → 戻り値は文字列型

✅ コレクション型のヒント(ジェネリクス)

リストや辞書など、要素の型まで指定できます。

from typing import List, Dict, Tuple

names: List[str] = ["Alice", "Bob"]
user: Dict[str, str] = {"name": "Alice", "email": "a@example.com"}
point: Tuple[int, int] = (10, 20)
  • List[X], Dict[K, V], Tuple[X, Y] のように使う
  • Python 3.9 以降は list[int], dict[str, int] のように書ける

✅ Optional型(Noneを許容)

None を返す・受け取る可能性がある値には Optional を使う。

from typing import Optional

def get_name(user_id: int) -> Optional[str]:
    if user_id == 0:
        return None
    return f"user{user_id}"
  • Optional[X]Union[X, None] と同じ意味

✅ Union型(複数の型を許容)

複数の型のどれかを受け取るときは Union を使う。

from typing import Union

def to_str(value: Union[int, float]) -> str:
    return str(value)
  • Python 3.10 以降は int | float のような簡潔な書き方も可能
  • 関数内では isinstance() で型の分岐が必要になることがある

✅ 可変長引数の型ヒント(*args, **kwargs)

def sum_all(*args: int) -> int:
    return sum(args)

def show_attrs(**kwargs: str) -> None:
    for key, value in kwargs.items():
        print(f"{key} = {value}")
  • *args: T → T型の任意個の位置引数
  • **kwargs: T → T型の任意個のキーワード引数(キーは常に str

✅ Callable(関数を引数にする)

関数を引数に受け取る場合には Callable を使う。

from typing import Callable

def apply_twice(f: Callable[[int], int], x: int) -> int:
    return f(f(x))
  • Callable[[引数の型...], 戻り値の型]
  • 引数や戻り値の型が重要な意味を持つ関数に特に有効

✅ ジェネリクス(TypeVarの応用)

より汎用的な関数やクラスを定義したいときには、型変数 TypeVar を使う。

from typing import TypeVar, List

T = TypeVar("T")

def first_item(items: List[T]) -> T:
    return items[0]
  • 入力の型と戻り値の型を連動させられる
  • 再利用性の高いAPIを設計するのに便利

✅ バージョンによる記法の違い

Pythonバージョン 記法 備考
3.8以下 List[int], Dict[str, T] typing モジュールをimport
3.9以降 list[int], dict[str, T] 組み込み型でOK(PEP 585)

📌 まとめのポイント

  • Pythonの型ヒントは 静的解析のための仕組み(実行時には影響しない)
  • 型ヒントを使うことで コードの可読性、保守性、バグの早期発見が可能になる
  • mypy, pyright, pylance などと一緒に使うと効果が最大化される
  • バージョン差異や記法の違いに注意して、一貫したスタイルを心がけるとよい

🔗 公式ドキュメント - typing