>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True
结果是 True
。为什么会这样?为什么具有不同参数值的两个不同的 range
对象被视为相等?
>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True
结果是 True
。为什么会这样?为什么具有不同参数值的两个不同的 range
对象被视为相等?
range
对象是特殊的:Python将比较range
对象作为序列。这实际上意味着比较不评估它们如何表示给定的序列,而是评估它们代表什么。
事实上,start
、stop
和step
参数完全不同并不重要,因为当它们扩展时都代表一个空列表:
例如,第一个range
对象:
list(range(0)) # []
还有第二个range
对象:
list(range(2, 2, 2)) # []
< p >< em >两者都代表一个空列表,由于两个空列表比较相等(True
),因此表示它们的 range
对象也将相等。
因此,您可以拥有完全不同的外观 range
对象;如果它们表示相同的序列,则它们将比较相等:
range(1, 5, 100) == range(1, 30, 100)
两者都表示只有一个元素[1]
的列表,因此这两个也相等。
range
对象确实非常特殊:请注意,即使比较不评估它们表示序列的方式,但可以仅使用range
对象的start
,step
以及len
的值来实现比较结果。这对于比较速度具有非常有趣的影响:
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中的range对象。Python 2的range()
是一个返回列表的普通函数,而2.x
xrange
对象没有与range
对象在Python 3中具有的比较能力(不仅仅是这些..)。
查看@ajcr的答案,直接引用Python 3 range
对象的源代码。文档记录了两个不同范围之间的比较实际上包括什么:简单快速的操作。在range_richcompare
函数中,range_equals
函数用于EQ
和NE
情况,并分配给PyRange_Type
类型的tp_richcompare
槽。
我认为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);
我在这里也加入了一些自己的评论;参考@ajcr的答案,查看Python等效内容。
range
返回一个普通的列表,而xrange
返回一个xrange
类型对象,它不进行智能比较。xrange(0) == xrange(2, 2, 2)
返回False
。 - SuperBiasedMan来自文档的直接引用(强调我的):
使用==和!=测试区间对象的等价性将它们视为序列进行比较。也就是说,如果两个range对象表示相同的值序列,则它们被认为是相等的。(请注意,比较相等的两个range对象可能具有不同的start、stop和step属性,例如range(0) == range(2, 1, 3)或range(0, 3, 2) == range(0, 4, 2))。
如果将range
与“相同”的列表进行比较,则会得到不相等的结果,正如文档中所述:
除了不同的数字类型外,不同类型的对象永远不会相等。
例如:
>>> 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
。
为了在这个页面上的优秀回答中添加一些额外的细节,两个range
对象r0
和r1
进行比较,大致如下:
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
使用 start
、stop
和 step
参数可以轻松地计算出 range
对象的长度。例如,当 start == stop
时,Python 可以立即知道长度为0。在非平凡的情况下,Python 只需使用 start
、stop
和 step
值进行 简单算术计算。
因此,在 range(0) == range(2, 2, 2)
的情况下,Python 执行以下操作:
range(0)
和 range(2, 2, 2)
在内存中是不同的对象。start == stop
),所以需要进行另一个测试。len(range(0))
为0。这意味着 len(range(2, 2, 2))
也为0(之前的不等式测试失败了),因此比较应该返回 True
。res = range(0) == range(2, 2, 2)
其中:
range(0)
这里的范围是从0
到0
,步长为1
(默认值),是一个没有值的列表。
range(2, 2, 2)
这意味着范围从2
到2
,步长为2
,列表中没有值。
因此,这些范围实际上是相等的。
range(0)
返回 range(0,0)
。你从0开始,到0结束,步长为1,这是未定义的,因为默认情况下第三个参数不能为0。你不能通过步长为1到达0。没有计数器的操作,因此返回0。
range(2, 2, 2)
返回 range(2, 2, 2)
。你从2开始,到2结束,但步长为2。这实际上就相当于0,因为你不需要计数到任何东西。
range(0) == range(2,2,2)
真实且完全相同。
range(2,2,2)
绝不是“基本上为0”。 - Jasper
is
和==
之间的区别。 - Jasper