Python with 工作原理、装饰器、回收机制、内存管理机制、拷贝、作用域等(15)


12、迭代器与迭代对象
推荐博客:为什么range不是迭代器?range到底是什么类型?
迭代器是 23 种设计模式中最常用的一种(之一) , 在中随处可见它的身影 , 我们经常用到它 , 但是却不一定意识到它的存在 。有些方法是专门用于生成迭代器的 , 还有一些方法则是为了解决别的问题而“暗中”使用到迭代器 。
在系统学习迭代器之前 , 我一直以为 range() 方法也是用于生成迭代器的 , 现在却突然发现 , 它生成的只是可迭代对象 , 而并不是迭代器! (PS: 中 range() 生成的是列表 , 本文基于 , 生成的是可迭代对象)
range() 是什么?
它的语法:range(start, stop, [step]) ;start 指的是计数起始值 , 默认是 0;stop 指的是计数结束值 , 但不包括 stop ;step 是步长 , 默认为 1 , 不可以为 0。range() 方法生成一段左闭右开的整数范围 。
对于 range() 函数 , 有几个注意点:
(1)它表示的范围是左闭右开的区间;
(2)它接收的参数必须是整数 , 可以是负数 , 但不能是浮点数等其它类型;
(3)它是不可变的序列类型 , 可以进行判断元素、查找元素、切片等操作 , 但不能修改元素;
(4)它是可迭代对象 , 却不是迭代器 。
为什么range()不生产迭代器?
可以获得迭代器的内置方法很多 , 例如 zip() 、()、map()、() 和 () 等等 , 但是像 range() 这样仅仅得到的是可迭代对象的方法就绝无仅有了 。
在 for-循环 遍历时 , 可迭代对象与迭代器的性能是一样的 , 即它们都是惰性求值的 , 在空间复杂度与时间复杂度上并无差异 。我曾概括过两者的差别是“一同两不同”:相同的是都可惰性迭代 , 不同的是可迭代对象不支持自遍历(即next()方法) , 而迭代器本身不支持切片(即() 方法) 。
虽然有这些差别 , 但很难得出结论说它们哪个更优 。现在微妙之处就在于 , 为什么给 5 种内置方法都设计了迭代器 , 偏偏给 range() 方法设计的就是可迭代对象呢?把它们都统一起来 , 不是更好么?
zip() 、()、map()、() 和 () 等方法都需要接收确定的可迭代对象的参数 , 是对它们的一种再加工的过程 , 因此也希望马上产出确定的结果来 , 所以开发者就设计了这个结果是迭代器 。这样还有一个好处 , 即当作为参数的可迭代对象发生变化的时候 , 作为结果的迭代器因为是消耗型的 , 不会被错误地使用 。
而 range() 方法就不同了 , 它接收的参数不是可迭代对象 , 本身是一种初次加工的过程 , 所以设计它为可迭代对象 , 既可以直接使用 , 也可以用于其它再加工用途 。例如 , zip() 等方法就完全可以接收 range 类型的参数 。
range 类型是什么?
有三种基本的序列类型:列表、元组和范围(range)对象 。(There are three basictypes: lists, , and range .)
这我倒一直没注意 , 原来 range 类型居然跟列表和元组是一样地位的基础序列!我一直记挂着字符串和元组是不可变的序列类型 , 不曾想 , 这里还有一位不可变的序列类型呢!
>>> range(2) + range(3)-----------------------------------------TypeErrorTraceback (most recent call last)...TypeError: unsupported operand type(s) for +: 'range' and 'range'>>> range(2)*2-----------------------------------------TypeErrorTraceback (most recent call last)...TypeError: unsupported operand type(s) for *: 'range' and 'int'复制代码