生成器对象(Generator Object)
本质: 一个迭代器的实例
特点:按需产生值,不会一次性将值生产并存储值内存,节省了内存空间
核心思想:惰性求值
实现
1.调用生成器函数(函数体内含yield关键字)
2.通过生成器表达式得到一个生成器对象
生成器函数( Generator Function)
一种特殊的python函数,它使用yield关键字,调用该函数时,会返回一个生成器对象
yield
python中的一个关键字,主要用于生成函数,作用:
1.当函数执行yield后,函数就会暂停
2.通过使用next()或send()再次执行函数时,函数会从yield下一行继续执行
def demo():
print("开始运行...")
yield
print("中断运行")
yield
g = demo()
next(g)
print("第一次运行完毕,准备运行第二次")
next(g)
————————————————————————————————————————————————————————
开始运行...
第一次运行完毕,准备运行第二次
中断运行
3.yield可向外产出值(yield 右侧的表达式),类似于return
def demo():
yield '开始运行..'
yield '中断运行'
g = demo()
print(next(g))
print("第一次运行完毕,准备运行第二次")
print(next(g))
————————————————————————————————————————————————————————
开始运行..
第一次运行完毕,准备运行第二次
中断运行
4.yield本身就是一个表达式,在右侧的时候,类似于形参,进行参数传递,将下一次调用的值,赋值给左边的变量。故而yield 表达式在赋值号右边,充当接收容器
def demo():
print('开始运行..')
x = yield
print(x)
yield
g = demo()
next(g)
print("第一次运行完毕,准备运行第二次")
g.send(8)
——————————————————————————————————————————————————————
开始运行..
第一次运行完毕,准备运行第二次
8
也是组合使用,同时起到暂停、返回值、参数传递
def demo():
print('开始运行..')
x = yield '第一次运行'
print(x)
yield
g = demo()
print(next(g))
print("第一次运行完毕,准备运行第二次")
g.send(8)
————————————————————————————————————————————————————
开始运行..
第一次运行
第一次运行完毕,准备运行第二次
8
生成器函数例子
def infinite_counter():
i = 1
while True:
yield i
i += 1
counter = infinite_counter()
print(counter) #<generator object infinite_counter at 0x0000026FCF765B40>
生成器表达式(Generator Expression)
-
语法:(表达式 for 变量 in 迭代器 if 条件)
- 返回值:一个生成器对象
m = (i for i in range(1, 11)) print(m) #<generator object <genexpr> at 0x0000014A3FEA5B40>
生成器对象的取值
next()
每调用一次生成器对象,就会从生成器中获取一个值。生成器函数执行完毕(自然结束或return)后,再调用next()/send() 就会抛出StopIteration异常。故而在使用next(),需要单独处理StopIteration异常,可使用try...except进行处理。StopIteration异常不是错误,而是迭代协议的正常信息,表示没有值了。
def infinite_counter():
i = 1
while i<4:
yield i
i += 1
counter = infinite_counter()
n=1
while n < 5:
print(next(counter))
n+=1
——————————————————————————————————————————————
Traceback (most recent call last):
File "D:\automation\casual\casual.py", line 30, in <module>
print(next(counter))
~~~~^^^^^^^^^
StopIteration
1
2
3
for循环
会自动调用next(),并会自动捕获StopIteration异常并停止
def infinite_counter():
i = 1
while i<4:
yield i
i += 1
counter = infinite_counter()
for item in counter:
print(item)
send(value)
将value传递给暂停的yield表达式,故而需要先预激活,使用next(g)/g.send(None),将生成对象遇到第一个yield表达式并暂停,等send(value)调用执行
未先预激活
def gen():
x = yield
yield x + 1
g = gen()
g.send(10)
——————————————————————————————————————————
#TypeError: can't send non-None value to a just-started generator
先预激活
def gen():
x = yield
yield x + 1
g = gen()
print(g.send(None))
print(g.send(10))
——————————————————————————————————————————
None
11
第一次使用send(None)进行启动生成器,执行到x=yield,由于yield右侧无表达式,因此产出的值为None,故而send(None)返回该None
使用next(g) 进行预激活也可,两者功能上是等价的。同时,next(g)会更常用于“启动/激活”。
推导公式
推导公式与生成器表达式整体语法结构相似,但不同于生成器,推导公式是一次性生成所有的数值并存储至内存中,故而相较于生成表达式,内存占据较大。推导公式主要分为列表推导公式、字典推导公式、集合推导公式
列表推导公式
语法:[表达式 for 变量 in 迭代器 if 条件]
返回值:一个完整的列表
m = [i for i in range(1, 11)]
print(m,type(m))
——————————————————————————————————————————————
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] <class 'list'>
字典推导公式
语法:{ 键表达式: 值表达式 for 变量 in 可迭代对象 if 条件 }
返回值:一个完整的字典
m = {i:i+1 for i in range(1,11) if i % 2 == 0}
print(m,type(m))
——————————————————————————————————————————————
{2: 3, 4: 5, 6: 7, 8: 9, 10: 11} <class 'dict'>
集合推导公式
语法:{表达式 for 变量 in 迭代器 if 条件}
返回值:一个完整的集合
m = {i for i in range(1,11) if i % 2 == 0}
print(m,type(m))
——————————————————————————————————————————————
{2, 4, 6, 8, 10} <class 'set'>