



Python内置了名为iter()的函数。当向iter()函数传入可迭代对象时,iter()会返回迭代器对象:
迭代器是个对象,可以逐个生成序列中的值:
不需要直接调用iter()。当使用for循环,如for num in numbers时,Python会自动在底层调用iter()。返回的对象会作为for循环的迭代器:
集合的迭代器是独立对象,拥有唯一id,可借助id()函数进行确认:
iter()有多种获取迭代器的方法,其中之一是使用魔术方法__iter__()。任意类都能定义魔术方法,且不需要传入参数。每次调用__iter__()时,就会生成一个新的迭代器对象。例如,列表就内置了__iter__()方法:
Python对迭代器对象和可迭代对象进行了区分。判定一个对象是否可迭代,唯一的条件是能否将其传递给iter()函数,然后得到可以直接使用的迭代器。如果该对象具有__iter__()方法,iter()就会调用__iter__()方法获取迭代器。Python中的列表和元组都是可迭代的,字符串也是可迭代的,所以才能用for char in my_str:语句迭代字符串my_str中的字符。一般而言,可用于for循环的任何容器类对象都是可迭代的。
for循环是最常见的迭代序列的方法。不过,代码有时需要更细致地迭代序列。这时,可以借助内置函数next()。使用next()时需要传入参数,即迭代器。每次调用next(my_iterator)时,即可获取并返回下一个元素:
如果再次调用next(names_it),则会触发特定的内置异常,即StopIteration:
迭代器抛出特定异常StopIteration,表明序列已结束。通常情况下,不需要手动抛出或捕获StopIteration异常,后文会介绍StopIteration的用法。对于理解for循环的运行机制,可以认为for循环每次运行时,都会在内部调用next()方法。抛出StopIteration异常后,退出循环。
在调用next()时,可以额外添加一个参数,用于指定默认值。如此一来,当迭代到序列末尾时,next()函数就不会抛出StopIteration异常,而是返回预设的默认值:
再考虑另一种情况。倘若处理的不是简单的数值序列,而是需要通过计算、读取或其他方式获取序列元素,又该如何操作呢?
看一个简单的例子。假设需要编写一个创建平方数列表的函数:
该函数可以运行,但其中潜藏着问题,你能发现吗?
问题之一,假如MAX的值不是5,而是10 000 000或是10 000 000 000,甚至是更大的值,那么代码会占用大量内存。创建如此庞大的列表后,仅使用一次就丢弃了,这样的内存占用在一定程度上是毫无意义且可怕的。此外,必须等待所有平方值都计算完毕,for循环才能开始运行。若有用户使用该程序,或许会怀疑程序卡住了。
更严重的问题是,若不采用运算快速且资源消耗低的算术方法获取每个元素,而是执行非常耗费资源的计算,或是通过网络调用API,又或是从数据库读取数据,那么程序就会运行得特别慢,毫无响应,甚至因内存不足而出现程序崩溃的情况。此时用户一定会质疑程序员的编程水平。
最好的解决方案是先创建迭代器,仅在需要值时,以惰性计算的方式进行计算。这样就能保证循环的每一步执行都是即时的。
那么,该如何实现呢?有多种实现方法,最佳方法是使用生成器函数。你肯定会爱不释手!