为什么 range(0) == range(2, 2, 2) 在 Python 3 中为真?
Why is range(0) == range(2, 2, 2) True in Python 3?
为什么在Python 3 中用不同值初始化的范围相互比较相等?
当我在我的解释器中执行以下命令时:
>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True
结果是True
。为什么会这样?为什么具有不同参数值的两个不同 range
对象被视为相等?
range
个对象比较特殊:
Python 将比较 range
objects as Sequences。这本质上意味着 比较不会评估 如何 它们代表给定的序列,而是 什么 它们代表。
start
、stop
和 step
参数完全不同的事实在这里没有区别,因为 它们在展开时都代表一个空列表:
例如第一个range
对象:
list(range(0)) # []
和第二个 range
对象:
list(range(2, 2, 2)) # []
两者都代表一个空列表,并且由于两个空列表比较相等 (True
),因此 代表的 range
个对象也相等 他们。
因此,您可以拥有完全不同的外观 range
对象;如果它们代表相同的序列,它们将 compare equal:
range(1, 5, 100) == range(1, 30, 100)
两者都表示具有单个元素的列表[1]
,所以这两个也比较相等。
不,range
对象确实 特殊:
但是请注意,即使比较不评估 如何 它们表示一个序列,比较的结果 也可以实现 仅使用 start
、step
的值以及 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 noted, this only applies to the range objects in Python 3. Python 2 range()
is a plain ol' function that returns a list while the 2.x
xrange
object doesn't have the comparing capabilies ( ) range
对象在 Python 3.
看看 for quotes directly from the source code on Python 3 range
objects. It's documented in there what the comparison between two different ranges actually entails: Simple quick operations. The range_equals
function is utilized in the range_richcompare
function for EQ
and NE
cases and assigned to the tp_richcompare
slot for PyRange_Type
types。
我相信 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);
我也在这里散布了一些自己的评论;查看 以获得 Python 等价物。
res = range(0) == range(2, 2, 2)
其中:
range(0)
表示范围从0
到0
- 0
步(这里step
等于默认值1
),没有值的列表。
range(2, 2, 2)
表示从2
到2
的范围,步长等于2
,没有值的列表。
所以,这些范围实际上是相等的
range(0)
returnsrange(0,0)
。您从 0 开始到 0,第 1 步,这是未定义的,因为第三个参数不能为 0 [默认]。你不能用 1 达到 0。没有计数器的动作,因此 0。
range(2, 2, 2)
returnsrange(2, 2, 2)
。你从 2 开始到 2,但步长为 2。同样,这基本上是 0,因为你什么都不算。
range(0) == range(2,2,2)
正确和完全相同。
直接引自the docs(强调我的):
Testing range objects for equality with == and != compares them as
sequences. That is, two range objects are considered equal if they
represent the same sequence of values. (Note that two range objects
that compare equal might have different start, stop and step
attributes, for example range(0) == range(2, 1, 3) or range(0, 3, 2)
== range(0, 4, 2).)
如果您将 range
s 与 "same" 列表进行比较,您将得到不平等,如 the docs 中所述:
Objects of different types, except different numeric types, never
compare equal.
示例:
>>> type(range(1))
<class 'range'>
>>> type([0])
<class 'list'>
>>> [0] == range(1)
False
>>> [0] == list(range(1))
True
请注意,这明确仅适用于 Python 3。在 Python 2 中,range
只是 returns 一个列表,range(1) == [0]
计算为 True
.
为了向本页的优秀答案添加一些额外的细节,比较了两个 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
使用 start
、stop
和 step
参数可以轻松计算 range
对象的长度。例如在start == stop
的情况下,Python可以立即知道长度为0。在non-trivial的情况下,Python可以直接做一个simple arithmetic calculation使用start
、stop
和 step
值。
所以在 range(0) == range(2, 2, 2)
的情况下,Python 执行以下操作:
- 看到
range(0)
和 range(2, 2, 2)
是内存中的不同对象。
- 计算两个对象的长度;两个长度都是 0(因为两个对象中的
start == stop
)所以需要另一个测试。
- 看到
len(range(0))
是 0。这意味着 len(range(2, 2, 2))
也是 0(之前的不等式测试失败)所以比较应该 return True
。
为什么在Python 3 中用不同值初始化的范围相互比较相等?
当我在我的解释器中执行以下命令时:
>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True
结果是True
。为什么会这样?为什么具有不同参数值的两个不同 range
对象被视为相等?
range
个对象比较特殊:
Python 将比较 range
objects as Sequences。这本质上意味着 比较不会评估 如何 它们代表给定的序列,而是 什么 它们代表。
start
、stop
和 step
参数完全不同的事实在这里没有区别,因为 它们在展开时都代表一个空列表:
例如第一个range
对象:
list(range(0)) # []
和第二个 range
对象:
list(range(2, 2, 2)) # []
两者都代表一个空列表,并且由于两个空列表比较相等 (True
),因此 代表的 range
个对象也相等 他们。
因此,您可以拥有完全不同的外观 range
对象;如果它们代表相同的序列,它们将 compare equal:
range(1, 5, 100) == range(1, 30, 100)
两者都表示具有单个元素的列表[1]
,所以这两个也比较相等。
不,range
对象确实 特殊:
但是请注意,即使比较不评估 如何 它们表示一个序列,比较的结果 也可以实现 仅使用 start
、step
的值以及 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 noted, this only applies to the range objects in Python 3. Python 2 range()
is a plain ol' function that returns a list while the 2.x
xrange
object doesn't have the comparing capabilies (range
对象在 Python 3.
看看range
objects. It's documented in there what the comparison between two different ranges actually entails: Simple quick operations. The range_equals
function is utilized in the range_richcompare
function for EQ
and NE
cases and assigned to the tp_richcompare
slot for PyRange_Type
types。
我相信 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);
我也在这里散布了一些自己的评论;查看
res = range(0) == range(2, 2, 2)
其中:
range(0)
表示范围从0
到0
- 0
步(这里step
等于默认值1
),没有值的列表。
range(2, 2, 2)
表示从2
到2
的范围,步长等于2
,没有值的列表。
所以,这些范围实际上是相等的
range(0)
returnsrange(0,0)
。您从 0 开始到 0,第 1 步,这是未定义的,因为第三个参数不能为 0 [默认]。你不能用 1 达到 0。没有计数器的动作,因此 0。
range(2, 2, 2)
returnsrange(2, 2, 2)
。你从 2 开始到 2,但步长为 2。同样,这基本上是 0,因为你什么都不算。
range(0) == range(2,2,2)
正确和完全相同。
直接引自the docs(强调我的):
Testing range objects for equality with == and != compares them as sequences. That is, two range objects are considered equal if they represent the same sequence of values. (Note that two range objects that compare equal might have different start, stop and step attributes, for example range(0) == range(2, 1, 3) or range(0, 3, 2) == range(0, 4, 2).)
如果您将 range
s 与 "same" 列表进行比较,您将得到不平等,如 the docs 中所述:
Objects of different types, except different numeric types, never compare equal.
示例:
>>> type(range(1))
<class 'range'>
>>> type([0])
<class 'list'>
>>> [0] == range(1)
False
>>> [0] == list(range(1))
True
请注意,这明确仅适用于 Python 3。在 Python 2 中,range
只是 returns 一个列表,range(1) == [0]
计算为 True
.
为了向本页的优秀答案添加一些额外的细节,比较了两个 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
使用 start
、stop
和 step
参数可以轻松计算 range
对象的长度。例如在start == stop
的情况下,Python可以立即知道长度为0。在non-trivial的情况下,Python可以直接做一个simple arithmetic calculation使用start
、stop
和 step
值。
所以在 range(0) == range(2, 2, 2)
的情况下,Python 执行以下操作:
- 看到
range(0)
和range(2, 2, 2)
是内存中的不同对象。 - 计算两个对象的长度;两个长度都是 0(因为两个对象中的
start == stop
)所以需要另一个测试。 - 看到
len(range(0))
是 0。这意味着len(range(2, 2, 2))
也是 0(之前的不等式测试失败)所以比较应该 returnTrue
。