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=', ')