为什么 range (0) = = range (2,2,2)在 Python3中为真?

为什么在 Python3中使用不同值初始化的范围相互之间是相等的?

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

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

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

23257 次浏览

range物体是特殊的:

Python 将把 < a href = “ https://docs.python.org/3.5/library/stdtypees.html # range”rel = “ noReferrer”> range 对象比作 < a href = “ https://docs.python.org/3.5/library/stdtypees.html # 弱序列-类型-列表-元组-范围”rel = “ noReferrer”> 弱序列

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

例如,第一个 range对象:

list(range(0))  # []

第二个 range对象:

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

它们都表示一个空列表 ,因为两个空列表比较相等(True) ,所以 range对象对它们进行 代表

因此,可以有完全不同的 寻找 range对象; 如果它们表示相同的序列,它们将 比较相等:

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

它们都表示一个包含单个元素 [1]的列表,因此这两个元素也会相等。


不,range对象是 真的特殊的:

请注意,尽管这个比较不能评估 怎么做,但是它们代表了一个序列,这个序列是使用 独自一人比较 可以实现startstep以及 range对象的 len的结果; 这对比较的速度有着非常有趣的意义:

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


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

范围相比超级快:

%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 所指出的,这只适用于 Python3中的 range 对象。Python 2 range()是一个普通的 ol’函数,它返回一个列表,而 2.x xrange对象没有 Python 3中 range对象所具有的比较功能(而且不仅仅是这些)。

查看 译自: http://www.ajcr/(http://stackoverflow. com/a/35014301/4952130),直接从 Python3range对象的源代码中获取引号。其中记录了两个不同范围之间的比较实际需要的内容: 简单的快速操作。range_equals功能在 < a href = “ https://github.com/python/cpython/blob/2f37d372927a4c2c843e2813c32354979c682919/Objects/range eobject.c # L468”rel = “ noReferrer”> range_richcompare function 中用于 EQNE病例,并分配给 range_equals0。

我相信 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);

我还在这里分散了一些我自己的注释; 请参考 译自: http://www.ajcr/(http://stackoverflow. com/a/35014301/4952130)中的 Python 等价物。

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

地点:

range(0)

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

range(2, 2, 2)

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

这些范围是相等的

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

返回 range(2, 2, 2)。你从2到2开始,但是步骤是2。同样的,基本上是0,因为你不计算任何东西。

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

True 没错相同。

直接引自 那些文件(强调我的) :

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

如果你将 range与“相同”的列表进行比较,你会得到不平等,正如 那些文件所述:

除了不同的数值类型之外,不同类型的 比较平等。

例如:

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

注意,这只显式地应用于 Python3。在 Python2中,range只返回一个列表,而 range(1) == [0]的计算结果是 True

为了增加一些额外的细节,在这个页面上的优秀的答案,两个 range对象 r0r1比较 大致如下:

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

使用 startstopstep参数很容易计算出 range对象的长度。例如,在 start == stop的情况下,Python 可以立即知道长度为0。在非平凡的情况下,Python 只能使用 startstopstep值执行 简单的算术运算

因此,在 range(0) == range(2, 2, 2)中,Python 执行以下操作:

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