首先创建一个用于显示引用计数的函数(请注意,每次函数都必须为-1以获取正确的值,因为该函数本身将INCREF-s作为参数)
>>> from sys import getrefcount as rc
>>> x=1.1
>>> rc(x)-1
1
现在再次引用相同的
PyObject
:>>> y=x
>>> rc(x)-1
2
>>> rc(y)-1
2
>>> x is y
True
现在在第二个句柄
y
上执行操作:>>> y+=1
这应该在
PyNumber_InPlaceAdd
指向的PyObject
上调用y
。因此,如果这是真的,我希望
x
也能读2.1
>>> x,y
(1.1, 2.1)
>>> x is y
False
>>> rc(x)-1
1
>>> rc(y)-1
1
所以我的问题是,Python在内部做什么以提供正确的行为,而不是我从查看
PyNumber_InPlaceAdd
所期望的行为?(注意:我使用的是
1.1
;如果使用的是1
,则初始引用计数将为> 300,因为1
必须在CPython的幕后所有位置使用,并且足够聪明重用对象。)(这也引出了一个问题:如果我有
foo = 20; bar = 19; bar += 1
,这是否意味着它必须浏览其所有对象并检查是否已经存在具有该值的对象,如果可以的话,请重用它?一个简单的测试表明答案是不,这是个好消息。一旦程序变大,它就会变得非常慢。因此Python必须仅对小整数进行优化。) 最佳答案
为此,您不需要getrefcount
,只需使用id
:
>>> x = 1.1
>>> id(x)
50107888
>>> y = x
>>> id(y)
50107888 # same object
>>> y += 1
>>> id(y)
40186896 # different object
>>> id(x)
50107888 # no change there
float
对象(以及str
和int
)在Python中是不可变的,它们不能就地更改。因此,加法操作将使用新值创建一个新对象,并将其有效地分配给名称y
:temp = y + 1
y = temp
在CPython中,从
-5
到256
的整数是“ interned”的,即被存储以供重用,这样任何带有结果的操作例如1
将引用相同的对象。与每次需要这些频繁使用的值创建新对象相比,这节省了内存。没错,每次可能需要一个新对象时,都要搜索所有现有对象以查找匹配项是很痛苦的,因此只能在有限的范围内完成。使用连续范围还意味着“搜索”实际上只是数组中的偏移量。关于python - 在Python中,为什么不'y = x; y + = 1'也增加x?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27399340/