immutableクイズ
>>> dic = {}
(空欄)
>>> dic
{[1]: 1, [2]: 2, [3]: 3}
>>> for k in dic.keys():
k.append(0)
>>> dic
{[1, 0]: 1, [2, 0]: 2, [3, 0]: 3}
答えは2通り以上あるけど、、どういうのとどういうの、っていうとヒントになるから言わない。
ファインマンが 「小学校の教科書で『電車はなぜ動くのでしょう?それはエネルギーが動かすからです』なんて説明はまずくないか?それを習った生徒が『エネルギー』という言葉なしで習ったことを説明できるか?できないのは、結局言葉の定義以外を覚えただけだからじゃないのか?」というようなことを言っていた。 で、(im)mutableという言葉を使わずになぜPythonの辞書にはリストが入れられないのかを説明できるだろうか?
初心者向けの解説で「リストはミュータブル(変更できる)ので辞書には入れられません」なんて説明をするのはやめたほうがいいのではないだろうか。
答えは30cm下で書く。
>>> dic = {}
>>> class Foo(object):
def __init__(self, vs):
self.value = vs
def __repr__(self):
return repr(self.value)
def append(self, v):
self.value.append(v)
>>> dic[Foo([1])] = 1
>>> dic
{[1]: 1}
>>> class Bar(list):
def __hash__(self):
return id(self)
>>> dic[Bar([2])] = 2
>>> dic
{[1]: 1, [2]: 2}
>>> class Baz(list):
def __hash__(self):
return sum(self)
>>> dic[Baz([3])] = 3
>>> dic
{[1]: 1, [2]: 2, [3]: 3}
>>> for k in dic.keys():
k.append(0)
>>> dic
{[1, 0]: 1, [2, 0]: 2, [3, 0]: 3}
このように、辞書にリストを入れること自体は別に難しいことではない。
ではなぜ、わざわざ辞書にリストが入らないようにしてあるのだろうか?
id(オブジェクトの同一性)を使って区別をするFooとBarの場合、下のような現象が起きる。
>>> dic2 = {}
>>> dic2[Bar([])] = 1
>>> dic2[Bar([])] = 2
>>> dic2[Bar([1])] = 3
>>> dic2[Bar([1])] = 4
>>> dic2
{[]: 1, []: 2, [1]: 4, [1]: 3}
1つ目の[]と2つ目の[]は別物だから。
下のコードはエラーになる。
>>> dic = {}
>>> dic[Bar([])] = 1
>>> dic
{[]: 1}
>>> k = Bar([])
>>> k
[]
>>> print dic[k]
Traceback (most recent call last):
File "", line 1, in -toplevel-
print dic[k]
KeyError: [] # 1つ目の[]と2つ目の[]は別物なのでキーが見つからない
中身を見て区別をする(同じ中身のものは同じと見なす)ようにすると、 この現象は起こらなくなる。
>>> dic = {}
>>> dic[Baz([1, 2])] = 1
>>> dic
{[1, 2]: 1}
>>> k = Baz([1, 2])
>>> k
[1, 2]
>>> print dic[k]
1
しかし、中身で分類しているので、中身を書き換えられると困る。
中身の合計が3だから3の棚にしまったので
6の棚を探しても見つからない。
>>> k = dic.keys()[0]
>>> k
[1, 2]
>>> k.append(3)
>>> k
[1, 2, 3]
>>> dic
{[1, 2, 3]: 1}
>>> print dic[Baz([1, 2, 3])]
Traceback (most recent call last):
File "", line 1, in -toplevel-
print dic[Baz([1, 2, 3])]
KeyError: [1, 2, 3]
こうなったら書き換えられないリストを作るしかないんじゃない?
というわけでタプルが生まれたのではないかと思う。
フィードバック
python使用暦もうすぐ2年ですがぶっちゃけわかりません。ちょっと考えてみます。が、現状の自分には考えるよりライブラリリファレンスを漁る必要があるのだろうと思います。案の定ミュータブルという言葉も今知りました。 答えのついでにこれはどういった場合に利用されるテクなのか知りたいところですね。
listにdelegateしたclassを作る。うーん、行数長いなあ...
listを継承して__hash__をオーバーライド