25

私は最近いじっていてid、(c?)Python が非常に賢明なことをしていることに気付きました: 小さな int が常に同じid.

>>> a, b, c, d, e = 1, 2, 3, 4, 5
>>> f, g, h, i, j = 1, 2, 3, 4, 5
>>> [id(x) == id(y) for x, y in zip([a, b, c, d, e], [f, g, h, i, j])]
[True, True, True, True, True]

しかし、数学的演算の結果についても同じことが言えるのではないかと思いました。それは次のとおりです。

>>> nines = [(x + y, 9) for x, y in enumerate(reversed(range(10)))]
>>> [id(x) == id(y) for x, y in nines]
[True, True, True, True, True, True, True, True, True, True]

n=257 で失敗し始めるようです...

>>> a, b = 200 + 56, 256
>>> id(a) == id(b)
True
>>> a, b = 200 + 57, 257
>>> id(a) == id(b)
False

しかし、より大きな数でも機能する場合があります。

>>> [id(2 * x + y) == id(300 + x) for x, y in enumerate(reversed(range(301)))][:10]
[True, True, True, True, True, True, True, True, True, True]

何が起きてる?Pythonはこれをどのように行いますか?

4

3 に答える 3

20

あなたは珍しくない罠に陥りました:

id(2 * x + y) == id(300 + x)

2 つの式2 * x + yとの300 + x有効期間は重複していません。これは、Python が左辺を計算し、その ID を取得してから、右辺を計算する前に整数を解放できることを意味します。CPython が整数を解放すると、それは解放された整数のリストに追加され、次に別の整数が必要になったときに別の整数として再利用されます。したがって、計算結果が大きく異なる場合でも、ID は一致します。

>>> x, y = 100, 40000
>>> id(2 * x + y) == id(300 + x)
True
>>> 2 * x + y, 300 + x
(40200, 400)
于 2011-05-23T19:03:59.777 に答える
17

Python は、int特定の数のオブジェクトのプールを保持します。その範囲で作成すると、実際には既存のものへの参照が取得されます。これは最適化のためだと思います。

そのプールの範囲外の数値については、新しいオブジェクトを作成しようとするたびに、新しいオブジェクトが返されるように見えます。

$ python
Python 3.2 (r32:88445, Apr 15 2011, 11:09:05) 
[GCC 4.5.2 20110127 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = 300
>>> id(x)
140570345270544
>>> id(100+200)
140570372179568
>>> id(x*2)
140570345270512
>>> id(600)
140570345270576

ソース

PyObject* PyInt_FromLong(long ival) 戻り値: 新しい参照。値が ival の新しい整数オブジェクトを作成します。

現在の実装では、-5 から 256 までのすべての整数に対して整数オブジェクトの配列が保持されます。その範囲で int を作成すると、実際には既存のオブジェクトへの参照が返されます。したがって、1 の値を変更できるはずです。この場合の Python の動作は未定義であると思われます。:-)

強調鉱山

于 2011-05-23T18:43:19.267 に答える
2

私の知る限り、idはパラメーターのサイズとは何の関係もありません。生涯一意の識別子を返す必要があり、2 つの異なるパラメーターが同時に存在しない場合は、同じ結果を返すことができます。

于 2011-05-23T18:40:51.157 に答える