为什么在Python3中,用不同值初始化的范围彼此比较相等?
当我在解释器中执行以下命令时:
>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True
结果是True
.为什么会这样?为什么具有不同参数值的两个不同对象被视为相等?
为什么在Python3中,用不同值初始化的范围彼此比较相等?
当我在解释器中执行以下命令时:
>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True
结果是True
.为什么会这样?为什么具有不同参数值的两个不同对象被视为相等?
range
objects are special:Python将100个对象与101个对象进行比较.这本质上意味着
start
、stop
和step
参数完全不同这一事实在这里没有区别,因为they all represent an empty list when expanded:
例如,前range
个对象:
list(range(0)) # []
第二个range
个物体:
list(range(2, 2, 2)) # []
Both represent an empty list由于两个空列表的比较相等(True
),所以range
个对象将对它们进行比较.
因此,你可以拥有完全不同的lookingrange
个对象;如果它们代表相同的序列,它们将相等:
range(1, 5, 100) == range(1, 30, 100)
两者都表示一个列表,其中只有一个元素[1]
,因此这两个元素的比较也将相等.
range
objects are really special:但是,请注意,即使比较没有判断how,它们也代表了一个序列,即使用solely比较can be achieved、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
另一方面, list ..
%timeit l0 == l1
10 loops, best of 3: 27.8 ms per loop
是 啊
如前所述,这只适用于Python 3中的range对象.Python 2 range()
是一个普通的ol'函数,它返回一个列表,而2.x
xrange
对象不具备Python 3中range
个对象所具有的比较能力(102).
请看100,直接从Python 3 range
对象的源代码中获取引号.其中记录了两个不同范围之间的比较实际需要的内容:简单快速的操作.range_equals
功能在102中用于EQ
和NE
种情况,并分配给103.
我相信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);
我也在这里散布了自己的一些 comments ;请看100中的Python等效项.