Pythonのタプルについて(三度目)

質問されたので答えてみた。

Q: 「一回目の記事を読んでなるほどと思ったのだけど、二回目の記事には『昔書いたよくない説明』と書いてあって、どのへんがよくないのかがわからないので教えてほしいです。」

A: 一言で説明すると「『変更可能だからハッシュ値を計算できず辞書のキーにすることができない』という説明は厳密には正しくない」となる。たとえばlistを継承してハッシュ関数を定義すれば変更可能なまま辞書のキーにすることが可能になるし、ユーザ定義のクラスのインスタンスはデフォルトのハッシュ関数を継承するので辞書のキーにすることができる。Pythonのリストが辞書のキーにできないのは「技術的に不可能」なのではなく「意図的に禁止してある」のだ。

        • -

以下86世代チャットのログを適当に編集して読みやすくしたもの。

N: どう納得した?
A: すげー便利じゃんっていう。タプルの使いどころみたいなものがいまいちわかってなかったけど、なるほど!と
N: うん、便利便利。タプルが辞書のキーになるのは便利。でもリストが辞書のキーになれないのはなぜ?なぜタプルとリストなんて2種類作って、片方のリストは辞書のキーになれないなんてことになってるの?
A: pythonの辞書の仕様
N: なんでそんな仕様にしたのか、ということね。だってリストが辞書のキーになれるならタプルいらないってことでしょ。
A: リストだとmutableだからこまる?
N: mutableだとなぜ困る?
A: リストの内容変えてもkeyとして使えて直観的でないとか
N: うん、それだ。リストをキーにして辞書に入れた後、そのリストを書き換えてしまったときに一体どういうことがおこるのか。
N: dict = {}; key = [1, 2]; dict[key] = 1したあとでkey.append(3)したときに、dict[key]が取得できるのかできないのか。
N: これはハッシュ関数の実装による。取得できるようにハッシュ関数を実装したなら「中に入っている物に無関係にハッシュ値が決まる」ということだよね。それだとdict = {[1, 2]: 1}に対してdict[[1, 2]]したときに値が取れない。なぜかというとその二つのリストは「たまたま中に入っている値が同じなだけの別のリスト」だから。
A: ああ、なるほど。
N: 逆にリストの内容が同じならば同じ値になるようにハッシュ関数を実装したなら「中に入っている物によってハッシュ値が決まる」わけだから、key.append(3)するとハッシュ値が変わってしまいdict[key]で値を取れなくなる。キーを書き換えても大丈夫にすることと、dict[[1, 2]]で値が取れるようにすることは両立できない。
A: なるほどなるほど
N: ちなみにPythonは「変更できないリスト」であるタプルを導入するって解決策をとったのだけど、Rubyは「キーにリストを入れられるかわりに、キーを書き換えたら値が取れなくなる」という選択肢を選んでいる。どちらを選ぶかってのは言語の設計上では選択肢の一つにすぎないんだな。実際問題としてハッシュのキーを書き換えて嬉しいシチュエーションって思いつかないし。
U: rubyは大クラス主義でなんでもありなんでそういうゆるい考え方、ととらえたらいいかな
N: 個人的にはタプルの導入は失敗だと思っている。Ruby流でいいじゃんと。
U: pythonにはちゃんと配列とタプル両方用意されてるんか
N: リストとタプルの2つがある。「なんだこれ、どう使い分けるんだ」ってのが誰しもが通る道w
U: ということは、リストは中身が同一型制限あり?
N: のん
U: うええ
N: 中身に同一型の制限があるのはarrayw http://www.python.jp/doc/current/lib/module-array.html
U: ああ、リストとarrayとタプルがあるんかww それならリストとタプルの使い分けがわからんなあ
N: でしょでしょ
U: haskellとかだと明らかに違うから使いわけを考えるまでもないんだよね >list, tuple, array