Why do ranges which are initialized with different values compare equal to one another in Python 3?
为什么用不同值初始化的范围在Python 3中是相等的?
When I execute the following commands in my interpreter:
当我在我的解释器中执行以下命令时:
>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True
The result is True
. Why is this so? Why are two different range
objects with different parameter values treated as equal?
结果是正确的。为什么会这样呢?为什么两个不同的范围对象具有不同的参数值?
5 个解决方案
#1
68
The range
objects are special:
Python will compare range
objects as Sequences. What that essentially means is that the comparison doesn't evaluate how they represent a given sequence but rather what they represent.
Python将比较范围对象作为序列。这从本质上说,比较不是评价它们如何表示一个给定的序列,而是评价它们表示什么。
The fact that the start
, stop
and step
parameters are completely different plays no difference here because they all represent an empty list when expanded:
实际上,开始、停止和步骤参数完全不同,在这里没有区别,因为它们都表示扩展后的空列表:
For example, the first range
object:
例如,第一个范围对象:
list(range(0)) # []
and the second range
object:
第二范围对象:
list(range(2, 2, 2)) # []
Both represent an empty list and since two empty lists compare equal (True
) so will the range
objects that represent them.
它们都表示一个空列表,由于两个空列表比较相等(True),因此表示它们的range对象也一样。
As a result, you can have completely different looking range
objects; if they represent the same sequence they will compare equal:
因此,你可以有完全不同的外观范围对象;如果它们表示相同的序列,它们将比较相等:
range(1, 5, 100) == range(1, 30, 100)
Both represent a list with a single element [1]
so these two will also compare equal.
它们都表示一个包含单个元素的列表[1],因此这两个元素也会比较相同。
No, range
objects are really special:
Do note, though, that even though the comparison doesn't evaluate how they represent a sequence the result of comparing can be achieved using solely the values of start
, step
along with the len
of the range
objects; this has very interesting implications with the speed of comparisons:
但是,请注意,即使比较没有评估它们如何表示一个序列,仅使用start的值就可以实现比较的结果,与range对象的len一起执行;这对比较的速度有非常有趣的影响:
r0 = range(1, 1000000)
r1 = range(1, 1000000)
l0 = list(r0)
l1 = list(r1)
Ranges compares super fast:
范围比较超快:
%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
on the other hand, the lists..
另一方面,清单。
%timeit l0 == l1
10 loops, best of 3: 27.8 ms per loop
Yeah..
是的. .
As @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 (and not only these..) that range
objects have in Python 3.
正如@SuperBiasedMan指出的,这只适用于Python 3中的range对象。Python 2 range()是一个普通的ol'函数,它返回一个列表,而2返回一个列表。xrange对象不具有Python 3中范围对象所具有的比较能力(而且不仅仅是这些..)。
Look at @ajcr's answer 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.
看看@ajcr从Python 3系列对象的源代码直接引用的答案。这里记录了两个不同范围之间的比较实际需要的内容:简单的快速操作。在EQ和NE的range_richcompare函数中使用range_equals函数,并将其分配给PyRange_Type类型的tp_richcompare槽。
I believe the implementation of range_equals
is pretty readable (because it is nice as simple) to add here:
我相信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);
I've also scattered some of my own comments here; look at @ajcr's answer for the Python equivalent.
我也散布了一些我自己的评论;看看@ajcr对Python等价类的回答。
#2
13
Direct quote from the docs (emphasis mine):
直接引用文件(重点是我的):
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(0) = range(2,1,3)或range(0,3,2) = range(0,4,2))。
If you compare range
s with the "same" list, you'll get inequality, as stated in the docs as well:
如果您将范围与“相同”列表进行比较,您将得到不等,正如文档中所述:
Objects of different types, except different numeric types, never compare equal.
不同类型的对象,除了不同的数值类型外,从不相等地进行比较。
Example:
例子:
>>> type(range(1))
<class 'range'>
>>> type([0])
<class 'list'>
>>> [0] == range(1)
False
>>> [0] == list(range(1))
True
Note that this explicitly only applies to Python 3. In Python 2, where range
just returns a list, range(1) == [0]
evaluates as True
.
注意,这只显式地适用于Python 3。在Python 2中,range只返回一个列表,range(1) =[0]计算为True。
#3
10
To add a few additional details to the excellent answers on this page, two range
objects r0
and r1
are compared roughly as follows:
为了给这个页面上的优秀答案增加一些额外的细节,我们将两个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
The length of a range
object is easily to calculate using the start
, stop
and step
parameters. In the case where start == stop
, for example, Python can immediately know that the length is 0. In non-trivial cases, Python can just do a simple arithmetic calculation using the start
, stop
and step
values.
使用开始、停止和步骤参数很容易计算范围对象的长度。例如,在start == stop的情况下,Python可以立即知道长度为0。在非平凡的情况下,Python可以使用start、stop和step值进行简单的算术计算。
So in the case of range(0) == range(2, 2, 2)
, Python does the following:
因此,对于range(0) = range(2,2,2), Python执行以下操作:
- sees that
range(0)
andrange(2, 2, 2)
are different objects in memory. - 看到range(0)和range(2,2,2)是内存中的不同对象。
- computes the length of both objects; both lengths are 0 (because
start == stop
in both objects) so another test is needed. - 计算两个对象的长度;两个长度都为0(因为在两个对象中start = stop),因此需要进行另一个测试。
- sees that
len(range(0))
is 0. This means thatlen(range(2, 2, 2))
is also 0 (the previous test for inequality failed) and so the comparison should returnTrue
. - 看到len(range(0))是0。这意味着len(range(2,2,2))也是0(之前的不等式检验失败),所以比较应该返回True。
#4
6
res = range(0) == range(2, 2, 2)
res = range(0) = range(2,2,2)
Where:
地点:
range(0)
means the range from 0
to 0
- 0
steps (here step
equals to default value 1
), list without values.
表示从0到0 - 0的范围(这里的步骤等于默认值1),列表中没有值。
range(2, 2, 2)
means the range from 2
to 2
with step equals to 2
, list without values.
表示步骤等于2到2的范围,列表中没有值。
So, these ranges are really equal
这些范围是相等的
#5
5
range(0)
returns range(0,0)
. You start from 0 up to 0 with step 1, which is undefined since the third argument can't be 0 [by default]. You can't reach 0 with 1. No counter's action in place, therefore 0.
范围(0)返回范围(0,0)。从0到0开始,第1步没有定义,因为第三个参数不能为0[默认]。1不能达到0。没有计数器的作用,因此0。
range(2, 2, 2)
returns range(2, 2, 2)
. You start from 2 up to 2 but with step of 2. Which again, is basically 0 since you don't count up to anything.
范围(2,2,2)返回范围(2,2,2)。从2到2,但步骤2。这基本上是0,因为你不计算任何东西。
range(0) == range(2,2,2)
True and exactly the same.
完全一样。
#1
68
The range
objects are special:
Python will compare range
objects as Sequences. What that essentially means is that the comparison doesn't evaluate how they represent a given sequence but rather what they represent.
Python将比较范围对象作为序列。这从本质上说,比较不是评价它们如何表示一个给定的序列,而是评价它们表示什么。
The fact that the start
, stop
and step
parameters are completely different plays no difference here because they all represent an empty list when expanded:
实际上,开始、停止和步骤参数完全不同,在这里没有区别,因为它们都表示扩展后的空列表:
For example, the first range
object:
例如,第一个范围对象:
list(range(0)) # []
and the second range
object:
第二范围对象:
list(range(2, 2, 2)) # []
Both represent an empty list and since two empty lists compare equal (True
) so will the range
objects that represent them.
它们都表示一个空列表,由于两个空列表比较相等(True),因此表示它们的range对象也一样。
As a result, you can have completely different looking range
objects; if they represent the same sequence they will compare equal:
因此,你可以有完全不同的外观范围对象;如果它们表示相同的序列,它们将比较相等:
range(1, 5, 100) == range(1, 30, 100)
Both represent a list with a single element [1]
so these two will also compare equal.
它们都表示一个包含单个元素的列表[1],因此这两个元素也会比较相同。
No, range
objects are really special:
Do note, though, that even though the comparison doesn't evaluate how they represent a sequence the result of comparing can be achieved using solely the values of start
, step
along with the len
of the range
objects; this has very interesting implications with the speed of comparisons:
但是,请注意,即使比较没有评估它们如何表示一个序列,仅使用start的值就可以实现比较的结果,与range对象的len一起执行;这对比较的速度有非常有趣的影响:
r0 = range(1, 1000000)
r1 = range(1, 1000000)
l0 = list(r0)
l1 = list(r1)
Ranges compares super fast:
范围比较超快:
%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
on the other hand, the lists..
另一方面,清单。
%timeit l0 == l1
10 loops, best of 3: 27.8 ms per loop
Yeah..
是的. .
As @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 (and not only these..) that range
objects have in Python 3.
正如@SuperBiasedMan指出的,这只适用于Python 3中的range对象。Python 2 range()是一个普通的ol'函数,它返回一个列表,而2返回一个列表。xrange对象不具有Python 3中范围对象所具有的比较能力(而且不仅仅是这些..)。
Look at @ajcr's answer 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.
看看@ajcr从Python 3系列对象的源代码直接引用的答案。这里记录了两个不同范围之间的比较实际需要的内容:简单的快速操作。在EQ和NE的range_richcompare函数中使用range_equals函数,并将其分配给PyRange_Type类型的tp_richcompare槽。
I believe the implementation of range_equals
is pretty readable (because it is nice as simple) to add here:
我相信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);
I've also scattered some of my own comments here; look at @ajcr's answer for the Python equivalent.
我也散布了一些我自己的评论;看看@ajcr对Python等价类的回答。
#2
13
Direct quote from the docs (emphasis mine):
直接引用文件(重点是我的):
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(0) = range(2,1,3)或range(0,3,2) = range(0,4,2))。
If you compare range
s with the "same" list, you'll get inequality, as stated in the docs as well:
如果您将范围与“相同”列表进行比较,您将得到不等,正如文档中所述:
Objects of different types, except different numeric types, never compare equal.
不同类型的对象,除了不同的数值类型外,从不相等地进行比较。
Example:
例子:
>>> type(range(1))
<class 'range'>
>>> type([0])
<class 'list'>
>>> [0] == range(1)
False
>>> [0] == list(range(1))
True
Note that this explicitly only applies to Python 3. In Python 2, where range
just returns a list, range(1) == [0]
evaluates as True
.
注意,这只显式地适用于Python 3。在Python 2中,range只返回一个列表,range(1) =[0]计算为True。
#3
10
To add a few additional details to the excellent answers on this page, two range
objects r0
and r1
are compared roughly as follows:
为了给这个页面上的优秀答案增加一些额外的细节,我们将两个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
The length of a range
object is easily to calculate using the start
, stop
and step
parameters. In the case where start == stop
, for example, Python can immediately know that the length is 0. In non-trivial cases, Python can just do a simple arithmetic calculation using the start
, stop
and step
values.
使用开始、停止和步骤参数很容易计算范围对象的长度。例如,在start == stop的情况下,Python可以立即知道长度为0。在非平凡的情况下,Python可以使用start、stop和step值进行简单的算术计算。
So in the case of range(0) == range(2, 2, 2)
, Python does the following:
因此,对于range(0) = range(2,2,2), Python执行以下操作:
- sees that
range(0)
andrange(2, 2, 2)
are different objects in memory. - 看到range(0)和range(2,2,2)是内存中的不同对象。
- computes the length of both objects; both lengths are 0 (because
start == stop
in both objects) so another test is needed. - 计算两个对象的长度;两个长度都为0(因为在两个对象中start = stop),因此需要进行另一个测试。
- sees that
len(range(0))
is 0. This means thatlen(range(2, 2, 2))
is also 0 (the previous test for inequality failed) and so the comparison should returnTrue
. - 看到len(range(0))是0。这意味着len(range(2,2,2))也是0(之前的不等式检验失败),所以比较应该返回True。
#4
6
res = range(0) == range(2, 2, 2)
res = range(0) = range(2,2,2)
Where:
地点:
range(0)
means the range from 0
to 0
- 0
steps (here step
equals to default value 1
), list without values.
表示从0到0 - 0的范围(这里的步骤等于默认值1),列表中没有值。
range(2, 2, 2)
means the range from 2
to 2
with step equals to 2
, list without values.
表示步骤等于2到2的范围,列表中没有值。
So, these ranges are really equal
这些范围是相等的
#5
5
range(0)
returns range(0,0)
. You start from 0 up to 0 with step 1, which is undefined since the third argument can't be 0 [by default]. You can't reach 0 with 1. No counter's action in place, therefore 0.
范围(0)返回范围(0,0)。从0到0开始,第1步没有定义,因为第三个参数不能为0[默认]。1不能达到0。没有计数器的作用,因此0。
range(2, 2, 2)
returns range(2, 2, 2)
. You start from 2 up to 2 but with step of 2. Which again, is basically 0 since you don't count up to anything.
范围(2,2,2)返回范围(2,2,2)。从2到2,但步骤2。这基本上是0,因为你不计算任何东西。
range(0) == range(2,2,2)
True and exactly the same.
完全一样。