生成器对象、生成器函数、生成器表达式和推导公式

8 阅读4分钟

生成器对象(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'>