モダンPython基礎 #2 — 変数、基本型、型ヒント
#1 はじめ方と uv セットアップ で作ったプロジェクトを土台に、そのまま続けます。今回のテーマは 変数、基本型、そして型ヒント です。
Python は動的型付け言語です。変数に型を書かなくても動きます。それでも モダンPythonでは最初から型を書く という流れが標準になりました。なぜそうするのか、どう書くのかを整理していきます。
変数 — 宣言は別にない #
他の言語との大きな違いのひとつです。let, var, int x のような宣言キーワードはありません。名前に値を代入すれば、それが変数です。
name = "curtis"
age = 30
height = 175.5
is_admin = True命名規則は snake_case が基本です。JavaScript の camelCase に慣れていると最初は少し違和感があります。定数として扱う値は大文字 + アンダースコアで書きます。
MAX_RETRY = 3基本型 — 4 + 1 #
まずは最もよく使う 5 つを押さえれば十分です。
i: int = 42
f: float = 3.14
s: str = "hello"
b: bool = True
n: None = Noneこの : int の部分が 型ヒント です。変数名の後ろにコロンと型を書きます。実行そのものには影響しませんが、IDE や型チェッカーがそれを読んで補完や検証をしてくれます。
動的型付けなのに、なぜ型を書くのか #
型ヒントは ランタイムでは無視されます。 たとえば次のコードは普通に動きます。
x: int = "this is a string"
print(x) # this is a stringそれでも型を書く理由ははっきりしています。
- 補完と定義ジャンプが正確になる。VS Code でも PyCharm でも型情報を見ています
- 型チェッカー (
mypy,pyright,Pyrefly) が実行前にミスを見つけてくれる - コードの読み手にとって文書になる。関数シグネチャを見るだけで、何を受けて何を返すのか分かる
- リファクタリング時の安全網になる。シグネチャを変えたとき、どこが壊れるか追いやすい
古い Python コードには型が付いていないことも多いですが、新しいコードではできるだけ書く、というのが今の標準です。
型ヒント — 変数、関数、コレクション #
変数 #
count: int = 0
name: str
name = "curtis"name: str のように、値なしで型だけ先に書くこともできます。これはクラス属性の宣言などでもよく使います。
関数 #
def add(a: int, b: int) -> int:
return a + b
def greet(name: str) -> None:
print(f"hi, {name}")引数には : 型、戻り値には -> 型 を書きます。戻り値がなければ -> None です。これがないと IDE 側は「この関数が何を返すか分からない」状態になり、その関数を使う側の補完も弱くなります。
コレクション — 組み込みジェネリクス #
リストや辞書のようなコレクションは、中に何が入るか まで含めて書きます。
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] と書いていました。
from typing import List, Dict
nums: List[int] = [1, 2, 3]
ages: Dict[str, int] = {"curtis": 30}古いコードには残っていますが、新しく書くコードでは組み込みジェネリクスに寄せておけば十分です。
None とオプショナル型 — int | None
#
値があるかもしれないし、ないかもしれません。そういうときは次のように書きます。
def find_user(id: int) -> str | None:
if id == 1:
return "curtis"
return Nonestr | None は Python 3.10 から入った Union の短縮記法 です。以前は Optional[str] や Union[str, None] を typing から import して使っていました。
from typing import Optional, Union
def find_user(id: int) -> Optional[str]: ...
def parse(value: str) -> Union[int, float, None]: ...新しいコードでは基本的に | を使えば十分です。短く、追加 import も不要です。
def parse(value: str) -> int | float | None:
try:
return int(value)
except ValueError:
try:
return float(value)
except ValueError:
return None数値 — int と float
#
Python の int は 任意精度整数 です。64bit の範囲に縛られません。
big = 10 ** 100
print(big)一方 float は IEEE 754 の倍精度浮動小数点です。つまり、他の言語と同じように丸め誤差の問題があります。
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False金額や高精度な小数が必要なら decimal.Decimal を使います。
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2")) # 0.3数値リテラルの小さなコツ #
読みやすさのためにアンダースコアを挟めます。
ONE_MILLION = 1_000_000
HEX = 0xFF_FF
BIN = 0b_1010_1010文字列 — str と f-string
#
シングルクォートでもダブルクォートでも動きは同じです。プロジェクト内で統一されていれば十分です。
s1 = "hello"
s2 = 'world'
s3 = """複数行の
文字列"""値を埋め込むときは 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 オブジェクトとして保持される のが違いです。
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 には True と False の 2 値があります。そして bool は int のサブタイプ です。
print(True + True) # 2
print(isinstance(True, int)) # True面白い仕様ですが、bool の代わりに int を混ぜて使うのは避けた方が無難です。意図が伝わるよう、素直に True / False を使います。
truthy / falsy #
空のコンテナや 0 は偽、それ以外は基本的に真として扱われます。
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 は 暗黙の型変換をあまりしません。 整数と文字列をそのまま足すとエラーになります。
n = 42
s = "answer: " + str(n)
# "answer: " + n -> TypeError
age = int(input("age: "))よく使う変換は次の通りです。
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 UserId = int
type UserName = str
type UserMap = dict[UserId, UserName]
def get_user(id: UserId) -> UserName | None: ...この type 構文は Python 3.12 で追加されました。それ以前は次のように書いていました。
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 が動くので、導入も軽めです。
uv add --dev pyright
uv run pyright .簡単な例を見てみます。
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+) - 静的検証には
mypyかpyright
次回の #3 制御フロー では、if, while, for、そして Python 3.10 で入った match-case を扱います。他言語の switch と何が違うのかが重要なポイントです。