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

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

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

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

推荐答案

The range objects are special:

Python将100个对象与101个对象进行比较.这本质上意味着

startstopstep参数完全不同这一事实在这里没有区别,因为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],因此这两个元素的比较也将相等.


No, range objects are really special:

但是,请注意,即使比较没有判断how,它们也代表了一个序列,即使用solely比较can be achievedstartstep以及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中用于EQNE种情况,并分配给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等效项.

Python-3.x相关问答推荐

海象表达可以放在方括号中而不是括号中吗?

将Trio与基于线程的事件侦听器混合使用

是否可以使用参数对Flask重定向?

切片时是否在NumPy ND数组中创建新对象?

Pandas 根据条件增加Dataframe列

根据第一个字典的值序列对第二个字典进行排序

如何计算累积几何平均数?

合并两个数据帧并对某些总和进行求和

如何获取自定义文件上传路径的对象ID?

python 分代垃圾收集:get_count 没有报告正确的对象创建数?

pytorch 中 mps 设备的 manual_seed

为什么 mypy 不适用于 sqlalchemy?

获取字符串中的两个工作日之间的差异

使用大型多个数据集,其中每个数据集包含多个值 - Pytorch

Dask worker post-processing

如何调试垂死的 Jupyter Python3 内核?

如何使用 python 库连接到 poloniex.com websocket api

如何区分文件之类的对象和文件路径之类的对象

在 Alembic 迁移期间更新列内容

如何使用 Celery 和 Django 将任务路由到不同的队列