25. 生成器和迭代器的详解

1. 生成器

(1) 在 python 中,使用了 yield 关键字的函数被称为生成器。也可以用基于tuple的推导式来创建一个生成器(见下文(6))。
(2) 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器
(3) 在调用生成器运行的过程中,每次遇到yield关键字时函数会暂停并保存当前所有的运行信息返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行
(4) 调用一个生成器函数,返回的是一个迭代器对象。

# 普通函数
def test():
    pass

# 生成器
def g_test():
    m, n = 1, 2
    yield m, n
    pass


print(test()) # 调用普通函数
print(g_test()) # 调用生成器, 返回了一个生成器

在这里插入图片描述
(5) yield后面可以加多个数值(可以是任意类型),如果返回的个数>=2个,则是以元组的形式返回的,否则就正常返回。
那怎么理解yield的运行过程?
  这里,最难理解的就是generator函数和普通函数的执行流程不一样。普通函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行

def test():
    count = 0
    counter = 0
    List = [1, 2, 3, 4]
    Set = {1, 2, 3, 4}
    while True:
        count += 1
        yield count
        yield count, counter
        yield count, List, Set


# 初次调用test(), 执行到第一个yield处, 此时会执行一个操作,
# 程序并不会继续往下执行了, 而是暂停该函数并保存上下文运行信息,  然后直接返回到调用处, 等待被next()激活; 
it = test()  

# 第一次调用next()时, 激活之前函数中的暂停状态, 此时会进入到函数体继续执行函数, 当遇到yield语句时直接返回值(count);
# 此时遇到第二个yeild, 继续暂停返回, 等待被next()激活;
# 当再次调用next()时, 又从上次返回yield的语句出开始继续执行, 对应到上面代码, 就是执行第二个yield, 直接返回值(count,counter, 以元组形式返回).
# 此时遇到第三个yield, 同样继续暂停并返回, 等待被next()激活;
# 当第三次调用时next()时, 继续上面流程, 直接执行第三个yield, 然后返回值(count,List,Set, 以元组形式返回).

print(next(it))  # 返回值, 并继续执行, 在第二个yield处暂停返回
print(next(it))  # 返回值, 并继续执行, 在第三个yield处暂停返回
print(next(it))  # 返回值, 并继续执行

在这里插入图片描述

请添加图片描述

(6) 除了yield关键字可以创建生成器,基于tuple的推导式也会创建一个生成器
这个我们在之前的文章就有写过(https://blog.csdn.net/TianYanRen111/article/details/128744906),可参考此篇中的tuple推导式小节。

# 如果要一个一个打印出来, 可以通过next()函数获得generator的下一个返回值.
# generator保存的是算法, 每次调用next(g), 就计算出g的下一个元素的值, 
# 直到计算到最后一个元素, 没有更多的元素时, 抛出StopIteration的错误。

g = (item for item in ['aaa', 'bbb', 'ccc', 'ddd']) # 返回的是一个生成器对象

print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))  # 没有更多元素时抛出StopIteration错误

在这里插入图片描述

2. 迭代器

我们已经知道,可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function;
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable
可以通过collections.abc模块的Iterable类型判断。
在这里插入图片描述

(1) 迭代是python最强大的功能之一,是访问集合元素的一种方式。
(2) 迭代器是一个可以记住遍历的位置的对象。
(3) 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退
(4) 迭代器有两个基本的方法:iter() 和 next()
(5) 字符串,列表或元组对象都可用于创建迭代器

from collections.abc import Iterable

print(isinstance('', Iterable))  # 字符串
print(isinstance((), Iterable))  # 元组
print(isinstance([], Iterable))  # 列表
print(isinstance({}, Iterable))  # 字典
print(isinstance(set(), Iterable))  # 集合
# 创建迭代器
list = [1,2,3,4]
it = iter(list)   # 创建迭代器对象
print(next(it))   # 输出迭代器的下一个元素
print(next(it))

在这里插入图片描述
(6) 迭代器对象也可以使用常规的for语句进行遍历。

list = [5,6,7,8]
it = iter(list)
for item in it:
    print(item, end=" ")
# 运行结果:> 5 6 7 8 

(7) 创建一个迭代器
把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()__next__()
[a]. __iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。
[b]. __next__() 方法(Python 2 里是 next())会返回下一个迭代器对象。

# 创建一个返回数字的迭代器,初始值为 1,逐步递增 1. 
class MyNumbers:
    def __iter__(self):
        self.a = 1
        return self
    def __next__(self):
        x = self.a
        self.a += 1
        return x
        
myIter = iter(MyNumbers())
print(next(myIter))
print(next(myIter))
print(next(myIter))
print(next(myIter))

在这里插入图片描述
(8) StopIteration
StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。

# 在 20 次迭代后停止执行, 否则抛出异常StopIteration.
class MyNumbers:
    def __iter__(self):
        self.a = 1
        return self
    def __next__(self):
        if self.a <= 20:
            x = self.a
            self.a += 1
            return x
        else:
            raise StopIteration

myiter = iter(MyNumbers())
for item in myiter:
    print(item, end=', ')
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@十三阿哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值