range()是什么?为何不生产迭代器?【MySQL教程】,mysql,django,linux,flask,python
迭代器是 23 种设想形式中最常常运用的一种(之一),在 Python 中随处可见它的身影,我们经常常运用到它,然则却不肯定意想到它的存在。在关于迭代器的系列文章中(链接见文末),我最少提到了 23 种生成迭代器的要领。有些要领是特地用于生成迭代器的,另有一些要领则是为了处理别的题目而“暗中”运用到迭代器。
在体系进修迭代器之前,我一向以为 range() 要领也是用于生成迭代器的,如今却倏忽发明,它生成的只是可迭代对象,而并非迭代器! (PS:Python2 中 range() 生成的是列表,本文基于Python3,生成的是可迭代对象)
因而,我有了如许的疑问:为什么 range() 不生成迭代器呢?在查找答案的历程当中,我发明自身对 range 范例的熟悉存在一些误区。因而,本文将和人人周全地熟悉一下 range ,期待与你配合进修提高。
1、range() 是什么?
它的语法:range(start, stop [,step]) ;start 指的是计数起始值,默许是 0;stop 指的是计数完毕值,但不包含 stop ;step 是步长,默许为 1,不能够为 0 。range() 要领生成一段左闭右开的整数局限。
>>> a = range(5) # 即 range(0,5) >>> a range(0, 5) >>> len(a) 5 >>> for x in a: >>> print(x,end=" ") 0 1 2 3 4
关于 range() 函数,有几个注重点:(1)它示意的是左闭右开区间;(2)它吸收的参数必需是整数,能够是负数,但不能是浮点数等别的范例;(3)它是不可变的序列范例,能够举行推断元素、查找元素、切片等操纵,但不能修正元素;(4)它是可迭代对象,却不是迭代器。
# (1)左闭右开 >>> for i in range(3, 6): >>> print(i,end=" ") 3 4 5 # (2)参数范例 >>> for i in range(-8, -2, 2): >>> print(i,end=" ") -8 -6 -4 >>> range(2.2) ---------------------------- TypeError Traceback (most recent call last) ... TypeError: 'float' object cannot be interpreted as an integer # (3)序列操纵 >>> b = range(1,10) >>> b[0] 1 >>> b[:-3] range(1, 7) >>> b[0] = 2 TypeError Traceback (most recent call last) ... TypeError: 'range' object does not support item assignment # (4)不是迭代器 >>> hasattr(range(3),'__iter__') True >>> hasattr(range(3),'__next__') False >>> hasattr(iter(range(3)),'__next__') True
2、 为什么range()不生产迭代器?
能够获得迭代器的内置要领许多,比方 zip() 、enumerate()、map()、filter() 和 reversed() 等等,然则像 range() 如许仅仅获得的是可迭代对象的要领就独一无二了(如有反例,迎接示知)。这就是我存在学问误区的处所。
在 for-轮回 遍用时,可迭代对象与迭代器的机能是一样的,即它们都是惰性求值的,在空间庞杂度与时刻庞杂度上并没有差异。我曾归纳综合过二者的差异是“一同两差异”:雷同的是都可惰性迭代,差异的是可迭代对象不支撑自遍历(即next()要领),而迭代器自身不支撑切片(即__getitem__()
要领)。
虽然有这些差异,但很难得出结论说它们哪一个更优。如今玄妙的地方就在于,为什么给 5 种内置要领都设想了迭代器,偏偏给 range() 要领设想的就是可迭代对象呢?把它们都统一起来,不是更好么?
事实上,Pyhton 为了范例性就干过不少这类事,比方,Python2 中有 range() 和 xrange() 两种要领,而 Python3 就干掉了个中一种,还用了“李代桃僵”法。为什么不更范例点,令 range() 生成的是迭代器呢?
关于这个题目,我没找到官方诠释,以下纯属个人观点 。
zip() 等要领都须要吸收肯定的可迭代对象的参数,是对它们的一种再加工的历程,因而也愿望立时产出肯定的效果来,所以 Python 开发者就设想了这个效果是迭代器。如许另有一个优点,即当作为参数的可迭代对象发生变化的时刻,作为效果的迭代器因为是斲丧型的,不会被毛病地运用。
而 range() 要领就差异了,它吸收的参数不是可迭代对象,自身是一种首次加工的历程,所以设想它为可迭代对象,既能够直接运用,也能够用于别的再加工用处。比方,zip() 等要领就完全能够吸收 range 范例的参数。
>>> for i in zip(range(1,6,2), range(2,7,2)): >>> print(i, end="") (1, 2)(3, 4)(5, 6)
也就是说,range() 要领作为一种低级生产者,它生产的质料自身就有很大用处,早早把它变成迭代器的话,无疑是一种弄巧成拙的行动。
关于这类解读,你是不是以为有原理呢?迎接就这个话题与我讨论。
3、range 范例是什么?
以上是我对“为什么range()不发生迭代器”的一种解答。顺着这个思绪,我研讨了一下它发生的 range 对象,一研讨就发明,这个 range 对象也并不简朴。
起首新鲜的一点就是,它竟然是不可变序列!我从未注重过这一点。虽然说,我从未想过修正 range() 的值,但这一不可修正的特征照样令我惊奇。
翻看文档,官方是如许明白分别的——有三种基本的序列范例:列表、元组和局限(range)对象。(There are three basic sequence types: lists, tuples, and range objects.)
这我倒一向没注重,本来 range 范例竟然跟列表和元组是一样职位的基本序列!我一向记挂着字符串是不可变的序列范例,不曾想,这里另有一名不可变的序列范例呢。
那 range 序列跟别的序列范例有什么差异呢?
一般序列都支撑的操纵有 12 种,在《你真的晓得Python的字符串是什么吗?》这篇文章里提到过。range 序列只支撑个中的 10 种,不支撑举行加法拼接与乘法反复。
>>> range(2) + range(3) ----------------------------------------- TypeError Traceback (most recent call last) ... TypeError: unsupported operand type(s) for +: 'range' and 'range' >>> range(2)*2 ----------------------------------------- TypeError Traceback (most recent call last) ... TypeError: unsupported operand type(s) for *: 'range' and 'int'
那末题目来了:同样是不可变序列,为什么字符串和元组就支撑上述两种操纵,而偏偏 range 序列不支撑呢?虽然不能直接修正不可变序列,但我们能够将它们拷贝到新的序列上举行操纵啊,为什么 range 对象连这都不支撑呢?
且看官方文档的诠释:
...due to the fact that range objects can only represent sequences that follow a strict pattern and repetition and concatenation will usually violate that pattern.缘由是 range 对象仅仅示意一个遵照着严厉形式的序列,而反复与拼接通常会损坏这类形式...
题目的症结就在于 range 序列的 pattern,细致想一想,实在它示意的就是一个等差数列啊(喵,高中数学学问没忘...),拼接两个等差数列,也许反复拼接一个等差数列,想一想确切不妥,这就是为啥 range 范例不支撑这两个操纵的缘由了。由此推论,别的修正行动也会损坏等差数列构造,所以一切不给修正就是了。
4、小结
回忆全文,我获得了两个偏冷门的结论:range 是可迭代对象而不是迭代器;range 对象是不可变的等差序列。
若纯真看结论的话,你也许没有感想,也许还会说这没啥了不起啊。但假如我诘问,为什么 range 不是迭代器呢,为什么 range 是不可变序列呢?对这俩题目,你是不是还能答出个自作掩饰的设想头脑呢?(PS:我决议了,如有时机口试他人,我必要问这两个题目的嘿~)
因为 range 对象这纤细而有意义的特征,我以为这篇文章写得值了。本文是作为迭代器系列文章的一篇来写的,所以关于迭代器的基本学问引见不多,迎接检察之前的文章。别的,另有一种特别的迭代器也值得零丁成文,那就是生成器了,敬请期待后续推文哦~
以上就是range()是什么?为什么不生产迭代器?的细致内容,更多请关注ki4网别的相干文章!