生成器和迭代器

Posted 程序员孔乙己

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了生成器和迭代器相关的知识,希望对你有一定的参考价值。

介绍

什么是迭代器?迭代器是可以像我们在 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 (最近一次调用最后一次)
 <ipython-input-22-02870f953c83> in <module> 
---- > 1打印( next ( city ) ) 
StopIteration :

如我们所见,我们city在交互式 shell 中生成了一个迭代器。每次调用该方法都会next(city)返回另一个城市。在最后一个城市,即斯图加特创建完成后,另一个调用next(city)抛出异常,说迭代已经停止,即StopIteration. “我们可以向迭代器发送重置吗?” 是一个常见问题,因此它可以重新开始迭代。没有重置,但可以创建另一个生成器。这可以通过例如再次使用语句“city = city_generator()”来完成。虽然乍一看,yield 语句看起来像函数的 return 语句,但我们可以在这个例子中看到有很大的不同。如果我们有一个 return 语句而不是前一个例子中的 yield,它就是一个函数。但此函数将始终返回第一个城市“汉堡”,而不会返回任何其他城市,即“康斯坦茨”、“柏林”、“苏黎世”、“沙夫豪森”和“斯图加特”

操作方法

正如我们在本章的介绍中所阐述的,生成器提供了一种生成迭代器的舒适方法,这就是它们被称为生成器的原因。

 

工作方法:

  • 生成器就像函数一样被调用。它的返回值是一个迭代器,即生成器对象。生成器的代码不会在这个阶段执行。
  • 迭代器可以通过调用 next 方法来使用。第一次执行像函数一样开始,即迭代器主体内的第一行代码。代码会一直执行,直到到达 yield 语句。
  • yield返回表达式的值,它跟在关键字 yield 之后。这就像一个函数,但是 Python 会跟踪这个 yield 的位置,并且会存储局部变量的状态以备下次调用。在下一次调用时,继续执行 yield 语句之后的语句,并且变量的值与它们在前一次调用中的值相同。
  • 如果生成器主体已完全运行或程序流遇到没有值的返回语句,则迭代器已完成。

我们将在以下示例中说明这种行为。生成器计数创建一个迭代器,它通过从起始值 'firstval' 开始计数并使用 'step' 作为计数的增量来创建一系列值:

def  count ( firstval = 0 ,  step = 1 ): 
    x  =  firstval 
    while  True : 
        yield  x 
        x  +=  step 
counter  =  count ()  # 计数将从 0 开始
for  i  in  range ( 10 ): 
    print ( next ( counter ),  end = ", " ) 
start_value  =  2.1 
stop_value  =  0.3
打印(" \\n新计数器:" ) 
counter  =  count ( start_value ,  stop_value ) 
for  i  in  range ( 10 ): 
    new_value  =  next ( counter ) 
    print ( f "  new_value : 2.2f  " ,  end = ", " )

输出:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
新柜台:
2.10, 2.40, 2.70, 3.00, 3.30, 3.60, 3.90, 4.20, 4.50, 4.80, 

斐波那契作为生成器

斐波那契数列以比萨的列奥纳多命名,他被称为斐波那契(filius Bonacci,“博纳乔之子”的缩写)。在他的教科书 Liber Abaci 中,出现在 1202 年)他有一个关于兔子及其繁殖的练习:它从一对新生的兔子开始,即雄性和雌性。他们需要一个月的时间才能交配。在第二个月末,雌性生下了一对新兔子。现在让我们假设每只母兔在第一个月结束后每个月都会生另一对兔子。我们不得不提到斐波那契的兔子永远不会死。他们的问题是在一段时间后人口将有多大。

这会产生一系列数字:0, 1, 1, 2, 3, 5, 8, 13

这个序列可以用数学术语定义如下:

Fn=Fn-1+Fn-2 与种子值: F0=0 和 F1=1

def  fibonacci ( n ): 
    """ 用于创建斐波那契数列的生成器 """ 
    a ,  b ,  counter  =  0 ,  1 ,  0 
    while  True : 
        if  ( counter  >  n ):  
            return 
        yield  a 
        a ,  b  =  b ,  a  +  b
        计数器 +=  1 
f  =  fibonacci ( 5 ) 
for  x  in  f :
    打印( x ,  " " ,  end = "" )  #
打印()

输出:

0 1 1 2 3 5  

上面的生成器可用于创建由空格分隔的前 n 个斐波那契数字,或者更好的 (n+1) 个数字,因为还包括第 0 个数字。在下一个示例中,我们展示了一个能够返回无限迭代器的版本。当我们使用这个迭代器时,我们必须注意使用终止条件:

def  fibonacci (): 
    """按需生成无限序列的斐波那契数列""" 
    a ,  b  =  0 ,  1 
    while  True : 
        yield  a 
        a ,  b  =  b ,  a  +  b 
f  =  fibonacci () 
counter  =  0 
for  x  in  f : 
    print ( x ,  " " ,  end = "" ) 
    counter  +=  1 
    if  (counter  >  10 ): 
        中断 
打印()

输出:

0 1 1 2 3 5 8 13 21 34 55  

在生成器中使用“返回”

从 Python 3.3 开始,生成器也可以使用 return 语句,但是生成器仍然需要至少一个 yield 语句才能成为生成器!生成器内部的 return 语句等价于 raise StopIteration()

让我们看一下我们在其中引发 StopIteration 的生成器:

def  gen (): 
    yield  1 
    raise  StopIteration ( 42 ) 
    yield  2
g  =  gen ()
下一个( g )

输出:

1
下一个( g )

输出:

-------------------------------------------------- ------------------------- 
StopIteration                              Traceback(最近一次调用最后一次)
 <ipython-input-26-6735830131e8> in gen () 
      2      yield  1 
- ---> 3 raise StopIteration ( 42 ) 
      4      yield  2 
StopIteration : 42
上述异常是以下异常的直接原因:
RuntimeError                               Traceback(最近一次调用最后一次)
 <module>中的<ipython-input-29-e734f8aca5ac > 
----> 1 next ( g ) 
RuntimeError : generator raise StopIteration

我们现在证明这return“几乎”等同于引发“StopIteration”异常。

def  gen (): 
    yield  1 
    return  42 
    yield  2
g  =  gen ()
下一个( g )

输出:

1
下一个( g )

输出:

-------------------------------------------------- ------------------------- 
StopIteration                              Traceback(最近一次调用最后一次)
 <ipython-input-32-e734f8aca5ac> in <module> 
---- > 1 next ( g ) 
StopIteration : 42

发送方法/协程

生成器不仅可以发送对象,还可以接收对象。向生成器发送消息,即一个对象,可以通过对生成器对象应用send 方法来实现。请注意, send 既向生成器发送一个值,又返回生成器生成的值。我们将在以下简单的协程示例中演示这种行为:

def  simple_coroutine (): 
    print ( "coroutine has been started!" ) 
    while  True : 
        x  =  yield  "foo" 
        print ( "coroutine received:" ,  x ) 
cr  =  simple_coroutine () 
cr

输出:

<位于 0x7fa2a8049ac0 的生成器对象 simple_coroutine>
下一个( cr )

输出:

协程已经启动!
ret_value  =  cr 发送(“嗨” 打印“'发送'返回:” , ret_value )

输出:

收到协程:嗨
“发送”返回:foo

我们必须先在生成器上调用 next,因为生成器需要启动。使用发送到尚未启动的生成器会导致异常。

要使用send 方法,生成器必须等待yield 语句,以便发送的数据可以被处理或分配给左边的变量。到目前为止我们还没有说:下一个调用也发送和接收。它总是发送一个 None 对象。“next”和“send”发送的值被分配给生成器内的一个变量:这个变量new_counter_val在下面的例子中被调用。

下面的示例通过添加一个send功能来修改上一章节中的生成器“计数” 。

发送的另一个例子

from  random  import  choice 
def  song_generator ( song_list ): 
    new_song  =  None 
    while  True : 
        if  new_song  !=  None : 
            if  new_song  not  in  song_list : 
                song_list 追加(new_song )
            new_song  =  yield  new_song 
        else :
            new_song  =  yield (选择(song_list ))           
songs  =  [“她的 Şeyi Yak - Sezen Aksu” “Bluesette - Toots Thielemans” “六个马林巴琴 - Steve Reich” “Riverside - Agnes Obel” “Not for Radio - Nas” “What's going on - Taste” “On Stream” - Nils Petter Molvær” “La' Inta Habibi - Fayrouz “Ik Leef Niet Meer Voor Jou - Marco Borsato” “Δέκα λεπτά - Αθηνά Ανδρεάδη” ]
radio_program  =  song_generator (歌曲)
下一个( radio_program )

输出:

'河畔 - 艾格尼丝奥贝尔'
对于 范围(3 ):
    打印下一个(radio_program ))

输出:

Bluesette - Toots Thielemans
六个马林巴斯 - 史蒂夫赖希
六个马林巴斯 - 史蒂夫赖希
广播节目发送(“扭曲的天使 - 存档” 

输出:

'扭曲的天使 - 档案'
歌曲

输出:

['她的Şeyi Yak - Sezen Aksu',
 'Bluesette - Toots Thielemans',
 '六个马林巴斯 - 史蒂夫赖希',
 '河畔 - 艾格尼丝奥贝尔',
 '不适合广播 - Nas',
 “这是怎么回事 - 味道”,
 'On Stream - Nils Petter Molvær',
 "La' Inta Habibi - Fayrouz",
 'Ik Leef Niet Meer Voor Jou - Marco Borsato',
 'Δέκα λεπτά - Αθηνά Ανδρεάδη',
 '扭曲的天使 - 档案']

到目前为止,我们可以通过发送一首新歌曲来改变歌曲交互器的行为,即标题加上表演者。我们现在将扩展生成器。我们可以发送新的歌曲列表。我们必须向新迭代器发送一个元组,或者是一个元组:

(, ) 或者

("-歌曲列表-", )

 随机 导入 选择
def  song_generator ( song_list ): 
    new_song  =  None 
    while  True : 
        if  new_song  !=  None : 
            if  new_song [ 0 ]  ==  "-songlist-" : 
                song_list  =  new_song [ 1 ] 
                new_song  =  yield ( choice ( song_list )) 
            否则:
                标题, 表演者 =  new_song 
                new_song = 标题 +  “ - ”  + 表演
                ,如果 new_song 没有  song_list :
                    song_list 追加(new_song )
                new_song  =  yield  new_song 
        else :
            new_song  =  yield (选择(song_list ))   
歌曲 1  =  [ “Après un Rêve - Gabriel Fauré” 
         “On Stream - Nils Petter Molvær” “Der Wanderer Michael - Michael Wollny” “Les barricades mystérieuses - Barbara Thompson” “Monday - Ludovico Einaudi” ]
歌曲 2  =  [ “Dünyad” Uszak - Pinhani” “Again - Archive” “If I 
          have a Hear - Fever Ray”、“每个你,每个我 - 安慰剂” “熟悉的 - Angnes Obel” ]
radio_prog  =  song_generator ( song1 )
对于 范围(5 ):
    打印下一个(radio_prog ))

输出:

Les barricades mystérieuses - 芭芭拉·汤普森
Après un Rêve - Gabriel FauréOn Stream - Nils Petter Molvær
Les barricades mystérieuses - 芭芭拉·汤普森
Les barricades mystérieuses - 芭芭拉·汤普森
Après un Rêve - Gabriel FauréOn Stream - Nils Petter Molvær

我们现在通过交换歌曲列表来改变广播节目:

radio_prog 发送((“-songlist-” , song2 ))

输出:

《熟悉——安涅斯·奥贝尔》
对于 范围(5 ):
    打印下一个(radio_prog ))

输出:

再次 - 存档
如果我听到了 - 发烧射线每一个你,每一个我 - 安慰剂
Dünyadan Uszak - Pinhani
Dünyadan Uszak - Pinhani
如果我听到了 - 发烧射线每一个你,每一个我 - 安慰剂

抛出方法

throw() 方法在生成器暂停时引发异常,并返回生成器生成的下一个值。如果生成器退出而没有产生另一个值,它会引发 StopIteration。生成器必须捕获传入的异常,否则异常将传播给调用者。

count之前示例中生成器的无限循环不断产生顺序数据的元素,但我们没有关于“计数器”变量的索引或状态的任何信息。我们可以count通过在throw方法中抛出异常来获取这些信息,即变量的状态。我们在生成器内部捕获此异常并打印“count”值的状态:

def  count ( firstval = 0 ,  step = 1 ): 
    counter  =  firstval 
    while  True : 
        try : 
            new_counter_val  =  yield  counter 
            if  new_counter_val  is  None : 
                counter  +=  step 
            else : 
                counter  =  new_counter_val 
        except  Exception : 
            yield  ( firstval ,  step ,  counter )

在下面的代码块中,我们将展示如何使用这个生成器:

c  =  count () 
for  i  in  range ( 6 ): 
    print ( next ( c )) 
print ( "让我们看看迭代器的状态是什么:" ) 
state_of_count  =  c throw ( Exception ) 
print ( state_of_count ) 
print ( "now, we can continue:" ) 
for  i  in  range ( 3 ): 
    print ( next ( c ))

输出:

0
1
2
3
4
5
让我们看看迭代器的状态是什么:
(0, 1, 5)
现在,我们可以继续:
5
6
7

我们可以通过定义我们自己的异常类 StateOfGenerator 来改进前面的例子:

class  StateOfGenerator ( Exception ): 
     def  __init__ ( self ,  message = None ): 
         self message  =  message 
def  count ( firstval = 0 ,  step = 1 ): 
    counter  =  firstval 
    while  True : 
        try : 
            new_counter_val  =  yield  counter 
            if  new_counter_val  is  None : 
                counter  +=  step
            else : 
                counter  =  new_counter_val
        除了 StateOfGenerator : 
            yield  ( firstval ,  step ,  counter )

我们可以像这样使用之前的生成器:

c  =  count () 
for  i  in  range ( 3 ): 
    print ( next ( c )) 
print ( "让我们看看迭代器的状态是什么:" ) 
i  =  c throw ( StateOfGenerator ) 
print ( i ) 
print ( "now, we can continue:" ) 
for  i  in  range ( 3 ): 
    print ( next ( c ))

输出:

0
1
2
让我们看看迭代器的状态是什么:
(0, 1, 2)
现在,我们可以继续:
2
3
4

产量来自

“yield from”自 Python 3.3 起可用!该yield from <expr>语句可以在生成器的主体内使用。<expr>必须是一个评估为可迭代的表达式,从中提取迭代器。迭代器运行到耗尽,即直到遇到StopIteration异常。该迭代器向生成器的调用者(即包含 yield from 语句的调用者)生成和接收值。

我们可以通过查看两台发电机从下面的例子中学习“GEN1”和“第二代”的是,yield fromgen2 被替换为“GEN1”的循环:

def  gen1 (): 
    for  char  in  "Python" : 
        yield  char 
    for  i  in  range ( 5 ): 
        yield  i 
def  gen2 (): 
    yield from  "Python" 
    yield from  range ( 5 ) 
g1  =  gen1 () 
g2  =  gen2 ()
打印“G1” , 结束= “”  X  G1 :
    打印(X,  end = ", " )
打印( " \\n g2: " ,  end = ", " ) 
for  x  in  g2 : 
    print ( x ,  end = ", " ) 
print ()

输出:

g1: , P, y, t, h, o, n, 0, 1, 2, 3, 4, 
g2: , P, y, t, h, o, n, 0, 1, 2, 3, 4, 

我们可以从输出中看到两个生成器是相同的。

yield from 语句的好处可以看作是将生成器拆分为多个生成器的一种方式。这就是我们在前面的示例中所做的,我们将在以下示例中更明确地演示这一点:

DEF 城市():
    对于 城市  [ “柏林” “汉堡” “慕尼黑” “弗赖堡” ]:
        收率 城市
DEF 平方():
    对于 范围(10 ):
        产量**  2 
DEF  generator_all_in_one () :
    对于 城市 中的 城市():
        方块为单位的数量的产生 城市
    ():产生数量def   
         
 generator_splitted ():
    产率从 城市()
    从产率 平方()
LST1  =  [ EL  EL  generator_all_in_one ()] 
LST2  =  [ EL  EL  generator_splitted ()]
打印(LST1  ==  LST2 )

输出:

真的

前面的代码返回 True 因为生成器generator_all_in_onegenerator_splitted yield元素相同。这意味着如果<expr> fromyield from是另一个生成器,则效果与子生成器的主体在yield from语句点内联一样。此外,子生成器可以执行return带有值的语句,该值成为yield from表达式的值。我们用以下小脚本演示这一点:

def  subgenerator (): 
    yield  1 
    return  42 
def  delegating_generator (): 
    x  =  yield from  subgenerator () 
    print ( x ) 
for  x  in  delegating_generator (): 
    print ( x )

输出:

1
42

“PEP 380 -- 委派给子生成器的语法”中的六点根据生成器协议描述了 yield from 表达式的完整语义:

  • 迭代器产生的任何值都直接传递给调用者。
  • 使用 send() 发送到委托生成器的任何值都会直接传递给迭代器。如果发送的值为 None,则调用迭代器的next () 方法。如果发送的值不是 None,则调用迭代器的 send() 方法。如果调用引发 StopIteration,则恢复委托生成器。任何其他异常都会传播到委托生成器。
  • 除了 GeneratorExit 被抛出到委托生成器中的异常被传递给迭代器的 throw() 方法。如果调用引发 StopIteration,则恢复委托生成器。任何其他异常都会传播到委托生成器。
  • 如果在委托生成器中抛出 GeneratorExit 异常,或者委托生成器的 close() 方法被调用,则迭代器的 close() 方法如果有则调用。如果此调用导致异常,则将其传播到委托生成器。否则,在委托生成器中引发 GeneratorExit。
  • yield from 表达式的值是迭代器终止时引发的 StopIteration 异常的第一个参数。
  • 在生成器中返回 expr 会导致在退出生成器时引发 StopIteration(expr)。

递归生成器

以下示例是一个生成器,用于创建给定项目列表的所有排列。

对于那些不知道什么是排列的人,我们有一个简短的介绍:

正式定义:术语“排列”源自拉丁语动词“permutare”,意思是排列是有序列表元素的重新排列。换句话说:n 个元素的每一种排列都称为排列。

在以下几行中,我们向您展示了字母 a、b 和 c 的所有排列:

abc
ab
bac
bca
cab
cba

一组 n 个元素的排列数由 n!

啊!= n (n-1) (n-2) ... 2 * 1

啊!称为 n 的阶乘。

可以使用任意对象列表调用置换生成器。此生成器返回的迭代器生成所有可能的排列:

def  permutations ( items ): 
    n  =  len ( items ) 
    if  n == 0 :  yield  [] 
    else : 
        for  i  in  range ( len ( items )): 
            for  cc  in  permutations ( items [: i ] + items [ i + 1 ) :]):
                产量 [ items [ i ]] + cc 
for  p  置换([ <

以上是关于生成器和迭代器的主要内容,如果未能解决你的问题,请参考以下文章

迭代器和生成器

迭代器和生成器

Python之迭代器和生成器

5.迭代器和生成器

迭代器,可迭代对象,迭代器对象和生成器

迭代器 和 生成器