什么是迭代器?迭代器是可以像我们在 for 循环中那样迭代的对象。我们也可以说迭代器是一个对象,它一次返回一个元素。也就是说,在我们明确要求他们的下一个项目之前,他们不会做任何工作。它们的工作原理在计算机科学中称为惰性求值。惰性求值是一种求值策略,它将表达式的求值延迟到真正需要它的值。由于 Python 迭代器的惰性,它们是处理无穷大的好方法,即可以永远迭代的可迭代对象。您几乎找不到不与迭代器配合使用的 Python 程序。
迭代器是 Python 的一个基本概念。您已经在第一个 Python 程序中了解到可以迭代容器对象,例如列表和字符串。为此,Python 创建了列表或字符串的迭代器版本。在这种情况下,迭代器可以被看作是一个指向容器的指针,它使我们能够迭代这个容器的所有元素。迭代器是一种抽象,它使程序员能够访问可迭代对象(集合、字符串、列表等)的所有元素,而无需深入了解该对象的数据结构。
生成器是一种特殊的函数,它使我们能够实现或生成迭代器。
大多数情况下,迭代器是隐式使用的,就像在 Python 的 for 循环中一样。我们在以下示例中演示了这一点。我们正在迭代一个列表,但您不要误会:列表不是迭代器,但它可以像迭代器一样使用:
城市 = [ “巴黎” , “柏林” , “汉堡” ,
“法兰克福” , “伦敦” , “维也纳” ,
“阿姆斯特丹” , “海牙” ]
的 位置 在 城市:
打印(“位置:” + 位置)
输出:
地点:巴黎 地点:柏林 地点:汉堡 地点:法兰克福 地点:伦敦 地点:维也纳 地点:阿姆斯特丹 地点:海牙
执行 for 循环时到底发生了什么?函数'iter' 应用于'in' 关键字之后的对象,例如for i in o:。可能有两种情况: o 是可迭代的或不可迭代的。如果 o 不可迭代,则会引发异常,表示对象的类型不可迭代。另一方面,如果 o 是可迭代的,则调用 iter(o) 将返回一个迭代器,让我们称其为 iterator_obj for 循环使用此迭代器通过使用 next 方法迭代对象 o。当 next(iterator_obj) 耗尽时 for 循环停止,这意味着它返回一个 StopIteration 异常。我们在以下代码示例中演示了这种行为:
专业知识 = [ “Python 初学者” ,
“Python 中级” ,
“Python 精通” ,
“Python 高级” ]
专业知识 迭代器= iter (专业知识)
打印(“第一次调用“下一步”:“ , 下一步(专业知识迭代器))
打印(“第二次调用‘next’:” , next ( experts_iterator ))
输出:
第一次调用“next”:Python 初学者 第二次调用“next”:Python 中级
我们本可以再调用next
两次,但在这之后我们会得到一个 StopIteration 异常。
我们可以在 while 循环中模拟 for 循环的这种迭代行为:您可能已经注意到我们的程序中缺少一些东西:我们必须捕获“停止迭代”异常:
other_cities = [ "Strasbourg" , "Freiburg" , "Stuttgart" ,
"Vienna / Wien" , "Hannover" , "Berlin" ,
"Zurich" ]
city_iterator = iter ( other_cities )
而 city_iterator :
try :
city = next ( city_iterator )
打印(城市)
除了 StopIteration :
break
输出:
斯特拉斯堡 弗莱堡 斯图加特 维也纳/维也纳 汉诺威 柏林 苏黎世
Python 标准库的顺序基类型以及大多数类都支持迭代。字典数据类型 dict 也支持迭代器。在这种情况下,迭代会遍历字典的键:
资本 = {
“法国” :“巴黎” ,
“荷兰” :“阿姆斯特丹” ,
“德” :“柏林” ,
“瑞士” :“伯尔尼” ,
“奥地利” :“维也纳” }
为 国家 的 首都:
印刷(““ + 国家 + ”的 首都是“ + 首都[国家])
输出:
法国的首都是巴黎 荷兰的首都是阿姆斯特丹 德国的首都是柏林 瑞士的首都是伯尔尼 奥地利的首都是维也纳
题外话:从我们的例子中了解到,荷兰的首都不是海牙(海牙)而是阿姆斯特丹,有些读者可能会感到困惑。根据宪法,阿姆斯特丹是荷兰的首都,尽管荷兰议会和荷兰政府以及最高法院和国务委员会都位于海牙。
在 Python 中创建迭代器的一种方法是定义一个实现方法__init__
和__next__
. 我们通过实现一个类循环来展示这一点,该循环可用于永远循环遍历一个可迭代对象。换句话说,这个类的一个实例返回一个可迭代的元素,直到它用完为止。然后它无限期地重复该序列。
class Cycle ( object ):
def __init__ ( self , iterable ):
self 。可迭代 = 可迭代
self 。iter_obj = iter ( iterable )
def __iter__ ( self ):
return self
def __next__ ( self ):
while True :
try :
next_obj = next ( self . iter_obj )
return next_obj
除了 StopIteration :
self 。iter_obj = ITER (自我。可迭代)
X = 周期(“ABC” )
为 我 在 范围(10 ):
打印(下一个(X ), 结束= “” )
输出:
a、b、c、a、b、c、a、b、c、a、
尽管创建迭代器的面向对象方法可能非常有趣,但这不是 Pythonic 方法。
在 Python 中创建迭代器的常用且最简单的方法是使用生成器函数。您将在下一章中了解这一点。
从表面上看,Python 中的生成器看起来像函数,但在语法和语义上都存在差异。一个显着特征是产量声明。yield 语句将函数转换为生成器。生成器是一个返回生成器对象的函数。这个生成器对象可以看作是一个函数,它产生一系列结果而不是单个对象。这个值序列是通过迭代它产生的,例如使用 for 循环。可以迭代的值是通过使用 yield 语句创建的。yield 语句创建的值是 yield 关键字之后的值。当到达 yield 语句时,代码的执行停止。将返回 yield 背后的值。生成器的执行现在被中断。一旦在生成器对象上再次调用“next”,生成器函数将在最后一次调用的代码中的 yield 语句之后立即恢复执行。执行将在最后一次让步之后生成器离开的状态继续执行。换句话说,所有局部变量仍然存在,因为它们在调用之间自动保存。这是与函数的根本区别:函数总是在函数体的开头开始执行,而不管它们在之前的调用中离开了哪里。它们没有任何静态或持久值。生成器的代码中可能有多个 yield 语句,或者 yield 语句可能位于循环体内。如果生成器的代码中有 return 语句,当 Python 解释器执行此代码时,执行将停止并出现 StopIteration 异常错误。“发电机”这个词
可以用生成器完成的所有事情也可以用基于类的迭代器来实现。然而,生成器的关键优势在于自动创建方法__iter__
() 和 next()。生成器提供了一种非常简洁的方式来生成巨大甚至无限的数据。
下面是一个简单的生成器示例,它能够生成各种城市名称。
可以用这个生成器创建一个生成器对象,它一个接一个地生成所有城市名称。
def city_generator ():
yield ( "Hamburg" )
yield ( "Konstanz" )
yield ( "Berlin" )
yield ( "Zurich" )
yield ( "Schaffhausen" )
yield ( "Stuttgart" )
我们通过调用 city_generator() 创建了一个迭代器:
city = city_generator ()
打印(下一个(城市))
输出:
汉堡
打印(下一个(城市))
输出:
康斯坦茨
打印(下一个(城市))
输出:
柏林
打印(下一个(城市))
输出:
苏黎世
打印(下一个(城市))
输出:
沙夫豪森
打印(下一个(城市))
输出:
斯图加特
打印(下一个(城市))
输出:
-------------------------------------------------- ------------------------- StopIteration Traceback (最近一次调用最后一次)in ---- > 1打印( next ( city ) ) StopIteration :
如我们所见,我们city
在交互式 shell 中生成了一个迭代器。每次调用该方法都会next(city)
返回另一个城市。在最后一个城市,即斯图加特创建完成后,另一个调用next(city)
抛出异常,说迭代已经停止,即StopIteration
. “我们可以向迭代器发送重置吗?” 是一个常见问题,因此它可以重新开始迭代。没有重置,但可以创建另一个生成器。这可以通过例如再次使用语句“city = city_generator()”来完成。虽然乍一看,yield 语句看起来像函数的 return 语句,但我们可以在这个例子中看到有很大的不同。如果我们有一个 return 语句而不是前一个例子中的 yield,它就是一个函数。但此函数将始终返回第一个城市“汉堡”,而不会返回任何其他城市,即“康斯坦茨”、“柏林”、“苏黎世”、“沙夫豪森”和“斯图加特”
正如我们在本章的介绍中所阐述的,生成器提供了一种生成迭代器的舒适方法,这就是它们被称为生成器的原因。
工作方法:
我们将在以下示例中说明这种行为。生成器计数创建一个迭代器,它通过从起始值 'firstval' 开始计数并使用 'step' 作为计数的增量来创建一系列值:
def count ( firstval = 0 , step = 1 ):
x = firstval
while True :
<