Python、len 和整数的大小
所以,cPython(2.4)在处理长度接近1<<32(也就是整数的大小)时,有一些有趣的表现。
r = xrange(1<<30)
assert len(r) == 1<<30
这个是没问题的,但:
r = xrange(1<<32)
assert len(r) == 1<<32
ValueError: xrange object size cannot be reported`__len__() should return 0 <= outcome
Alex的wowrange也有类似的表现。wowrange(1<<32).l
是没问题的,但len(wowrange(1<<32))
就出问题了。我猜这里可能有一些浮点数的行为(被当作负数处理)在作怪。
- 这里到底发生了什么?(下面有比较好的解答!)
- 我该如何解决这个问题? 用长整型吗?
(我具体的应用是random.sample(xrange(1<<32),ABUNCH)
,如果有人想直接解决这个问题!)
3 个回答
1<<32
这个表达式,如果把它当作一个有符号的整数来看,结果是负数。
你会发现
xrange(1 << 31 - 1)
是最后一个表现得像你想要的那样。这是因为最大带符号的(32位)整数是 2^31 - 1。
1 << 32
不是一个正的带符号32位整数(Python中的 int
数据类型),所以你会遇到那个错误。
在 Python 2.6 中,我甚至不能执行 xrange(1 << 32)
或 xrange(1 << 31)
而不出错,更别提对结果使用 len
了。
补充说明 如果你想要更多细节……
1 << 31
代表的数字是 0x80000000,在二进制补码表示法中,这是一个32位 int
能表示的最小负数(-1 * 2^31)。所以,是的,由于你正在处理的数字的位表示,它实际上变成了负数。
对于一个32位的二进制补码数字,0x7FFFFFFF 是可以表示的最大整数(2^31 - 1),在你“溢出”到负数之前。
注意,当你在提示中看到像 2147483648L 这样的数字时,末尾的 "L" 表示它现在被表示为一个“长整数”(通常是64位,我不能保证 Python 是如何处理的,因为我还没有研究过)。
cPython假设列表可以放进内存里。这种假设也适用于那些像列表一样工作的对象,比如xrange。简单来说,len
这个函数希望__len__
方法返回一个可以转换成size_t
的值。如果逻辑上元素的数量太大,即使这些元素实际上并不存在于内存中,这种转换也不会成功。