想说一句题外话,很长一段时间我都没弄懂迭代跟遍历的关系
后来前辈跟我:遍历是目的,迭代是手段,我才恍然大悟,哈哈哈哈哈
1, 可迭代对象
所有Python 程序员都知道,序列可以迭代, 下面说明具体原因:
(1)检查对象是否实现了__iter__方法,如果实现了就调用它,获取一个迭代器(什么是迭代器,下面会讲)。
(2)如果没有实现__iter__方法,但实现了__getitem__方法,Python会创建一个迭代器,尝试按顺序
(从索引0开始)获取元素。
(3)如果尝试失败,Python跑出TpyeError异常,通常会提示“C object is not iterable”(C 对象不可迭代),
其中C 是目标对象所属的类。
任何Python序列都可迭代的原因是,它们都实现了__getitem__方法。其实,标准的序列也都实现了__iter__方法。
在从Python3.4开始,检查对象x能否迭代,最准确的方法是:调用iter(x)函数,如果不可迭代,在处理TypeError异常。
这是比使用isinstance(x, abc.Iteradble)更准确, 因为iter(x) 函数会考虑到遗留的__getitem__方法,
而abc.Iterable类则不考虑。
可迭代的对象:
使用iter 内置函数可以获取迭代器的对象。如果对象实现了能返 回迭代器的__iter__ 方法,那么对象就是可迭代的。
序列都可以迭 代;实现了__getitem__ 方法,而且其参数是从零开始的索引,这种 对象也可以迭代。
2,迭代器
创建一个迭代器
我们要明确可迭代的对象和迭代器之间的关系:Python从可迭代的对象中获取迭代器。
下面是一个简单的 for 循环,迭代一个字符串。这里,字符串 'ABC' 是可迭代的对象。背后是有迭代器的,只不过我们看不到:
如果没有for语句,就必须要用while 循环模拟,要写成想下面这样:
(1)使用可迭代的对象构建迭代器it。
(2)不断在迭代器上调用next函数,或许下一个字符。
(3)如果没有字符了,迭代器会跑出StopIteration异常。
(4)释放对it的引用,即废弃迭代器对象。
(5)退出循环。
标准的迭代器接口有两个方法。
__next__
返回下一个可用的元素,如果没有元素了,跑出StopIteration异常。
__iter__
返回self, 以便在应该使用可迭代对象的地方使用迭代器,例如for循环中。
迭代器定义:
迭代器是这样的对象:实现了无参数的__next__ 方法,返回序列 中的下一个元素,
如果没有元素了,那么抛出StopIteration 异常。
Python 中的迭代器还实现了__iter__ 方法,因此迭代器也可以迭代。
因为内置的 iter(...) 函数会对序列做特殊处理,所以上面的Sentence类也可以迭代。
构建可迭代的对象和迭代器时经常会出现错误,原因是混淆了二者。要 知道,可迭代的对象有个__iter__ 方法,
每次都实例化一个新的迭代 器;而迭代器要实现__next__ 方法,返回单个元素,此外还要实现__iter__ 方法,返回迭代器本身。
因此,迭代器可以迭代,但是可迭代的对象不是迭代器。
可迭代的对象一定不能是自身的迭代器。也就是说,可迭代的对象 必须实现__iter__ 方法,但不能实现__next__ 方法。
另一方面,迭代器应该一直可以迭代。迭代器的__iter__ 方法应 该返回自身。
3,生成器
只要 Python 函数的定义体中有 yield 关键字,该函数就是生成器函 数。
调用生成器函数时,会返回一个生成器对象。也就是说,生成器函 数是生成器工厂。
用生成器实现最上面的Sentence类
再用惰性实现
4, 生成器表达式
生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表, 而是返回一个生成器,按需惰性生成元素。
也就是说,如果列表推导是 制造列表的工厂,那么生成器表达式就是制造生成器的工厂。
简而言之,相对于列表推导式,用()的就是生成器推导式
现在用生成器表达式实现Sentence类
itertools 中一些有用的生成器函数: