为什么范围(0)=范围(2,2,2)在Python3中是真的?
为什么在Python3中,用不同值初始化的范围彼此比较相等 当我在解释器中执行以下命令时:为什么范围(0)=范围(2,2,2)在Python3中是真的?,python,python-3.x,range,identity,python-internals,Python,Python 3.x,Range,Identity,Python Internals,为什么在Python3中,用不同值初始化的范围彼此比较相等 当我在解释器中执行以下命令时: >>> r1 = range(0) >>> r2 = range(2, 2, 2) >>> r1 == r2 True 结果是True。为什么会这样?为什么具有不同参数值的两个不同的范围对象被视为相等?范围对象是特殊的: Python将对象比较为。这本质上意味着,比较不会评估它们如何表示给定序列,而是评估它们表示的内容 开始、停止和步骤参数完全不同这
>>> r1 = range(0)
>>> r2 = range(2, 2, 2)
>>> r1 == r2
True
结果是True
。为什么会这样?为什么具有不同参数值的两个不同的范围
对象被视为相等?范围对象是特殊的:
Python将对象比较为。这本质上意味着,比较不会评估它们如何表示给定序列,而是评估它们表示的内容
开始
、停止
和步骤
参数完全不同这一事实在这里没有什么区别,因为它们在展开时都表示一个空列表:
例如,第一个范围对象:
list(range(0)) # []
list(range(2, 2, 2)) # []
第二个范围对象:
list(range(0)) # []
list(range(2, 2, 2)) # []
两者都表示一个空列表,由于两个空列表比较相等(True
),因此表示它们的范围对象也将相等
因此,您可以拥有外观完全不同的范围
对象;如果它们代表相同的序列,它们将进行相等的比较:
两者都表示具有单个元素的列表[1]
,因此这两个元素的比较也将相等
不,范围
对象非常特殊:
但是请注意,即使比较没有评估它们如何表示序列,也可以使用单独使用开始
、步骤
的值以及范围
对象的长度
来获得比较结果;这对比较的速度有着非常有趣的影响:
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
嗯
如所述,这仅适用于Python 3中的范围对象。Python2range()
是一个普通的ol'函数,它返回一个列表,而2.x
对象没有Python3中range
对象所具有的比较功能()
查看直接从Python 3范围
对象的源代码中获取引号。其中记录了两个不同范围之间的比较实际需要的内容:简单快速的操作。该功能用于forEQ
和NE
情况,并分配给
我相信range_equals
的实现非常可读(因为它非常简单),可以添加到这里:
/*r0和r1是指向RangeObject的指针*/
/*检查指针是否指向同一对象,例如:
>>>r1=r2=范围(0,10)
>>>r1==r2
显然,返回True*/
如果(r0==r1)
返回1;
/*比较范围的长度(如果相等)
检查仍在继续。如果不是,则返回False*/
cmp_结果=PyObject_RichCompareBool(r0->length,r1->length,Py_EQ);
/*向调用者返回False或error
>>>范围(0,10)=范围(0,10,2)
这里失败了*/
如果(cmp_结果!=1)
返回cmp_结果;
/*查看范围是否有长度(非空)。如果长度为0
然后由于前面的检查,另一个范围的长度为
等于0。他们是平等的*/
cmp_结果=PyObject_Not(r0->长度);
/*向调用者返回True或error。
>>>范围(0)=范围(2,2,2)#真
(对)在这里被抓住。长度都是零*/
如果(cmp_结果!=0)
返回cmp_结果;
/*比较范围的起始值(如果不匹配)
那么我们不是在处理相等的范围*/
cmp\u结果=PyObject\u RichCompareBool(r0->start,r1->start,Py\u EQ);
/*向调用者返回False或error。
如果镜头相等,则检查其起始值
>>>范围(0,10)=范围(10,20)#错误
长度相等且非零,步长不匹配*/
如果(cmp_结果!=1)
返回cmp_结果;
/*检查长度是否等于1。
如果起点相同且长度为1,则表示相同的顺序:
>>>范围(0,10,10)=范围(0,20,20)#真*/
一个=塔龙(1);
如果(!一)
返回-1;
cmp\u结果=PyObject\u richcomarebool(r0->length,one,Py\u EQ);
Py_DECREF(一个);
/*向调用者返回True或error*/
如果(cmp_结果!=0)
返回cmp_结果;
/*最后,比较一下他们的步骤*/
返回PyObject\u richcomarebool(r0->step,r1->step,Py\u EQ);
我也在这里散布了我自己的一些评论;查看Python的等价物。res=range(0)==range(2,2,2)
其中:
range(0)
表示从0
到0
-0
步骤的范围(此处step
等于默认值1
),列表中没有值
range(2, 2, 2)
表示从2
到2
的范围,步长等于2
,列表中没有值
range(2, 2, 2)
因此,这些范围实际上是相等的range(0)
返回range(0,0)
。步骤1从0开始到0,这是未定义的,因为第三个参数[默认情况下]不能为0。使用1无法到达0。没有适当的计数器操作,因此为0
range(2,2,2)
返回range(2,2,2)
。从2开始到2,但步骤为2。这也是,基本上是0,因为你什么都不算
range(0) == range(2,2,2)
正确和完全相同。直接引用(强调我的):
测试范围对象是否与==和!=相等将它们比较为
序列。也就是说,如果两个范围对象
表示相同的值序列。(请注意,两个范围对象
比较相等可能有不同的开始、停止和步骤
属性,例如范围(0)=范围(2,1,3)或范围(0,3,2)
==范围(0、4、2)
如果将range
s与“same”列表进行比较,就会得到不等式,如