为什么使用不同值初始化的范围在Python 3中相互比较?
当我在我的解释器中执行以下命令时:
>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True
结果是True
。为什么会这样?为什么将具有不同参数值的两个不同range
对象视为相等?
答案 0 :(得分:71)
range
个对象是特殊的: Python会将 range
对象与 Sequences 进行比较。这实际上意味着比较并不会评估 它们代表给定的序列,而是它们所代表的。
start
,stop
和step
参数完全不同的事实在这里没有区别,因为它们在展开时都代表一个空列表:< / 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
个对象真的特殊:请注意,即使比较没有评估 它们如何代表序列,也可以使用来实现比较的结果 start
,step
的值以及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 以获取引号。它在那里记录了两个不同范围之间的比较实际上需要:简单的快速操作。 range
在EQ
和NE
个案例中使用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
个对象r0
和r1
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
使用range
,start
和stop
参数可以轻松计算step
个对象的长度。例如,在start == stop
的情况下,Python可以立即知道长度为0.在非平凡的情况下,Python可以使用start
,{{1}来执行simple arithmetic calculation }和stop
值。
因此,在step
的情况下,Python会执行以下操作:
range(0) == range(2, 2, 2)
和range(0)
在内存中是不同的对象。range(2, 2, 2)
)所以需要另一个测试。start == stop
为0.这意味着len(range(0))
也为0(之前的不等式测试失败),因此比较应该返回len(range(2, 2, 2))
。答案 3 :(得分:6)
res = range(0) == range(2, 2, 2)
其中:
range(0)
表示从0
到0
- 0
步骤的范围(此处step
等于默认值1
),列出没有值。
range(2, 2, 2)
表示从2
到2
的范围,其中步长等于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)
真且完全相同。