モダンPython基礎 #2 — 変数、基本型、型ヒント

読了 7分

#1 はじめ方と uv セットアップ で作ったプロジェクトを土台に、そのまま続けます。今回のテーマは 変数、基本型、そして型ヒント です。

Python は動的型付け言語です。変数に型を書かなくても動きます。それでも モダンPythonでは最初から型を書く という流れが標準になりました。なぜそうするのか、どう書くのかを整理していきます。

変数 — 宣言は別にない #

他の言語との大きな違いのひとつです。let, var, int x のような宣言キーワードはありません。名前に値を代入すれば、それが変数です。

variables
name = "curtis"
age = 30
height = 175.5
is_admin = True

命名規則は snake_case が基本です。JavaScript の camelCase に慣れていると最初は少し違和感があります。定数として扱う値は大文字 + アンダースコアで書きます。

constant style
MAX_RETRY = 3

基本型 — 4 + 1 #

まずは最もよく使う 5 つを押さえれば十分です。

basic types
i: int = 42
f: float = 3.14
s: str = "hello"
b: bool = True
n: None = None

この : int の部分が 型ヒント です。変数名の後ろにコロンと型を書きます。実行そのものには影響しませんが、IDE や型チェッカーがそれを読んで補完や検証をしてくれます。

動的型付けなのに、なぜ型を書くのか #

型ヒントは ランタイムでは無視されます。 たとえば次のコードは普通に動きます。

ignored at runtime
x: int = "this is a string"
print(x)   # this is a string

それでも型を書く理由ははっきりしています。

  1. 補完と定義ジャンプが正確になる。VS Code でも PyCharm でも型情報を見ています
  2. 型チェッカー (mypy, pyright, Pyrefly) が実行前にミスを見つけてくれる
  3. コードの読み手にとって文書になる。関数シグネチャを見るだけで、何を受けて何を返すのか分かる
  4. リファクタリング時の安全網になる。シグネチャを変えたとき、どこが壊れるか追いやすい

古い Python コードには型が付いていないことも多いですが、新しいコードではできるだけ書く、というのが今の標準です。

型ヒント — 変数、関数、コレクション #

変数 #

variable annotations
count: int = 0
name: str
name = "curtis"

name: str のように、値なしで型だけ先に書くこともできます。これはクラス属性の宣言などでもよく使います。

関数 #

function signature
def add(a: int, b: int) -> int:
    return a + b

def greet(name: str) -> None:
    print(f"hi, {name}")

引数には : 型、戻り値には -> 型 を書きます。戻り値がなければ -> None です。これがないと IDE 側は「この関数が何を返すか分からない」状態になり、その関数を使う側の補完も弱くなります。

コレクション — 組み込みジェネリクス #

リストや辞書のようなコレクションは、中に何が入るか まで含めて書きます。

collection types
nums: list[int] = [1, 2, 3]
names: list[str] = ["a", "b"]
ages: dict[str, int] = {"curtis": 30, "smith": 25}
unique: set[str] = {"a", "b"}
point: tuple[float, float] = (1.0, 2.0)

この list[int] のように組み込み型へ直接 [] を付ける記法 は Python 3.9 から使えます。それ以前は from typing import List をして List[int] と書いていました。

old style (3.8 and earlier)
from typing import List, Dict

nums: List[int] = [1, 2, 3]
ages: Dict[str, int] = {"curtis": 30}

古いコードには残っていますが、新しく書くコードでは組み込みジェネリクスに寄せておけば十分です。

None とオプショナル型 — int | None #

値があるかもしれないし、ないかもしれません。そういうときは次のように書きます。

optional type
def find_user(id: int) -> str | None:
    if id == 1:
        return "curtis"
    return None

str | None は Python 3.10 から入った Union の短縮記法 です。以前は Optional[str]Union[str, None]typing から import して使っていました。

old optional style
from typing import Optional, Union

def find_user(id: int) -> Optional[str]: ...
def parse(value: str) -> Union[int, float, None]: ...

新しいコードでは基本的に | を使えば十分です。短く、追加 import も不要です。

multiple union types
def parse(value: str) -> int | float | None:
    try:
        return int(value)
    except ValueError:
        try:
            return float(value)
        except ValueError:
            return None

数値 — intfloat #

Python の int任意精度整数 です。64bit の範囲に縛られません。

big integer
big = 10 ** 100
print(big)

一方 float は IEEE 754 の倍精度浮動小数点です。つまり、他の言語と同じように丸め誤差の問題があります。

floating point
print(0.1 + 0.2)          # 0.30000000000000004
print(0.1 + 0.2 == 0.3)   # False

金額や高精度な小数が必要なら decimal.Decimal を使います。

decimal
from decimal import Decimal

print(Decimal("0.1") + Decimal("0.2"))  # 0.3

数値リテラルの小さなコツ #

読みやすさのためにアンダースコアを挟めます。

numeric literals
ONE_MILLION = 1_000_000
HEX = 0xFF_FF
BIN = 0b_1010_1010

文字列 — str と f-string #

シングルクォートでもダブルクォートでも動きは同じです。プロジェクト内で統一されていれば十分です。

strings
s1 = "hello"
s2 = 'world'
s3 = """複数行の
文字列"""

値を埋め込むときは f-string を使います。

f-string
name = "curtis"
age = 30

print(f"hi, {name}! you are {age} years old.")
print(f"next year you'll be {age + 1}.")
print(f"{name = }")

f"{name = }" の形は、変数名と値を一緒に出力 します。ちょっとしたデバッグにかなり便利です。

t-string — Python 3.14 の新機能 #

Python 3.14 では PEP 750 によって t-string が追加されました。見た目は f-string に近いですが、その場で文字列に展開されず、Template オブジェクトとして保持される のが違いです。

t-string
from string.templatelib import Template

name = "curtis"
tpl: Template = t"hi, {name}!"

def render_html(template: Template) -> str: ...
html = render_html(t"<a href='{user_url}'>{user_name}</a>")

どこで使うのか。主な用途は ユーザー入力を安全に扱う必要がある場面 です。SQL、HTML、shell などで、f-string のように即時展開してしまうとエスケープ漏れを起こしやすい場面があります。入門段階では、まず f-string が分かれば十分ですが、ライブラリコードで t-string を見かけたら「安全な補間のための仕組みだ」と理解しておけば足ります。

bool と truthy / falsy #

bool には TrueFalse の 2 値があります。そして boolint のサブタイプ です。

bool is int
print(True + True)            # 2
print(isinstance(True, int))  # True

面白い仕様ですが、bool の代わりに int を混ぜて使うのは避けた方が無難です。意図が伝わるよう、素直に True / False を使います。

truthy / falsy #

空のコンテナや 0 は偽、それ以外は基本的に真として扱われます。

falsy values
if not []:    print("empty list is falsy")
if not "":    print("empty str is falsy")
if not 0:     print("zero is falsy")
if not None:  print("None is falsy")

そのため if not items: のような書き方は Python ではとても一般的です。

型変換 — 明示的にやる #

Python は 暗黙の型変換をあまりしません。 整数と文字列をそのまま足すとエラーになります。

explicit conversion
n = 42
s = "answer: " + str(n)
# "answer: " + n  -> TypeError

age = int(input("age: "))

よく使う変換は次の通りです。

common conversions
str(42)        # '42'
int("42")      # 42
int("42", 16)  # 66
float("3.14")  # 3.14
bool(0)        # False
bool("any")    # True
list("abc")    # ['a', 'b', 'c']

型エイリアス — type キーワード #

同じ型の形が何度も出てくるなら、名前を付けた方が読みやすくなります。

type alias (3.12+)
type UserId = int
type UserName = str
type UserMap = dict[UserId, UserName]

def get_user(id: UserId) -> UserName | None: ...

この type 構文は Python 3.12 で追加されました。それ以前は次のように書いていました。

old alias style
UserId = int

from typing import TypeAlias
UserId: TypeAlias = int

新しいコードでは type に寄せて問題ありません。

型チェッカー — mypy / pyright #

型ヒントはランタイムでは検証されません。だからこそ、静的検証のためのツール を使います。代表的なのは次の系統です。

  • mypy — もっとも古く、Python 界隈で長く使われている標準的な選択肢
  • pyright / Pylance — Microsoft 製で、高速かつ VS Code との相性が良い
  • Pyrefly — Meta が進めている新しい型チェッカー。2026年時点ではまだベータ

新規プロジェクトなら、まず pyright から始めるのが無難です。VS Code では Python 拡張を入れるだけで Pylance が動くので、導入も軽めです。

install pyright
uv add --dev pyright
uv run pyright .

簡単な例を見てみます。

check.py
def add(a: int, b: int) -> int:
    return a + b

result = add("hello", 1)
$ uv run pyright check.py
check.py:4:18 - error: Argument of type "Literal['hello']" cannot be assigned to parameter "a" of type "int"

このように、実行前に問題を見つけられます。最初は少し細かく感じるかもしれませんが、しばらく使うと手放しにくくなります。

まとめ #

今回押さえたポイントは次の通りです。

  • Python には変数宣言がありません。代入すればそれが定義
  • 基本型はまず int, float, str, bool, None を押さえる
  • 型ヒントはランタイムでは無視されるが、モダンPythonでは常に書く のが標準
  • コレクションは list[int], dict[str, int], tuple[int, str] のように組み込みジェネリクスで書く
  • オプショナルは T | None。古い Optional は新規コードではほぼ不要
  • f-string と f"{var = }" を知っておくと便利。t-string は安全な補間向け
  • int は任意精度、float は IEEE 754。精度が必要なら Decimal
  • 型エイリアスは type Name = ... (3.12+)
  • 静的検証には mypypyright

次回の #3 制御フロー では、if, while, for、そして Python 3.10 で入った match-case を扱います。他言語の switch と何が違うのかが重要なポイントです。

X