よく見る文
python tupleの要素は変更できない --- (1)
変更をしようとすると確かに、エラーが出ます。
>>> a = (1, 2, 3)
>>> a[0] = 10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
Webで調べると、tupleは要素の変更ができないとさんざん言われていますので、私は催眠術にかかってしまったのですが、ある時目が覚めました。以下の実験例を見てください。
>>> a = (1, [2, 3], '4', {5: 6}) # --- (2)
>>> a[1].append(4)
>>> a
(1, [2, 3, 4], '4', {5: 6}) # --- (3)
>>> a[-1][7] = 8
>>> a
(1, [2, 3, 4], '4', {5: 6, 7: 8}) # --- (4)
要素の変更ができています。これは、二つ目の要素はlistでmutable、最後の要素はdictでこれもmutableなので、そういわれればそうか、と思います。
>>> a = (1, [2, 3], '4', {5: 6})
>>> type(a[1])
<class 'list'>
>>> type(a[-1])
<class 'dict'>
では、よく見る上の(1)の文はなんなんでしょうか。間違いなんでしょうか、もし間違いなら、そんなことがあちらこちらで同様に言われているのはなぜでしょうか。もしかしたら、(2), (3), (4)は同じ名前ではあるが、その都度新たにobjectが作られているだけなのかもしれません。調べてみます。
>>> a = (1, [2, 3], '4', {5: 6}) # --- (5)
>>> id(a)
1643952602144
>>> a[1].append(4)
>>> a
(1, [2, 3, 4], '4', {5: 6}) # --- (6)
>>> id(a)
1643952602144
>>> a[-1][7] = 8
>>> a
(1, [2, 3, 4], '4', {5: 6, 7: 8}) # --- (7)
>>> id(a)
1643952602144
idは変わらない。idがすべて同じということは、(5), (6), (7)はidentical objectです。このcontextではtupleの要素は変更可能に見えます。
いま、二つ目の要素をリストとして構築していますが、もしかしたら別のリストへの変更も可能でしょうか?[2, 3]
を、内容としては同じ[2, 3]
としてみます。
>>> a = (1, [2, 3], '4', {5: 6})
>>> a[1] = [2, 3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
だめでした。最初と同じTypeErrorです。メッセージがまさに真髄を述べています。「tupleはitemのassignmentをサポートしません」。そうなんですね、
tupleでは並べた要素のre-assignmentができない。
それが本質のようです。だから、re-assignがからまない、listやdictの変更であれば(どちらもmutable)、エラーなく通るわけですね。
最初の、
>>> a = (1, 2, 3)
>>> a[0] = 10 # --- (8)
がエラーになるのは、まさに一番目の要素がint、immutableだからです。(8)では要素のre-assignmentをしてしまっています。
一見わかりにくいですが、たとえばi += 1
、s += 'def'
という文では変数のre-assignmentが起こります。それぞれi = i + 1
、s = s + 'def'
と書けますね。int、strはどちらもimmutableなので、これらの文ではre-assignmentが起こります。以下の実験結果でエラーが出ることとconsistentです。
>>> a = (1, 'abc')
>>> a[0] += 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> a[1] += 'def'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
idを調べてre-assignmentの確認をしてみます。idが操作でかわることを見てください。
>>> i = 1
>>> id(i)
140718230349600
>>> i += 1
>>> i
2
>>> id(i)
140718230349632
>>> s = 'abc'
>>> id(s)
1643952123760
>>> s += 'def'
>>> s
'abcdef'
>>> id(s)
1643952641200
以上から、(1)の文は、
python tupleの要素はre-assignすることができない --- (1')
と理解したほうがいいと思います。mutable要素は変更可能だが、その要素をre-assignすることはできない。immutablesはもとより変更不可なので、変更に見えることをしようとするとre-assignmentとなって通らない。
tupleの使いどころ
tupleは、要素のre-assignがきかないことを利用して、一度構築したら変わらないものを表現するのに使えるので、listでも実質問題ないけどtupleね。わかってね、この気持ち。を表現するのに使えます。そう考えると、上の例のようにmutableな要素を入れること自体、最初からお行儀が悪いのかもしれません。
私が意識してtupleを使う場面は、複数の値が一塊になったものをdictのkeyとして使いたい時です。tupleはdictのkeyとして使えます。しかし、mutableを要素として含んでしまうと叱られます(errorがでます)ので、dict keyとして使いたい時にはimmutableだけを要素として含むtupleを用意しましょう。
まとめ
tupleは変更不可、tupleの要素は変更不可、このような文を何度も何度も見ているうちに、tupleはとにかく変更できない、と思ってしまうと、もしmutableな要素があった場合、予期せず変更が加わったりする可能性があるので注意が必要です。これを逆手にとって、ある要素だけはちょっぴり変更できるように忍ばせておく、という使い方はできるかもしれませんが、お行儀としては、mutableな要素は入れないことだと思います。
まとめのまとめ:python tupleの要素はre-assignがきかない