《Python基础教程(第3版)》笔记:9.6迭代器和9.7生成器

本文介绍了Python中的迭代器和生成器概念。迭代器是实现__iter__和__next__方法的对象,用于遍历数据。生成器是包含yield语句的函数,允许在运行时动态生成序列。通过实例展示了如何创建斐波那契数列迭代器和使用生成器表达式。还讨论了递归式生成器以及如何处理类似字符串的对象。最后,提到了生成器的send方法,允许在生成器执行过程中传递值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《Python基础教程(第3版)》笔记:9.6迭代器和9.7生成器

可迭代任何实现了__iter__方法的对象,方法__iter__返回一个迭代器,它是包含方法__next__的对象;若迭代器没有可返回的值,将引发StopIteration异常。


注意 实现了方法__iter__的对象是可迭代的,而实现了方法__next__的对象是迭代器。


# 斐波那契数列的迭代器
class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1
    def __next__(self):
        self.a, self.b = self.b, self.a+self.b
        return self.a
    def __iter__(self):
        return self

fibs = Fibs()
for f in fibs:
    if f>1000:
        print(f)
        break

提示 通过对可迭代对象调用内置函数iter,可获得一个迭代器。

>>> it = iter([1,2,3])
>>> next(it)
1
>>> it
<list_iterator object at 0x00000188C71CCA30>
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    next(it)
StopIteration
9.6.2 从迭代器创建序列
  1. 使用构造函数list显式地将迭代器转换为列表

    class TestIterator:
        value = 0
        def __next__(self):
            self.value +=1
            if self.value > 10: raise StopIteration
            return self.value
        def __iter__(self):
            return self
    ti = testiterator()
    print(list(ti))   #[1,2,3,4,5,6,7,8,9,10]
            
    

    9.7 生成器

    生成器是一种使用普通函数语法定义的迭代器。

    def flatten(nested):
        for sublist in nested:
            for element in sublist:
                yield element
    nested = [[1,2], [3,4],[5]]
    print(list(flatten(nested)))
    

    包含yield语句的函数都被称为生成器。每次使用yield生成一个值后,函数都将冻结,即:“在此停止执行 ,等待被重新唤醒

生成器表达式

>>> g = ((i+2)**2 for i in range(2, 27))
>>> next(g)
9.7.2 递归式生成器

如果要处理任意层嵌套的列表

def flatten(nested):
    try:
        for sublist in nested:
            for element in flatten(sublist):
                yield element
    except TypeError:
        yield nested

调用flatten时,有两种可能性:基线条件和递归条件。在基线条件下,要求这个函数展开单个元素(如一个数)。在这种情况下,for循环将引发TypeError异常(因为你试图迭代一个数),而这个生成器只生成一个元素。

然而,如果要展开的是一个列表(或其他任何可迭代对象),你就需要做:**遍历所有的子列表(其中有些可能并不是列表)并对它们调用flatten,然后使用另一个for循环生成展开后的子列表中的所有元素。这可能看起来有点不可思议,但确实可行。


>>> list(flatten([[[1], 2], 3, 4, [5, [6, 7]], 8])) 
[1, 2, 3, 4, 5, 6, 7, 8] 

然而,这个解决方案存在一个问题。如果nested是字符串或类似于字符串的对象,它就属于序列,因此不会引发TypeError异常,可你并不想对其进行迭代。


注意 在函数flatten中,不应该对类似于字符串的对象进行迭代,主要原因有两个。首先,你想将类似于字符串的对象视为原子值,而不是应该展开的序列。其次,对这样的对象进行迭代会导致无穷递归,因为字符串的第一个元素是一个长度为1的字符串,而长度为1的字符串的第一个元素是字符串本身!


要处理这种问题,必须在生成器开头进行检查。要检查对象是否类似于字符串,最简单、最快捷的方式是,尝试将对象与一个字符串拼接起来,并检查这是否会引发TypeError异常①。添加这种检查后的生成器如下:

def flatten(nested): 
    try: 
        # 不迭代类似于字符串的对象:
        try: nested + '' 
        except TypeError: pass 
        else: raise TypeError 
        for sublist in nested:
            for element in flatten(sublist): 
                yield element 
    except TypeError: 
        yield nested

如你所见,如果表达式nested + ''引发了TypeError异常,就忽略这种异常;如果没有引发TypeError异常,内部try语句中的else子句将引发TypeError异常,这样将在外部的excpet子句中原封不动地生成类似于字符串的对象。明白了吗?

9.7.3 通用生成器

生成器是包含关键字yield的函数,但被调用时不会执行函数体内的代码,而是返回一个迭代器。

生成器由两个单独的部分组成:生成器的函数和生成器的迭代器。

  • 生成器的函数是由def语句定义的,其中包含yield
  • 生成器的迭代器是这个函数返回的结果

注意观察下列代码的反馈:

>>> def simple_generator():
        yield 1
... 
>>> simple_generator
<function simple_generator at 153b44>
>>> simple_generator()
<generator object at 1510b0>
9.7.4 生成器的方法

在生成器开始运行后,可使用生成器和外部之间的通信渠道向它提供值。这个通信渠道包含如下两个端点。

  • 外部世界:外部世界可访问生成器的方法send,接受一个参数(要发送的“消息”,可以是任何对象)。

  • 生成器:在挂起的生成器内部,yield可能用作表达式而不是语句。换而言之,当生成器重新运行时,yield返回一个值——通过send从外部世界发送的值。如果使用的是next,yield将返回None

    请注意,仅当生成器被挂起(即遇到第一个yield)后,使用send(而不是next)才有意义。
    要在此之前向生成器提供信息,可使用生成器的函数的参数。

def repeater(value): 
    while True: 
        new = (yield value) 
        if new is not None: value = new 
>>> r = repeater(42) 
>>> next(r) 
42
>>> next(r) 
42

>>> r.send("Hello, world!") 
"Hello, world!" 

>>> next(r)
"Hello, world!"
>>> next(r)
"Hello, world!"

注意 使用圆括号将yield表达式括起来了。并非必须这样做,但小心驶得万年船。如果要以某种方式使用返回值,就不管三七二十一,将其用圆括号括起吧。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔚蓝慕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值