タプルとアンパッキング
変更できないリスト — それがまさに欲しいものである場面、インデックスの代わりに名前を使うアンパッキング、1行での入れ替え、そして`(5)`と`(5,)`の罠。
どうやって学びますか?
解決する問題
3日分の天気を記録する必要があります。各日に必要なのは、名前、気温、そして湿度です。
リストでできます:["monday", 31.5, 78]。動きますが、引っかかる点が2つあります。
まず、そのリストは変更できます。誰かがうっかり月曜日の記録にappendしても、Pythonは一言も言いません — 記録に本来4つ目のものが入るはずはないのにです。変えてはならないものを、変えられる入れ物に入れるのは、静かなリスクです。
次に、3つの値を取り出すにはrecord[0]、record[1]、record[2]と書くことになります — 名前ではなく番号で。半年後にrecord[1]を見ても、それが気温なのか湿度なのか、誰にも分かりません。
タプルはその両方に答えます:変更できないリストです — そして、インデックスの代わりに名前を使える、アンパッキング(unpacking)という能力をもたらします。
この章を終える頃には
- タプルを作り、タプルが正しく、リストが正しくない場面を言える
- アンパッキングで、複数の値を一度に複数の名前へ入れられる
- 1行で2つの変数を入れ替えられる
(5)と(5,)を区別できる- タプルへの
appendがAttributeErrorを出す理由を説明できる
前提条件: リストのソートと、最初から使えるメソッドたち.
タプル — 変更できないリスト
角括弧ではなく丸括弧を使います:
point = (3, 7)
print(point)
print(point[0])
print(len(point))(3, 7)
3
2読み取り、インデックス指定、len、スライス、in、forでの走査 — リストにできたことはすべてそのままできます。違いはたった1つですが、それがこの話のすべてです:
point = (3, 7)
point[0] = 5TypeError: 'tuple' object does not support item assignmentこのメッセージは見覚えがあるでしょうか。第6章で文字列の中の1文字を変えようとしたときに出たものと、一字一句同じです — ただ'tuple'のところが'str'になっているだけ。Pythonの変更できないものは、みな同じ言葉で文句を言います。
変更系のメソッドもありません:
point = (3, 7)
point.append(9)AttributeError: 'tuple' object has no attribute 'append'AttributeErrorは「このオブジェクトには、その名前のものが何もない」という意味です。タプルは変更できないので、appendというメソッドがそもそも存在しません。Pythonはpoint.appendを探す段階で止まり、9を足そうとする前に終わってしまいます。前の例と比べてください:point[0] = 5はタプルが許さない操作を要求したものなのでTypeErrorでした。ここではメソッド自体が存在しないので、AttributeErrorです。
これは制限ではなく、利点です
最初は、タプルは機能を取り除いたリストに見えます。逆に考えてみてください:変えるはずのないものは、変えられない場所に置けば、間違いが起きる前に止められます。
座標(3, 7)に3つ目の数を足す意味はありません。日付(2026, 9, 16)に4つ目の要素はありません。タプルにしておけば、そういう事故は後でバグになる代わりに、その場でエラーとして届きます。
ルールとして:個数が決まっていて、それぞれが違う意味を持つもの — タプル。個数が不定で、すべて同じ種類のもの — リスト。
ですから、3日分の天気は「リストの中のタプル」です:日数は決まっていないのでリストですが、各記録がちょうど3つのものを保持するのでタプルです。
アンパッキング — 本当の利点はここに
point = (3, 7)
x, y = point
print(x)
print(y)3
71行に2つの名前と2つの値です。そしてpoint[1]ではなくyになりました — 読んだときに意味が分かる名前です。
左の名前の数は、右の値の数と一致していなければなりません:
point = (3, 7)
x, y, z = pointValueError: not enough values to unpack (expected 3, got 2)このメッセージは珍しく親切です — 何個期待していて、何個届いたかの両方を教えてくれます。
アンパッキングはタプルに限らず、リストにも使えます。ただ、タプルでこそ本当に役立ちます — 値の数が最初に分かっているのはタプルだからです。
1行での入れ替え
他の言語では2つの値を入れ替えるのに、一時的な3つ目の変数が必要です。Pythonでは不要です:
a = 1
b = 2
a, b = b, a
print(a, b)2 1この行の仕組みを理解する価値があります:Pythonはまず右辺全体を評価してタプル(2, 1)を作り、それから左辺にアンパッキングします。ですからaが変わっても、bの古い値はすでにタプルに取り込まれており、何も失われません。
括弧は実は不要です
point = 3, 7
print(point)
print(type(point))(3, 7)
<class 'tuple'>タプルを作るののはカンマであって、括弧ではありません。括弧は読みやすさのためのものです。それを知れば、a, b = b, aが魔法らしく見えるのも終わりです — 右辺のb, aは単なるタプルです。
そしてそこから、罠が1つ生まれます:
one = (5)
really = (5,)
print(type(one))
print(type(really))
print(len(really))<class 'int'>
<class 'tuple'>
1(5)はタプルではありません — 括弧の中の数で、(2 + 3)とまったく同じです。1つの値のタプルを作るにはカンマが必要です:(5,)。タイプミスのように見えますが、そのカンマがすべての違いです。
ループの中のタプル
people = [("rafi", 22), ("ahmed", 35)]
for name, age in people:
print(f"{name} is {age}")rafi is 22
ahmed is 35for name, age in peopleは各タプルをそのまま2つの名前にアンパッキングします。person[0]もperson[1]も不要です — 読んだときに、コードがそのまま意味を伝えてくれます。
知らないうちに、もう使っています:
stock = ["pen", "bag"]
for pair in enumerate(stock):
print(pair)(0, 'pen')
(1, 'bag')enumerateはタプルを返します。第12章でfor position, item in enumerate(...)と書いたとき、それをアンパッキングしていたのです — 名前を知らなかっただけです。
タプルとリストの変換
as_list = [3, 7]
as_tuple = (3, 7)
as_list.append(9)
print(as_list)
print(as_tuple)
print(list(as_tuple))
print(tuple(as_list))[3, 7, 9]
(3, 7)
[3, 7]
(3, 7, 9)list()とtuple()は片方からもう片方を作ります — そのつど新しいもので、元はそのままです。タプルを「変更」する方法はこれです:リストにして、変えて、またタプルにする。
完全な例
weather.py:
# A tuple is the right shape for a record that must not change
readings = [
("monday", 31.5, 78),
("tuesday", 33.0, 71),
("wednesday", 29.5, 85),
]
print("Day Temp Humidity")
for day, temp, humidity in readings:
print(f"{day:<10} {temp:>5} {humidity:>5}")
hottest = readings[0]
for reading in readings:
if reading[1] > hottest[1]:
hottest = reading
day, temp, humidity = hottest
print()
print(f"Hottest was {day} at {temp}")
temps = []
for _, temp, _ in readings:
temps.append(temp)
print("Temps :", temps)
print("Average :", round(sum(temps) / len(temps), 2))Day Temp Humidity
monday 31.5 78
tuesday 33.0 71
wednesday 29.5 85
Hottest was tuesday at 33.0
Temps : [31.5, 33.0, 29.5]
Average : 31.33気づく価値のあることが4つあります。
外側がリスト、内側がタプルです。日数は増えるかもしれないので、外側のものはリストです。各記録はちょうど3つのものを保持するので、内側はタプルです。実データが最もよくある形がこれです。
f"{day:<10}"とf"{temp:>5}"は列を揃えます。<は左揃え、>は右揃えで、数字は幅を示します。第6章の:.2fと同じく、コロンの後ろはすべて書式設定です。
最も暑い日を探すときにreading[1]が必要でした。比較の間は、アンパッキングされたものではなく、タプル全体が手元にあるからです。見つかったらすぐにアンパッキングされます — 次の行にインデックスがなくなるのはそのためです。
for _, temp, _ in readings — 下線は「これは要らない」という意味の慣習です。Pythonにとってただの名前ですが、読む人には、どの部分を意図的に捨てるかを伝えます。
うまくいかないとき
TypeError: 'tuple' object does not support item assignment タプルの中の値を変えようとしたものです。タプルは変更できません — 最初からリストを使うか、list(...)で変換してから変更し、またタプルに戻しましょう。
AttributeError: 'tuple' object has no attribute 'append' 同じ原因が違う服を着たものです。タプルには変更系のメソッド — append、remove、sort — は存在しません。sorted(my_tuple)は動きます:新しいリストを返して、タプルは触らないからです。
ValueError: not enough values to unpack (expected 3, got 2) 左の名前が右の値より多い状態です。print(len(record))で、実際の個数を確認できます。
ValueError: too many values to unpack (expected 2) その逆です — 名前より値が多い状態です。
1つの値のタプルを作ったのに、タプルにならない カンマがありません。(5)は数で、(5,)がタプルです。
タプルの中のリストが変わった タプルが守るのはタプル自身の構造です — 何が入っているかは変わりません。しかし、その中身の1つ自体が変更可能(リストなど)なら、そのリストの中身は変わり得ます。([1, 2], 3)では最初のリストにappendできます。
ステップ 4 / 6 — 予想する
理解度をチェック
アンパッキングの後、何が表示されますか?
point = (3, 7)
x, y = point
print(y, x)- A3 7
- B(3, 7)
- C`ValueError`
- D7 3
タプルにappendしようとしています。どのエラーが出て、なぜそのエラーなのでしょう?
point = (3, 7)
point.append(9)- A`ValueError`
- B何も起きません;タプルは変わりません
- C`AttributeError` — タプルは変更できないので、変更系のメソッドがそもそも存在しません
- D`TypeError` — タプルでは`append`は許されません
2つの行はカンマ1つだけ違います。type()は何と報告しますか?
one = (5)
really = (5,)
print(type(one))
print(type(really))- A<class 'int'> <class 'int'>
- B<class 'tuple'> <class 'tuple'>
- C<class 'int'> <class 'tuple'>
- D<class 'tuple'> <class 'int'>
回答にはアカウントが必要です
ログインして答えを確認
問題は上にあります。頭の中で答えを考えることがいちばん大切です。ログインすると、正解・解説・3 段階のヒントを確認できます。
やってみよう
contacts.pyというファイルを書き、5人以上の人のリストを保存しましょう。各人は、名前・年齢・好きな科目のタプルです。
そして:
for name, age, subject in contacts:を使って、全員を列を揃えて表示する- 最年長の人を見つけ、アンパッキングして、その人について1文表示する
- 名前だけのリストを作り、
joinで1行に表示する - 平均年齢を表示する
- 1行で2つの変数を入れ替え、それが動いたことを示す
次に、意図的に3回壊して、それぞれのメッセージを読みましょう:
- どれかのタプルの中の年齢を変えようとする
- タプルに
appendしようとする - 3つの値のタプルを2つの名前にアンパッキングしようとする
3つの違うエラーが返ってきます — TypeError、AttributeError、ValueError。どれがどれで、なぜそうなのかを自分で説明できるようになれば、タプルはあなたのものになったということです。