Pythonでの代入について(変更不可能なTupleに代入できる?)

以下のコードでなにが出力されるだろうか?

a = (1, 2)
a = (3, 4)
print a

そもそもTupleは変更できないのでエラーを吐くのでは?とかおもってしまうが、ちゃんと代入できるし(3, 4)のほうが出力される。一方

a = (1, 2)
a[1] = 3
print a

のようなコードを書くとエラーが出る。この違いは一体なんだろうか?
Pythonの代入はオブジェクト(値)を変数に入れるというのではなくオブジェクトの参照を変数に入れている。だから、最初の例では参照先を取り替えただけでTupleの実体には一切触っていないので合法というわけ。図にしてみると
f:id:ikaro1192:20130226193001p:plain
のように参照を点線から実線に変更しても参照先の値はどこも変わってないから、Tupleは変更された事にはならず、aに束縛されているものだけが変更されるというわけだ。

参照だと勝手にインタプリタ側でされてしまってイメージがわきにくいと思う(のは私だけ?)のでC++でポインタを使って同じようなことを再現しようとすると以下のようになる。

const Tuple* a;

これでいくと

const Tuple* a = obj1;
a = &obj2;

とするのは合法だが、

const Tuple* a = &obj1;
//a[1]と同値
*(a+1) = 3;

みたいにするとエラーとなるのがわかりやすいと思う。

ちなみにTupleもオブジェクトの参照を持っていてその参照が変更できないということなので、Tupleの要素としてリストをもたせることもできそれは変更できる。

a = ([1,2], 2)
a[0][0] = 3 
print a