为什么在Python 3中范围(0)==范围(2,2,2)为真?

时间:2016-01-25 23:04:01

标签: python python-3.x range identity python-internals

为什么使用不同值初始化的范围在Python 3中相互比较?

当我在我的解释器中执行以下命令时:

>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True

结果是True。为什么会这样?为什么将具有不同参数值的两个不同range对象视为相等?

5 个答案:

答案 0 :(得分:71)

range个对象是特殊的:

Python会将 range 对象与 Sequences 进行比较。这实际上意味着比较并不会评估 它们代表给定的序列,而是它们所代表的

startstopstep参数完全不同的事实在这里没有区别,因为它们在展开时都代表一个空列表:< / p>

例如,第一个range对象:

list(range(0))  # []

和第二个range对象:

list(range(2, 2, 2)) # []

两者都代表一个空列表,因为两个空列表比较相等(True)所以代表它们的range个对象。< / p>

因此,您可以拥有完全不同的外观 range对象;如果它们代表相同的序列,它们将比较相等:

range(1, 5, 100) == range(1, 30, 100) 

两者都代表一个包含单个元素[1]的列表,因此这两个元素也将相等。

不,range个对象真的特殊:

请注意,即使比较没有评估 它们如何代表序列,也可以使用来实现比较的结果 startstep的值以及len个对象的range;这与比较的速度有着非常有趣的含义:

r0 = range(1, 1000000)    
r1 = range(1, 1000000)

l0 = list(r0)    
l1 = list(r1)

Ranges比较超快:

%timeit r0 == r1
The slowest run took 28.82 times longer than the fastest. This could mean that an intermediate result is being cached 
10000000 loops, best of 3: 160 ns per loop

另一方面,列表..

%timeit l0 == l1
10 loops, best of 3: 27.8 ms per loop

是啊..

正如 @SuperBiasedMan 所指出的,这仅适用于Python 3中的范围对象.Python 2 range()是一个普通的&#39;在2.x xrange对象没有{{1>}个对象所具有的比较功能( and not only these.. )时返回列表的函数Python 3。

直接从Python 3 range对象的源代码中查看 @ajcr's answer 以获取引号。它在那里记录了两个不同范围之间的比较实际上需要:简单的快速操作。 rangeEQNE个案例中使用range_equals函数并分配给 range_richcompare function

我相信range_equals的实现非常易读(因为它很简单)在这里添加:

/* r0 and r1 are pointers to rangeobjects */

/* Check if pointers point to same object, example:    
       >>> r1 = r2 = range(0, 10)
       >>> r1 == r2
   obviously returns True. */
if (r0 == r1)
    return 1;

/* Compare the length of the ranges, if they are equal 
   the checks continue. If they are not, False is returned. */
cmp_result = PyObject_RichCompareBool(r0->length, r1->length, Py_EQ);
/* Return False or error to the caller
       >>> range(0, 10) == range(0, 10, 2)  
   fails here */
if (cmp_result != 1)
    return cmp_result;

/* See if the range has a lenght (non-empty). If the length is 0
   then due to to previous check, the length of the other range is 
   equal to 0. They are equal. */
cmp_result = PyObject_Not(r0->length);
/* Return True or error to the caller. 
       >>> range(0) == range(2, 2, 2)  # True
   (True) gets caught here. Lengths are both zero. */
if (cmp_result != 0)
    return cmp_result;

/* Compare the start values for the ranges, if they don't match
   then we're not dealing with equal ranges. */
cmp_result = PyObject_RichCompareBool(r0->start, r1->start, Py_EQ);
/* Return False or error to the caller. 
   lens are equal, this checks their starting values
       >>> range(0, 10) == range(10, 20)  # False
   Lengths are equal and non-zero, steps don't match.*/
if (cmp_result != 1)
    return cmp_result;

/* Check if the length is equal to 1. 
   If start is the same and length is 1, they represent the same sequence:
       >>> range(0, 10, 10) == range(0, 20, 20)  # True */
one = PyLong_FromLong(1);
if (!one)
    return -1;
cmp_result = PyObject_RichCompareBool(r0->length, one, Py_EQ);
Py_DECREF(one);
/* Return True or error to the caller. */
if (cmp_result != 0)
    return cmp_result;

/* Finally, just compare their steps */
return PyObject_RichCompareBool(r0->step, r1->step, Py_EQ);

我也在这里散布了一些自己的评论;查看 tp_richcompare slot for PyRange_Type types 获取Python等效项。

答案 1 :(得分:13)

直接引自the docs(强调我的):

  

使用==和!=测试范围对象是否相等,将它们比较为   序列。也就是说,两个范围对象被认为是相等的   表示相同的值序列。 (注意两个范围对象   比较相等可能有不同的开始,停止和步骤   属性,例如range(0)== range(2,1,3)或range(0,3,2)   ==范围(0,4,2)。)

如果您将range与&#34;相同&#34;进行比较列表,您将获得不平等,如the docs中所述:

  

不同类型的对象,除了不同的数字类型,从不   比较平等。

示例:

>>> type(range(1))
<class 'range'>
>>> type([0])
<class 'list'>
>>> [0] == range(1)
False
>>> [0] == list(range(1))
True

请注意,这仅适用于Python 3.在Python 2中,range只返回一个列表,range(1) == [0]的计算结果为True

答案 2 :(得分:10)

要在此页面上为优秀答案添加一些其他详细信息,我会比较两个range个对象r0r1 roughly as follows

if r0 is r1:                 # True if r0 and r1 are same object in memory
    return True
if len(r0) != len(r1):       # False if different number of elements in sequences
    return False
if not len(r0):              # True if r0 has no elements
    return True
if r0.start != r1.start:     # False if r0 and r1 have different start values
    return False
if len(r0) == 1:             # True if r0 has just one element
    return True
return r0.step == r1.step    # if we made it this far, compare step of r0 and r1

使用rangestartstop参数可以轻松计算step个对象的长度。例如,在start == stop的情况下,Python可以立即知道长度为0.在非平凡的情况下,Python可以使用start,{{1}来执行simple arithmetic calculation }和stop值。

因此,在step的情况下,Python会执行以下操作:

  1. 发现range(0) == range(2, 2, 2)range(0)在内存中是不同的对象。
  2. 计算两个对象的长度;两个长度都是0(因为两个对象都是range(2, 2, 2))所以需要另一个测试。
  3. 看到start == stop为0.这意味着len(range(0))也为0(之前的不等式测试失败),因此比较应该返回len(range(2, 2, 2))

答案 3 :(得分:6)

res = range(0) == range(2, 2, 2)

其中:

range(0)

表示从00 - 0步骤的范围(此处step等于默认值1),列出没有值。

range(2, 2, 2)

表示从22的范围,其中步长等于2,列出没有值。

所以,这些范围真的相等

答案 4 :(得分:5)

index返回range(0)。从步骤1开始,从0开始到0,这是未定义的,因为第三个参数不能为0 [默认]。你不能用1来达到0.没有计数器的动作,因此为0。

range(0,0)返回range(2, 2, 2)。你从2开始到2但是步长为2.再次,基本上是0,因为你没有计算任何东西。

range(2, 2, 2)

完全相同。