首先创建一个显示引用计数的函数(注意我们每次都要-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
必须在后面的地方使用foo = 20; bar = 19; bar += 1
CPython中的场景,它足以聪明地重用对象。)
(这也引出了一个问题:如果我有{{1}}这是否意味着它必须查看其所有对象并检查是否已存在具有此值的对象,如果是,则重用它?一个简单的测试表明答案是否定的。这是个好消息。一旦程序规模变大,它就会非常慢。所以Python必须只针对小整数进行优化。)
答案 0 :(得分:4)
你不需要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中是 immutable ,它们无法就地更改。因此,添加操作使用新值创建一个新对象,并将其有效地分配给名称y
:
temp = y + 1
y = temp
在CPython中,从-5
到256
的整数是" interned",即存储以便重复使用,以便任何具有结果的操作,例如1
将提供对同一对象的引用。与每次需要时为这些常用值创建新对象相比,这节省了内存。您是对的,每次需要新对象时搜索所有现有对象以获得匹配将是一件痛苦的事情,因此这只能在有限的范围内完成。使用连续范围也意味着"搜索"实际上只是数组中的偏移量。
答案 1 :(得分:0)
现在对第二个句柄执行操作,y:
>>> y+=1
这应该在y的PyObject上调用PyNumber_InPlaceAdd 指向。
到此为止,你是对的。
但是,就地添加数字会返回一个不同的对象,而不是旧对象。
旧的,因为它是不可改变的,保持其价值。