Pythonでは、複数の変数に一行で値を代入できる強力な機能があります。
シンプルな同値代入から、アンパック代入によるリストやタプルの展開、スワップや戻り値の受け取りまで、さまざまな場面で役立ちます。
本記事では、Pythonの複数代入を体系的に整理し、実務レベルで使いこなすためのテクニックを詳しく解説します。
Pythonの複数代入とは
複数変数への一行代入の基本
Pythonの複数代入は、複数の変数に対して、一行で同時に値を割り当てる構文です。
最も基本的な形は、カンマ区切りで並べた変数と値を対応させる書き方です。

この構文は、タプルやリストの形を明示しなくても利用できます。
# 基本的な複数代入の例
# 3つの変数に3つの値を一行で代入
x, y, z = 1, 2, 3
print("x =", x)
print("y =", y)
print("z =", z)
x = 1
y = 2
z = 3
値の右辺は、カンマで区切られた並びであれば、タプルの括弧がなくてもタプルとして扱われます。
そのため、次の2つは同じ意味になります。
a, b = 10, 20 # 括弧なし
c, d = (30, 40) # 括弧あり(タプルを明示)
print(a, b)
print(c, d)
10 20
30 40
Pythonではこの形が多用されるため、複数代入に慣れておくとコードの読み書きがとても楽になります。
複数代入がPythonで多用される理由
Pythonで複数代入が好まれる理由はいくつかありますが、代表的なものは次のとおりです。
1つ目は、代入と同時に値の構造を分解(アンパック)できることです。
タプルやリスト、辞書の要素を取り出すときに、indexアクセスを繰り返す必要がなくなります。
# タプルからの取り出しを複数代入で簡潔に書く
# 関数から(名前, 年齢)のタプルが返ってくるイメージ
def get_user():
return ("Alice", 30)
name, age = get_user() # ここでアンパック代入
print(name, age)
Alice 30
2つ目は、スワップ(値の入れ替え)や一時変数なしの多重代入が簡単に書けることです。
後半で詳しく説明しますが、他言語では面倒なパターンも、Pythonなら一行で書けます。
3つ目は、for文や内包表記と非常に相性が良いことです。
繰り返し処理の中で、タプルや辞書の要素を同時に受け取りながらループする書き方は、Pythonコードで頻出パターンです。
可読性とバグ防止のメリット
複数代入を上手に使うと、コードの意図が一目で分かるようになり、バグも減らせます。
たとえば、次の2つのコードを比較してみます。
# 1: 一つずつ代入する書き方
user = get_user()
name = user[0]
age = user[1]
# 2: 複数代入を使った書き方
name, age = get_user()
1つずつ代入する書き方は、インデックス番号と意味の対応を頭の中で解読する必要があります。
一方、複数代入は「左辺の変数名」がそのまま「右辺の構造の意味」を表しているため、読んだ瞬間に意図が伝わります。
また、次のようなバグを防ぐ効果もあります。
# NG例: コピペミスによるバグ
x = 0
y = 0
z = 0
x = 1
y = 2
y = 3 # 本当はz = 3と書きたかった…
print(x, y, z) # 1 3 0 となってしまう
複数代入で書くと、こうしたコピペミスが起こりにくくなります。
# OK例: 複数代入を使う
x, y, z = 1, 2, 3
「同じタイミングで決まる値は、同じ行で代入する」というルールにしておくと、コードの見通しも良くなり、後から読む人にも優しい実装になります。
アンパック代入のテクニック
タプルとリストのアンパック代入
アンパック代入(unpacking assignment)とは、シーケンス(タプルやリストなど)の中身を、一度に複数の変数に展開して代入するテクニックです。

基本形はとてもシンプルで、タプルもリストも同じ書き方でアンパックできます。
# タプルのアンパック代入
point = (10, 20)
x, y = point # (10, 20) を x と y に展開
print("x =", x, "/ y =", y)
# リストのアンパック代入
colors = ["red", "green", "blue"]
r, g, b = colors
print("r =", r, "/ g =", g, "/ b =", b)
x = 10 / y = 20
r = red / g = green / b = blue
アンパック代入では、左辺の変数の個数と、右辺のシーケンスの要素数が一致している必要があります。
一致しない場合はValueErrorになります。
data = [1, 2, 3]
# 要素が3つなのに、変数が2つだけ → エラー
a, b = data # ValueError: too many values to unpack
この制約を柔軟にするのが、後ほど説明するアスタリスク付きアンパックです。
ネスト構造のアンパック代入
Pythonのアンパック代入は、入れ子になった構造も一度に展開できるという特徴があります。
これは、タプル・リストを組み合わせたデータを扱うときに特に便利です。

# ネストしたタプルをアンパックする例
user = ("Alice", (2020, 5, 1))
# 左辺もネストさせて書くことで、まとめて分解できる
name, (year, month, day) = user
print("name:", name)
print("joined:", year, month, day)
name: Alice
joined: 2020 5 1
同様に、リストとタプルが混ざった構造でも、左辺のパターンを右辺の構造に合わせて書いておけば、そのまま展開できます。
data = ["Bob", ["Tokyo", "Shibuya"], 28]
name, (city, ward), age = data
print(name, city, ward, age)
Bob Tokyo Shibuya 28
ネスト構造のアンパックは、「構造にラベル(変数名)を付ける」イメージで使うと、とても読みやすいコードになります。
アスタリスク(*)付きアンパックで可変長を受け取る
アンパック代入の強力な拡張として、アスタリスク付きアンパックがあります。
これは、シーケンスの「残りすべて」を一つの変数にまとめて受け取る機能です。

nums = [1, 2, 3, 4, 5]
# 先頭、真ん中(まとめて)、末尾に分割する
head, *middle, tail = nums
print("head =", head)
print("middle=", middle)
print("tail =", tail)
head = 1
middle= [2, 3, 4]
tail = 5
アスタリスク付き変数には、0個以上の要素が入り、結果はリストになります。
要素数に応じて自動調整されるため、可変長のデータを扱うときに便利です。
values = [10, 20]
first, *rest = values
print("first:", first)
print("rest :", rest) # 要素がなければ空リストになる
first: 10
rest : [20]
アスタリスクは次のように、先頭以外の位置に1つだけ使えます。
# 先頭1つ・残り全部
first, *rest = [1, 2, 3, 4]
# 先頭2つ・残り全部
a, b, *others = [1, 2, 3, 4]
# 最後の1つ・残り全部
*body, last = [1, 2, 3, 4]
アスタリスク付き変数は、左辺に1つだけという制約があります。
2つ以上書くとSyntaxErrorになります。
辞書のキーと値のアンパック代入
辞書もシーケンスとして扱うことができ、キーの列・値の列・アイテム(キーと値のペア)を、それぞれアンパック代入で受け取ることができます。

user = {"name": "Alice", "age": 30}
# keys() はキーのシーケンス
k1, k2 = user.keys()
print("keys:", k1, k2)
# values() は値のシーケンス
v1, v2 = user.values()
print("values:", v1, v2)
# items() は (キー, 値) のタプルのシーケンス
(item1_key, item1_value), (item2_key, item2_value) = user.items()
print("1st item:", item1_key, item1_value)
print("2nd item:", item2_key, item2_value)
keys: name age
values: Alice 30
1st item: name Alice
2nd item: age 30
値の個数が多い場合は、そのまま代入するよりも、for文と組み合わせて使うほうが現実的です。
次のセクションで詳しく見ていきます。
for文とアンパック代入の組み合わせ
Pythonのfor文の左側も、アンパック代入パターンとして使えます。
これにより、ループのたびにシーケンスを自動的に分解してくれるため、処理内容がとても読みやすくなります。

pairs = [(1, "one"), (2, "two"), (3, "three")]
# 各要素(タプル)を num, word に分解しながらループ
for num, word in pairs:
print(num, "=>", word)
1 => one
2 => two
3 => three
辞書のitems()と組み合わせるのも定番です。
user = {"name": "Alice", "age": 30, "city": "Tokyo"}
for key, value in user.items():
print(key, ":", value)
name : Alice
age : 30
city : Tokyo
このように、反復処理とアンパック代入はセットで覚えると、Pythonらしいコードが自然に書けるようになります。
初期化と同値代入のパターン
一行での同値代入
Pythonでは、複数の変数に同じ値を一行で代入することができます。
これは、初期化時によく使われるパターンです。

# 3つの変数を同じ値で初期化
x = y = z = 0
print(x, y, z)
0 0 0
この書き方は、同じ初期値を持つカウンタやフラグを一気に用意するときに便利です。
ただし、対象がミュータブルなオブジェクト(リストや辞書など)の場合は注意が必要です。
# ミュータブルオブジェクトでの注意点
a = b = []
a.append(1)
print("a:", a)
print("b:", b) # b も同じリストを参照している
a: [1]
b: [1]
同値代入は「同じオブジェクトを参照させる」のであって、「同じ値をコピーして作る」わけではありません。
ミュータブルオブジェクトを別々に初期化したい場合は、次のように書きます。
# 別々のリストとして初期化したい場合
a, b = [], []
依存関係のある初期化を一行で書く
複数代入は、右辺で他の変数を使いながら、一行で複数の変数を初期化することもできます。
Pythonでは、代入式全体の右辺が先に評価されるため、左から右の順ではなく「右辺全体 → 左辺全体」の順で代入が起こります。

start = 10
# 右辺は先に全部評価され、その結果が左辺に順に割り当てられる
a, b, c = start, start + 1, start + 2
print(a, b, c)
10 11 12
このルールにより、次のような「依存関係を含んだ初期化」を一行にまとめることができます。
# 長さ length のリストを作り、その半分の長さも同時に計算する
length = 10
data, half = [0] * length, length // 2
print(data)
print("half length:", half)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
half length: 5
このように、「同時に決まる値」は複数代入で束ねると、初期化ロジックが整理されて読みやすくなります。
スワップ(値の入れ替え)を一行で行う
Pythonの複数代入でもっとも有名なテクニックのひとつが、スワップ(2つの変数の値の入れ替え)を一行で書く方法です。

a = 1
b = 2
# 一時変数なしで値を入れ替える
a, b = b, a
print("a =", a)
print("b =", b)
a = 2
b = 1
他の多くの言語では、一般的に次のように一時変数を必要とします。
# 一時変数を使った従来のスワップ
a = 1
b = 2
tmp = a
a = b
b = tmp
Pythonでは、複数代入の右辺がタプルとして評価され、そのタプルが左辺にアンパックされるため、このような書き方が可能になっています。
一時変数を使わない多重代入パターン
スワップ以外にも、一時変数を使わずに、複雑な更新を一行で書くことができます。
代表的なのは、連続する値の更新や、複数の状態の同時更新です。
# 連番を管理する例
a = 0
b = 1
# a, b を同時に更新(フィボナッチ数列風)
for _ in range(5):
a, b = b, a + b
print(a, b)
1 1
1 2
2 3
3 5
5 8
このような更新を、一時変数を使って書こうとすると、次のように煩雑になります。
# 同じ処理を一時変数で書いた場合(比較用)
a = 0
b = 1
for _ in range(5):
new_a = b
new_b = a + b
a = new_a
b = new_b
print(a, b)
「一時変数で名前が増えるほど、頭の中の管理コストが上がる」ので、複数代入を使ってシンプルに表現するほうが、バグも生みにくくなります。
実践で役立つ一行代入テクニック集
if文・条件式と複数代入を組み合わせる
複数代入は、条件によってまとめて値を変えるような場面でも使えます。
Pythonのif文や三項演算子(条件式)と組み合わせると、分岐ロジックをコンパクトに書けます。

mode = "debug"
# 条件式を使って一行で複数代入
log_level, verbose = ("DEBUG", True) if mode == "debug" else ("INFO", False)
print("log_level:", log_level)
print("verbose :", verbose)
log_level: DEBUG
verbose : True
条件分岐そのものをif文で書き、代入だけ複数代入にするパターンもあります。
is_admin = True
if is_admin:
role, permissions = "admin", ["read", "write", "delete"]
else:
role, permissions = "user", ["read"]
print(role, permissions)
admin ['read', 'write', 'delete']
「分岐ごとに変数が増減しない」ため、後続の処理から見るととても扱いやすくなります。
関数の戻り値を複数代入で受け取る
Pythonでは、関数から複数の値を返し、呼び出し側で複数代入で受け取るというスタイルが一般的です。
内部的にはタプルを返しているだけですが、見た目が自然なので違和感なく使えます。

def calc(x, y):
# 足し算と引き算の結果を同時に返す
return x + y, x - y
sum_, diff = calc(10, 3)
print("sum :", sum_)
print("diff:", diff)
sum : 13
diff: 7
戻り値のタプルをそのまま受け取ることもできますが、使う側でアンパックして名前を付けるほうが、後続の処理が読みやすくなります。
# タプルのまま受け取る例(あまり推奨されない)
result = calc(10, 3)
print(result[0], result[1]) # インデックスの意味を覚えないといけない
# アンパックして受け取る例(推奨)
sum_, diff = calc(10, 3)
print(sum_, diff)
列挙(enumerate)やzipと複数代入
Pythonの標準的なイテレータenumerateやzipは、複数代入と組み合わせて使うことを前提に設計されていると言ってもよいほど、相性が抜群です。

colors = ["red", "green", "blue"]
# enumerate: (インデックス, 要素)のタプルを返す
for index, color in enumerate(colors):
print(index, "=>", color)
0 => red
1 => green
2 => blue
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
# zip: 複数のシーケンスをまとめて走査
for name, score in zip(names, scores):
print(name, "scored", score)
Alice scored 85
Bob scored 92
Charlie scored 78
for文の左辺でタプル構造をそのまま書くことで、ループの中身が非常に読みやすくなっていることが分かります。
複数代入とアンパック代入の書き方の注意点
便利な複数代入・アンパック代入ですが、濫用すると逆に読みにくくなることがあります。
ここでは、書き方の注意点を整理します。
1つ目は、左辺を複雑にしすぎないことです。
ネストしたアンパックは強力ですが、入れ子が2段、3段と深くなると、読み手が構造を理解するのに苦労します。
# 避けたほうがよい例(ネストが深く読みづらい)
(a1, (b1, b2), (c1, (d1, d2))) = complex_structure
このような場合は、途中の段階で変数に受けてから分解するほうが、かえって分かりやすいことが多いです。
head, middle, tail = complex_structure
a1 = head
b1, b2 = middle
c1, d = tail
d1, d2 = d
2つ目は、同値代入とミュータブルオブジェクトの組み合わせに注意することです。
前述のように、a = b = []は同じリストを共有します。
意図せず共有したくない場合は、必ず別々にリストを作るようにしましょう。
3つ目は、アンパック時の要素数のミスマッチです。
要素数が変動する可能性のあるシーケンスをアンパックする場合は、アスタリスク付きアンパックを使って「残り全部」を受けるなど、エラーにならないよう設計しておくと安全です。
# 要素数が変わる可能性がある場合
values = get_values() # 要素数がその時々で違う
head, *rest = values # 少なくとも1要素はある、という前提を表現
コード例で学ぶPython複数代入のベストプラクティス
最後に、現場でよく使われる複数代入パターンを、まとめてコード例として示します。
どれも日常的によく登場する書き方です。

# 1. スワップ(値の入れ替え)
x, y = 10, 20
x, y = y, x
# 2. 関数からの複数戻り値
def divide(a, b):
q = a // b
r = a % b
return q, r
quotient, remainder = divide(17, 5)
# 3. enumerate と組み合わせたループ
items = ["apple", "banana", "orange"]
for i, item in enumerate(items, start=1):
print(i, item)
# 4. zip で複数リストをまとめて処理
names = ["Alice", "Bob"]
ages = [30, 25]
for name, age in zip(names, ages):
print(name, age)
# 5. 辞書 items() とアンパック
config = {"host": "localhost", "port": 5432}
for key, value in config.items():
print(key, "=", value)
# 6. 条件による一括設定
mode = "production"
debug, log_level = (True, "DEBUG") if mode == "debug" else (False, "INFO")
# 7. ネスト構造のアンパック
record = ("Alice", (2020, 5, 1))
name, (year, month, day) = record
# 8. アスタリスク付きアンパックで先頭と残りを分ける
nums = [1, 2, 3, 4, 5]
first, *middle, last = nums
1 apple
2 banana
3 orange
Alice 30
Bob 25
host = localhost
port = 5432
これらのパターンを組み合わせることで、「短いけれど意味が明確で、安全性も高い」Pythonコードを実現できます。
まとめ
Pythonの複数代入とアンパック代入は、単なる「一行で書けるテクニック」ではなく、コードの構造と意図をそのまま表現するための重要な言語機能です。
タプルやリストのアンパック、アスタリスク付きの可変長受け取り、辞書やfor文との連携、スワップや初期化パターンなどを組み合わせることで、可読性が高くバグの少ないコードを書けるようになります。
本記事で紹介したベストプラクティスを意識しながら、自分のプロジェクトのコードを少しずつ複数代入スタイルにリファクタリングしていくと、その効果を実感できるはずです。
