組み込み関数idは信用できない?

2012-01-22 追記

下記現象の説明がしてありました。
http://d.hatena.ne.jp/atsuoishimoto/20110426/1303772157

どうやらinstance methodというかbound methodは、束縛されるたびに新しくオブジェクトを
生成しているので、id(a.imtd1) == id(a.imtd2)は、
1. imtd1を生成する
2. imtd1はどこにも束縛されず、すぐに消えてなくなる
3. imtd2を生成する。この時、ちょうどいい感じに空いていたidを再取得(メモリ割り当て)
4. 両者とも同じidになる

という結末のようだ。

これはコードの最適化で、頻繁に行われるメソッド呼び出しをループの外に出す
、という事がよく行われるのとも関係するのかな。
その時はメソッド呼び出しのコストを無くすためだと思ってたけど。

      • -

ある日、id関数で遊んでいたら奇妙な現象を発見した。
Python2.7の日本語ドキュメントの言語リファレンス > データモデルから引用すると、

オブジェクトが一度生成されると、そのオブジェクトの アイデンティティ値 は決して変化することがありません;
アイデンティティ値をオブジェクトのメモリ上のアドレスと考えてもかまいません。
演算子 ‘is‘ は、二つのオブジェクト間のアイデンティティ値を比較します;
関数 id() は、オブジェクトのアイデンティティ値を表す整数 (現在の実装ではオブジェクトのメモリ上のアドレス) を返します。

とされている。また、id関数の項目には、

オブジェクトの “識別値” を返します。
この値は整数 (または長整数) で、このオブジェクトの有効期間は一意かつ定数であることが保証されています。
オブジェクトの有効期間が重ならない 2 つのオブジェクトは同じ id() 値を持つかもしれません。

との事。

さて、ここからが本題。上の説明をみると、メモリのアドレス云々はともかく、オブジェクトの識別値を返すんだから

class A(object):
    def imtd1(self):
        print 'i1'
    def imtd2(self):
        print 'i2'

というクラスAのimtd1とimtd2というインスタンスメソッドは違うオブジェクトだと普通は思う。
だがしかし。この2つはidが同じなのである。

a = A()
id(a.imtd1) == id(a.imtd2)  # True

幸いなことに?isや==での比較はFalseを返す

a.imtd1 == a.imtd2 # False
a.imtd1 is a.imtd2 # False

また、辞書のキーにメソッドを指定している人がいるかもしれないが、その場合にはhashが使われるので大丈夫。

hash(a.imtd1) == hash(a.imtd2) # False

極めつけには、id関数の戻り値が"変わる"。isで比較を行ったあとにもう一度idを呼んでみると

print 'first:', id(a.imtd1)
a.imtd1 is a.imtd2
print 'second', id(a.imtd1)

試しにインタプリタで試してみて欲しい(何回か試さないとダメかも)。firstとsecondで変わっているから。
仕様なら仕様でどっかに書いておいて欲しいなあ。
という訳であまり信用出来ないid関数なのでした。