这篇文章主要介绍了python生成器有几种写法,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获,下面让小编带着大家一起了解一下。
1. 生成器创建方式一
以 list
容器为例,在使用该容器迭代一组数据时,必须事先将所有数据存储到容器中,才能开始迭代;而生成器却不同,它可以实现在迭代的同时生成元素。并不会一次性生成它们,而是什么时候需要,才什么时候生成python画心的代码演示。
在生成列表或者元组的时候,我们常常使用生成器。
① 在列表中,直接生成新的列表
ls = [i for i in range(1, 10) if i % 2 == 0]
print(type(ls))
执行结果:
② 在元组中,生成的则为 generator 类型
l = (i for i in range(1, 10000000) if i % 2 == 0) # 这里只是创建了一个生成器,并不会执行输出里面的元素
print(type(l))
执行结果:
对于 generator
类型,生成器本身通过遍历指针获得新元组,当生成器完成后指针也就失效,这时再通过该生成器则不会有结果
l = (i for i in range(1, 10) if i % 2 == 0)
print("生成器在第一次生成元组时:", tuple(l))
print("当再此利用其生成元组时", tuple(l))
执行结果:
可见,生成器仅在第一次遍历时生效。
③ 探究生成器的工作机制
l = (i for i in range(1, 10) if i % 2 == 0)
print(next(l))
print(next(l))
print(l.__next__())
print(l.__next__())
print(l.__next__())
执行结果: 当指针到达末尾时,next
返回的数据也就不存在了,这时编译器便会报错。这里的 next()
和 生成器.__next()__
没有区别,效果一样
2. 生成器的创建方式二
创建生成器的步骤:
- 定义一个以
yield
关键字标识返回值的函数; - 调用刚刚创建的函数,即可创建一个生成器。
def intNum():
print("开始执行")
for i in range(5):
yield i
print("继续执行")
num = intNum()
由此,我们就成功创建了一个 num
生成器对象。显然,和普通函数不同,intNum()
函数的返回值用的是 yield
关键字,而不是 return
关键字,此类函数又成为生成器函数。
和 return
相比,yield
除了可以返回相应的值,还有一个更重要的功能,即每当程序执行完该语句时,程序就会暂停执行。不仅如此,即便调用生成器函数,Python
解释器也不会执行函数中的代码,它只会返回一个生成器(对象)。
要想使生成器函数得以执行,或者想使执行完 yield
语句暂停的程序得以继续执行,有以下 2 种方式:
生成器.__next__()
方法 或 Pyhon 内置的next()
方法;- 通过 for 循环遍历生成器。
# 创建生成器函数
def intNum():
print("开始执行")
for i in range(5):
yield i
print("继续执行")
# 调用生成器函数并然会生成器对象
num = intNum()
#调用 next() 内置函数
print(next(num))
#调用 __next__() 方法
print(num.__next__())
#通过for循环遍历生成器
for i in num:
print(i)
执行结果:
执行流程:
-
首先,在创建有
num
生成器的前提下,通过其调用next()
内置函数,会使 Python 解释器开始执行intNum()
生成器函数中的代码,因此会输出“开始执行”,程序会一直执行到yield i
,而此时的i == 0
,因此Python
解释器输出 “0”。由于受到 yield 的影响,程序会在此处暂停。 -
然后,我们使用
num
生成器调用__next__()
方法,该方法的作用和next()
函数完全相同(事实上,next()
函数的底层执行的也是__next__()
方法),它会是程序继续执行,即输出 “继续执行”,程序又会执行到yield i
,此时i==1
,因此输出 “1”,然后程序暂停。 -
最后,我们使用
for
循环遍历num
生成器,之所以能这么做,是因为for
循环底层会不断地调用next()
函数,使暂停的程序继续执行,因此会输出后续的结果。
除此之外,还可以使用 list()
函数和 tuple()
函数,直接将生成器能生成的所有值存储成列表或者元组的形式。
# 创建生成器函数
def intNum():
print("开始执行")
for i in range(5):
yield i
print("继续执行")
# 调用生成器函数并然会生成器对象
num = intNum()
print(list(num))
执行结果:
# 创建生成器函数
def intNum():
print("开始执行")
for i in range(5):
yield i
print("继续执行")
# 调用生成器函数并然会生成器对象
num = intNum()
print(tuple(num))
执行结果:
通过输出结果可以判断出,list()
和 tuple()
底层实现和 for 循环的遍历过程是类似的。
深入剖析生成器
yied
, 可以去阻断当前的函数执行, 然后, 当使用 next()
函数, 或者, __next__(),
都会让函数继续执行, 然后, 当执行到下一个 yield
语句的时候, 又会被暂停
def test():
print("xxx")
yield 1
print("a")
yield 2
print("b")
yield 3
print("c")
yield 4
print("d")
yield 5
print("e")
g = test()
print(g)
print(next(g))
print(next(g))
执行过程: 调用第一个 next(g)
,会执行 print("xxx")
,并在遇到 yield 1
后暂停执行,返回 1
,由于执行了 print(next(g))
,所以 1
被打印了出来,并保存执行状态,在执行第二个 next(g)
时,执行 print("a")
再遇到 yield 2
,并返回 2
,同时由于 print(next(g))
,所以 2
被打印了出来,后续同理
再看一个案例:
def test():
for i in range(1, 9):
yield i
g = test()
print(g)
next(g)
print(next(g))
执行结果:
执行流程: 定义好生成器函数后,通过 g = test()
得到一个生成器对象,执行 next(g)
时,会进入第一个循环,此时 i == 1
,所以 yield i
会返回 1
,但是并为通过 print
函数打印,然后执行 print(next(g))
,此时 i == 2
, 在执行到 yield i
时,返回 2
, 并通过 print
函数打印出
3. send 方法
send
不仅具有 next()
功能,还能够给上一个 yield
语句传值
def test():
# print("xxx")
res1 = yield 1
print(res1)
res2 = yield 2
print(res2)
g = test()
print(next(g))
print(g.__next__())
执行结果:
第一个 1
是通过 yield 1
返回后再通过 print(next(g))
打印出来,此时 res1
接收 yield 1
的赋值,为 None
,在下次执行时,优先打印出 None
def test():
# print("xxx")
res1 = yield 1 # "ooo"
print(res1)
res2 = yield 2
print(res2)
g = test()
print(g.__next__())
print(g.send("ooo"))
执行结果:
执行流程:print(g.__next__())
,会触发第一个 yield
语句,并打印返回的 1
,然后再执行 g.send("ooo")
,此时给上一个 yield
语句传值,并复制给 res1
,由于自身具备 next() 函数功能,所以触发下一个 yield
语句执行,打印出了 ooo
,以及返回的值 2
注: 再没有优先利用 next
触发 yield
语句, send
就不能给上一个 yield
赋值,此时,只能传入None
,即只能执行 send(None)
4. close 方法
def test():
yield 1
print("a")
yield 2
print("b")
yield 3
print("c")
return 10
g = test()
print(g.__next__())
print(g.__next__())
g.close()
print(g.__next__())
执行结果: