Python | 深入理解循环和迭代( 二 )


numbers = [1, 2, 3, 5, 7]i = 0while i < len(numbers):print(numbers[i])i += 1
很显然,上面的循环方式只适合于序列类对象,对其它的并非完全使用,比如字典、集合 。
比如使用索引手动遍历一个集合,我们将看到报错:
>>> fruits = {'lemon', 'apple', 'orange', 'watermelon'}>>> i = 0>>> while i < len(fruits):... print(fruits[i])... i += 1... Traceback (most recent call last): File "", line 2, inTypeError: 'set' object does not support indexing
集合不是序列,因此它们不支持索引 。
在Python中,我们不能通过使用索引手动遍历每个可迭代对象 。这对于不是序列的可迭代对象根本不起作用 。
迭代器
在Python中,迭代器可以用于for循环 。
什么是迭代器?它是驱动可迭代对象的一类对象 。我们可以从任意可迭代对象那里生成迭代器 。
这里有三个可迭代对象:集合、元组和字符串 。
>>> numbers = {1, 2, 3, 5, 7}>>> coordinates = (4, 5, 7)>>> words = "hello there"
可以用Python内置的iter函数用上面的可迭代对象生成迭代器 。
>>> iter(numbers)iterator object at 0x7f2b9271c860>>>> iter(coordinates)>>> iter(words)
有了迭代器,就把它传给内置函数next,从而获得它的下一项 。
>>> numbers = [1, 2, 3]>>> my_iterator = iter(numbers)>>> next(my_iterator)1>>> next(my_iterator)2
每从迭代器中取出一项,那一项就从迭代器中“消失”了 。如果到了迭代器的最后一项,还执行next,而实际上后面已经没有其他项了,这时候就会报出StopIteration异常 。
>>> next(iterator)3>>> next(iterator)Traceback (most recent call last): File "", line 1,in StopIteration不用for的循环
在了解了迭代器、以及iter和next函数后,我们将尝试手动遍历一个可迭代对象,而不使用for循环 。
不用for,就得用while了,Python中只有这么两个循环语句 。
def funky_for_loop(iterable, action_to_do):for item in iterable:action_to_do(item)
为了去掉for,需要:
 

  1. 根据给定的可迭代对象生成迭代器
  2. 从迭代器中重复获取下一项
  3. 如果成功获得了下一项,则相当于执行for循环了
  4. 如果在获取下一项时遇到“StopIteration”异常,则停止循环
def funky_for_loop(iterable, action_to_do):iterator = iter(iterable)done_looping = Falsewhile not done_looping:try:item = next(iterator)except StopIteration:done_looping = Trueelse:action_to_do(item) 
这里,其实是用while循环和迭代器重新发明了for循环 。
上面的代码基本上定义了Python中循环的工作方式 。如果你了解内置的iter和next函数在遍历对象时的工作方式,那么你就了解了Python的for循环是如何工作的,它们的工作过程是类似的 。
实际上,通过上面的代码,不仅仅展示了for循环的工作原理,所有可迭代对象的循环都如此 。
总结一下,迭代器协议是描述“Python中可迭代对象的循环如何工作的”的一种基本方式,它本质上是Python中iter和next函数所定义的,Python中所有形式的迭代都由迭代器协议提供支持 。
迭代器协议也被用于for:
for n in numbers:print(n)
多重赋值也使用迭代器协议:
x, y, z = coordinates
下面这种使用*的表达式也使用迭代器协议:
a, b, *rest = numbers print(*numbers)
许多内置函数依赖于迭代器协议:
unique_numbers = set(numbers)
Python中任何与可迭代对象一起工作的东西都可能以某种方式使用迭代器协议 。在Python中,每当你遍历一个可迭代对象时,都依赖于迭代器协议 。
生成器是迭代器
迭代器看起来很酷,不过,它是不是用途有限呢?或者说作为普通的Python编程者,是不是不需要关心它呢?
非也 。
迭代器很常见 。
>>> numbers = [1, 2, 3]>>> squares = (n**2 for n in numbers)
此处得到的squares是一个生成器,生成器也是迭代器,这意味着你可以对生成器调用next,以获取其下一项:
>>> next(squares)1>>> next(squares)4
用for循环同样可以遍历生成器:
>>> squares = (n**2 for n in numbers)>>> for n in squares:... print(n)... 1 4 9
下面这句话,貌似废话,但是重要:迭代器是可迭代对象 。


推荐阅读