python教程—为什么range(0) == range(2,2,2)在python3中为真?-Python实用宝典

python教程—为什么range(0) == range(2,2,2)在python3中为真?

为什么用不同的值初始化的范围在python3中彼此相等?

为什么用不同的值初始化的范围在python3中彼此相等?

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

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

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

回答

range对象很特殊:

Python将把range对象作为 sequence 。本质上的意思是,这个比较并不计算,它们如何表示一个给定的序列,而是,它们表示什么

start、stop和step参数完全不同的事实在这里没有什么区别,因为在展开时,它们都表示一个空列表:

例如,第一个range对象:

    list(range(0)) # []

第二个range对象:

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

都表示一个空列表,由于两个空列表比较起来是相等的(True),所以表示的range对象也表示它们

因此,您可以有完全不同的外观范围对象;如果它们表示相同的序列,则 =:

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

查看@ajcr的答案,直接从Python 3 range对象的源代码中引用。它记录了两个不同范围之间的比较实际上需要什么:简单的快速操作。< a href = " https://github.com/python/cpython/blob/2f37d372927a4c2c843e2813c32354979c682919/Objects/rangeobject.c L437”rel = " noreferrer " > range_equals < / >函数是利用在< em > < a href = " https://github.com/python/cpython/blob/2f37d372927a4c2c843e2813c32354979c682919/Objects/rangeobject.c # L468”rel = " noreferrer " > < / > < / em > range_richcompare函数对情商和NE病例和分配到< em > 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);

我也在这里散布了一些自己的评论;查看@ajcr的答案对应的Python。

​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

本文由 Python实用宝典 作者:Python实用宝典 发表,其版权均为 Python实用宝典 所有,文章内容系作者个人观点,不代表 Python实用宝典 对观点赞同或支持。如需转载,请注明文章来源。
0

发表评论