利用迭代器,我们可以在每次迭代获取数据时(通过next()方法)按照特定规律进行生成。但是我们在实现一个迭代器时,关于当前迭代的状态需要我们自己记录,进而才能根据当前的状态生成下一个数据。为了达到纪录当前状态,并配合next()函数进行迭代使用,可以采用更简洁的语法。
即生成器(generator),生成器是一种特殊迭代器,它比迭代器更优雅
生成器创建方法
1.把一个列表生成式的 [ ] 改成 ( )
li = [x ** 2 for x in range(6)]
print(li)
'''
[0, 1, 4, 9, 16, 25]
'''
gen = (x ** 2 for x in range(6))
print(gen)
'''
<generator object <genexpr> at 0x00000268D3C48BF8>
'''
print("通过next()函数取得下一个值")
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
'''
通过next()函数取得下一个值
0
1
4
9
16
25
'''
print("通过for遍历")
gen = (x ** 2 for x in range(6))
for i in gen:
print(i, end=' ')
'''
通过for遍历
0 1 4 9 16 25
'''
2.用函数实现
import collections
class FibIterator(object):
"""斐波那契数列迭代器"""
def __init__(self, n):
# 记录生成fibonacci的数列的个数
self.n = n
# 记录当前遍历的下标
self.current_index = 0
# 记录fibonacci数列前面的两个值
self.num1 = 0
self.num2 = 1
def __next__(self):
"""被next()函数调用来获取下一个数"""
if self.current_index < self.n:
num = self.num1
self.num1, self.num2 = self.num2, self.num1 + self.num2
self.current_index += 1
return num
else:
raise StopIteration
def __iter__(self):
"""迭代器的__iter__返回自身即可"""
return self
在用迭代器实现的方式中,我们要借助几个变量(n、current_index、num1、num2)来保存迭代的状态。现在我们用生成器来实现一下。
def fib(n):
current_index = 0
num1, num2 = 0, 1
while current_index < n:
# print(num1) # 打印斐波那契数列
"""
1. 假如函数中有yield,则不再是函数,而是生成器
2. yield 会产生一个断点
3. 假如yield后面紧接着一个数据,就会把数据返回,
作为next()函数或者for ...in...迭代出的下一个值
"""
yield num1
num1, num2 = num2, num1 + num2
current_index += 1
if __name__ == '__main__':
# 假如函数中有yield,则不再是函数,而是一个生成器
gen = fib(5)
# 生成器是一种特殊的迭代器
for num in gen:
print(num)
在使用生成器实现的方式中,我们将原本在迭代器__next__
方法中实现的基本逻辑放到一个函数中来实现,但是将打印输出方式换成 yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在函数中有yield关键字的 就称为 生成器
此时按照调用函数的方式( 案例中为gen = fib(5) )使用生成器就不再是执行函数体了,而是会返回一个生成器对象( 案例中为gen ),然后就可以按照使用迭代器的方式来使用生成器了。
# 使用for...in ...打印生成器的内容
if __name__ == '__main__':
# 假如函数中有yield,则不再是函数,而是一个生成器
gen = fib(5)
# 生成器是一种特殊的迭代器
for num in gen:
print(num)
'''
0
1
1
2
3
5
'''
生成器的执行过程
def fib(n):
current_index = 0
num1, num2 = 0, 1
print("---1---")
while current_index < n:
print("---2---")
# print(num1) # 打印斐波那契数列
yield num1 # 假如函数中有yield,则不再是迭代器,而是生成器
print("---3---")
num1, num2 = num2, num1 + num2
current_index += 1
print("---4---")
if __name__ == '__main__':
# 假如函数中有yield,则不再是函数,而是一个生成器
gen = fib(5)
# 取得生成器的下一个值
print(next(gen))
print(next(gen))
print(next(gen))
'''
---1---
---2---
0
---3---
---4---
---2---
1
---3---
---4---
---2---
1
'''
-
在函数中如果出现了yield关键字,那么该函数不再是一个普通函数而是一个生成器函数
-
yield关键字有两点作用:
- 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
- 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
-
可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
生成器支持的方法
-
close()
-
手动关闭生成器函数,后面调用会直接引起StopIteration异常
def gen(): yield 1 yield 2 yield 3 yield 4 g = gen() print(next(g)) print(next(g)) g.close() print(next(g)) # 回溯 ''' 1 2 StopIteration '''
-
-
send()
- send()的作用就是使receive赋值为其所传送的值,然后让生成器执行到下一个yield
- 如果生成器未启动,则必须在使用send()前启动生成器,而启动的方法可以是gen.next(),也可以是gen.send(None)执行到第一个yield处。之后就可以使用send()不断的传入值了
- 如果已启动,则send(para)的作用就是给x付志伟发送的值(send的参数),然后让生成器执行到下一个yield
def gen():
value = 0
while True:
receive = yield value
if receive == "e":
break
value = "got:%s" % receive
# send()的作用就是使receive赋值为其所传送的值,然后让生成器执行到下一个yield
g = gen()
print(g.send(None))
print(g.send("aaa"))
print(g.send(123))
'''
0
got:aaa
got:123
'''
print(g.send("e"))
'''
StopIteration
'''