Python 3.8で導入されたwalrus
(ウォルラス)演算子:=
は、式の中で代入できる「代入式」を実現します。
適切に使うとコードの重複を減らし、入出力や計算結果の再利用をシンプルにできます。
本稿では、基本から実用例、注意点まで初心者向けに丁寧に解説します。
walrus(ウォルラス)演算子とは(Python 3.8の代入式)
:= の意味とメリット
:=
は「値を変数に代入し、その値をそのまま式の結果として返す」演算子です。
つまり、代入と評価を同時に行えます。
これにより、同じ関数呼び出しや計算を2回書かずに済むため、コードが短くなりバグの温床となる重複が減ります。
特に条件分岐やループの条件で値を使い回したい時に威力を発揮します。
簡単なイメージは次の通りです。
従来は値を一度変数に代入してから使っていましたが、:=
ならその場で代入し、同じ値をすぐ使えます。
text = "This is a sample text."
# 従来の書き方(代入と利用が別々)
n = len(text)
if n > 10:
print(n)
# walrus(代入と条件判定を同時に)
if (n := len(text)) > 10:
print(n)
使えるバージョン(Python 3.8+)の確認
walrus演算子はPython 3.8以降でのみ使用可能です。
バージョンは以下のコードで確認できます。
# バージョン確認と、walrus利用可否の表示
import sys
print("Python version:", sys.version)
usable = sys.version_info >= (3, 8)
print("walrus ':=' usable?", usable)
Python version: 3.13.6 (tags/v3.13.6:4e66535, Aug 6 2025, 14:36:00) [MSC v.1944 64 bit (AMD64)]
walrus ':=' usable? True
Python 3.8未満では:=
を使うとSyntaxErrorになります。
次章の注意点も参照してください。
どんな場面で便利か
具体的には次のような場面で役立ちます。
いずれも、「一度求めた値を条件や本体で繰り返し使う」という構図です。
- 入力やファイル読み取りのループ条件に、その場で読み込んだデータを使う時
re
(正規表現)のマッチ結果を、条件分岐と処理で共用する時- 内包表記の中で計算結果を再利用し、フィルタと変換を一度に行う時
- 関数の戻り値を1回だけ計算して判定と処理に使い回す時
walrus演算子の基本の使い方
ifで代入と条件判定を同時に書く
if
文では、長さのチェックなどで典型的に使えます。
重複呼び出しを避けながら読みやすくできます。
# 文字列の長さを一度だけ計算し、条件と本文の両方で使う
text = "hello walrus"
if (n := len(text)) > 10: # len(text) を n に代入し、同時に n > 10 を判定
print(f"長い文字列です: 長さ={n}")
else:
print(f"短い文字列です: 長さ={n}")
長い文字列です: 長さ=12
whileループで読み取り処理を簡潔に
ファイルやストリームからの逐次読み取りと処理を同時に書けます。
# StringIOでファイル風のオブジェクトを作成し、空行で終了する例
from io import StringIO
f = StringIO("apple\nbanana\n\ncherry\n") # 空行で打ち切るイメージ
count = 0
while (line := f.readline().strip()): # 1行読みつつ空行でループ終了
print(f"読み取り: {line}")
count += 1
print(f"合計 {count} 行を処理しました")
読み取り: apple
読み取り: banana
合計 2 行を処理しました
大きなファイルでは次のようにチャンク読みも書きやすくなります。
# チャンク読み(空文字列でEOF)の例
from io import StringIO
f = StringIO("abcdefghijklmnopqrstuvwxyz")
size = 5
chunks = []
while (chunk := f.read(size)): # 読み込んだchunkが空なら終了
chunks.append(chunk)
print(chunks)
['abcde', 'fghij', 'klmno', 'pqrst', 'uvwxy', 'z']
内包表記で計算結果を再利用する
フィルタ条件と変換の両方で同じ中間結果を使いたい時に便利です。
# 文字列リストから、strip後に数字ならint化して2倍にする
raw = [" 10", "x", " 7", "42 ", "3.14", " -5"]
# m := s.strip() を条件と変換の両方で再利用
doubles = [n * 2
for s in raw
if (m := s.strip()).isdigit() and (n := int(m)) >= 0]
print(doubles) # 'x' や '3.14' や '-5' は除外される
[20, 14, 84]
関数の戻り値を1回だけ計算して使い回す
同じ関数を何度も呼び出す必要がある場合、:=
で1回だけ実行し、結果を複数箇所で参照できます。
# 高コストな計算を模した関数
def expensive(x: int) -> int:
print(f"expensive({x}) を実行")
return x * x
x = 10
# walrusなし(2回呼ばれるので非効率)
# if expensive(x) > 50:
# print(expensive(x))
# walrusあり(1回だけ呼ぶ)
if (res := expensive(x)) > 50:
print(f"結果: {res}")
else:
print("小さいです")
expensive(10) を実行
結果: 100
優先順位と括弧の付け方
:=
の優先順位は低めです。
比較演算子や算術演算子などと組み合わせる場合、基本的に括弧で囲むのが安全で読みやすくなります。
s = "abcdef"
# 良い例: 括弧で意図が明確
if (n := len(s)) > 3:
print(n)
# 悪い例: 括弧がないと読み手が優先順位を誤解しやすい
# if n := len(s) > 3: # これは (len(s) > 3) を n に代入してしまうので意図とズレる
# ...
6
迷ったら括弧で明示する、が実務では安定した指針です。
walrus演算子の実用例(Python初心者向け)
input()の入力チェックを短く書く
入力を受け取り、数字なら整数化、空入力なら終了、という典型パターンです。
# ユーザーに何度も入力を促し、数字なら合計に加算、空行で終了する
total = 0
print("数字を入力してください(空行で終了):")
while (s := input("> ").strip()):
if s.isdigit():
total += int(s)
print(f"OK: 合計={total}")
else:
print("数字ではありません。もう一度。")
print(f"最終合計: {total}")
数字を入力してください(空行で終了):
> foo
数字ではありません。もう一度。
> 42
OK: 合計=42
> 8
OK: 合計=50
>
最終合計: 50
re(正規表現)のマッチ結果を再利用
re.search
の戻り値を条件と本文で使い回せます。
import re
text = "Name: Alice, Age: 30"
pat = re.compile(r"Name:\s*(?P<name>\w+),\s*Age:\s*(?P<age>\d+)")
if (m := pat.search(text)): # マッチオブジェクトを m に束縛
print(f"名前={m.group('name')}, 年齢={m.group('age')}")
else:
print("マッチしませんでした")
名前=Alice, 年齢=30
ファイルの各行を読み込みつつ処理
空行や特定条件で途中終了したい時に、1行読みと条件判定を同時に書けます。
# 空行で処理終了、先頭が '#' の行(コメント)は飛ばす
from io import StringIO
f = StringIO("# comment\nalpha\nbeta\n\nomega\n")
while (line := f.readline()):
line = line.rstrip("\n")
if not line: # 空行で終了
break
if line.startswith("#"):
continue
print(f"処理: {line}")
処理: alpha
処理: beta
チャンク処理も同様に書けます。
# バイナリ風のチャンク読み(EOFで終了)
from io import BytesIO
g = BytesIO(b"0123456789")
bufs = []
while (buf := g.read(3)): # 空bytes(b'')で終了
bufs.append(buf)
print(bufs)
[b'012', b'345', b'678', b'9']
リストのフィルタと変換を同時に行う
1回の内包表記で、フィルタと変換を両立できます。
# 正整数文字列だけを抽出し、int化したうえで平方にする
raw = ["10", "x", "003", "-5", "7"]
squares = [n * n
for s in raw
if (t := s.strip()).isdigit() and (n := int(t)) >= 0]
print(squares)
[100, 9, 49]
注意点とベストプラクティス
読みやすさ優先: 無理に使わない
walrus演算子は「短く書ける=常に読みやすい」ではありません。
PEP 8の精神に沿い、意図が直感的に読み取れるかを最優先にしましょう。
ネストが深い条件や長い式では、あえて通常の代入に分けた方が明快な場合もあります。
:= と = と == の違いに注意
似た記号でも意味はまったく異なります。
混同はバグの元です。
表: 代入式/代入/比較の違い
記号 | 名称 | 使える場所 | 戻り値 | 例 | 説明 |
---|---|---|---|---|---|
:= | 代入式(walrus) | 式の中 | 代入した値 | if (n := len(s)) > 5: | 代入と評価を同時に行う |
= | 代入文 | 文としてのみ | なし | n = len(s) | 代入はできるが式の一部にはできない |
== | 等値比較 | 式の中 | 真偽値 | if n == 5: | 値が等しいかの判定 |
# 典型的な間違い例(コメントで示します)
# NG: if n = 5: # 代入文は式に使えないためSyntaxError
# OK: if n == 5:
# OK: if (m := re.search(pat, s)): # 代入式は式として使える
# ...
了解です。該当セクションを正しい内容に差し替えました。
内包表記のスコープに注意(修正版)
内包表記の中で :=
(代入式)で束縛した変数は、リスト/セット/辞書内包表記では外側スコープにも現れます(最後に束縛された値が残ります)。
外側に出したくない場合は、:=
を使わずにその場で値を使うか、関数で閉じ込めるなどの方法を取りましょう。
# 内包表記のスコープ確認
vals = [1, 2, 3]
res = [(y := v * 2) for v in vals] # y は外側スコープにも現れる
print(res)
print(y) # 最後に束縛された 6 が残る
[2, 4, 6]
6
代入できるのは変数名だけ
walrus演算子で代入できる対象は「単純な変数名」に限られます。
属性や添字、アンパック代入には使えません。
# OK
if (m := len("abc")) > 0:
pass
# NG例(コメント):以下はSyntaxErrorになるため使えません
# (obj.attr := 1) # 属性への代入は不可
# (d["key"] := 2) # 添字への代入は不可
# ((a, b) := (1, 2)) # 複数代入(アンパック)は不可
Python 3.8未満ではSyntaxErrorになる
インタプリタがPython 3.8未満の場合、:=
を一切使えません。
バージョン条件によって書き分けるか、代替の通常代入にしておきましょう。
# バージョンに応じて書き分ける例
import sys
def compute_len(s: str) -> int:
return len(s)
s = "walrus"
if sys.version_info >= (3, 8):
if (n := compute_len(s)) > 3:
print(f"3.8+ OK: {n}")
else:
n = compute_len(s)
if n > 3:
print(f"fallback: {n}")
3.8+ OK: 6
まとめ
walrus演算子:=
は「代入と評価の同時実行」により、重複を減らしつつ可読性も保てる強力な道具です。
特にif
やwhile
の条件、re
のマッチ結果の再利用、内包表記でのフィルタと変換の同時実行などで威力を発揮します。
一方で、読みやすさ優先・括弧での明示・スコープの理解・3.8+限定というポイントを守ることが大切です。
最初は本稿のパターンを真似し、徐々に自分のプロジェクトに合った形で取り入れていくと、コードの明快さと保守性が高まります。