Python 2中奇怪的xrange()行为

时间:2013-03-14 12:48:58

标签: python

我熟悉range()xrange()之间的区别。我注意到了xrange()

的奇怪之处
>>> xrange(1,10,2)
xrange(1, 11, 2)

>>> xrange(1,10,4)
xrange(1, 13, 4)

从功能上讲,它是正确的:

>>> for item in xrange(1,10,4):
...     print item
... 
1
5
9
>>>

但是,正如您所看到的,返回的xrange对象中的停止值是最后一个合法值之后的下一个较高值。有什么理由吗?

range()现在在Python 3中提供与Python 2中xrange相同的功能,其行为符合预期:

>>> range(1,10,4)
range(1, 10, 4)
>>> range(1,10,2)
range(1, 10, 2)
>>> 

3 个答案:

答案 0 :(得分:3)

rangexrange的停止值始终为独占。

引自docs(Python 2):

  

如果step为正,则最后一个元素是最大start + i * step 小于 stop;如果step为负数,则最后一个元素是最小的start + i * step 大于 stop

对于Python 3

  

对于正面step,范围r的内容由公式r[i] = start + step*i确定,其中i >= 0 r[i] < stop

     

对于否定step,范围内容仍由公式r[i] = start + step*i确定,但约束为i >= 0 r[i] > stop


关于repr() xrange的问题的第二部分:

xrange(1, 10, 4)xrange(1, 13, 4)相同,本地python对象的repr()通常会返回有效的python代码来重新创建对象。这不需要是最初创建对象的完全相同的python代码。

答案 1 :(得分:2)

真的重要吗?

效果是一样的。 xrange()的输出中不包含10和11,而xrange(1, 11, 2) 等效xrange(1, 10, 2)

Python 2范围类型(​​xrange()的结果)存储范围长度,而不是结束值,因此要创建repr输出,它会为您计算结束值。并且因为您使用了步长值,所以计算显示公式start + length * step的结果。对于实现,长度是更重要的值,end值可以安全地丢弃并根据需要重新计算。

因此,当您创建xrange(1, 10, 2)时,它会计算范围长度并存储 而不是结束值:

if (step > 0 && lo < hi)
return 1UL + (hi - 1UL - lo) / step;
else if (step < 0 && lo > hi)
return 1UL + (lo - 1UL - hi) / (0UL - step);
else
return 0UL;

Python 3 Range对象将末尾值另外存储到长度中,因此您可以查询对象并将其显示在repr输出中。

答案 2 :(得分:2)

xrange(1, 10, 4)相当于xrange(1, 13, 4)。要使用您的示例:

>>> for item in xrange(1,13,4):
...     print item
... 
1
5
9
>>> 
Python 2中的

xrange规范化了start, stop, step个参数。在内部,xrange实现存储三重开始,步长和长度(xrange对象中的元素数)而不是开始,步骤和停止。以下是xrange.__repr__()的实施方式[1]:

rtn = PyString_FromFormat("xrange(%ld, %ld, %ld)",
                          r->start,
                          r->start + r->len * r->step,
                          r->step);

[1] https://github.com/replit/empythoned/blob/master/cpython/Objects/rangeobject.c