Python生成器

Python生成器

生成器generator 的定义

生成器指的是生成器对象,可以有生成器表达式得到,也可以使用yield关键字得到一个生成器函数,调用这个函数就可以得到一个生成器对象

生成器函数

  • 生成器函数就是函数体中包含yield语句的函数,返回生成器对象
  • 生成器对象,就是一个可迭代对象,是一个迭代器
  • 生成器对象,是延迟计算,惰性求值的
    例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
def inc():
for i in range(5):
yield i

print(type(inc))
print(type(inc()))
x = inc()
print((type(x)))
print(next(x))
for m in x:
print(m,'*')
for m in x:
print(m,'**')
<class 'function'>
<class 'generator'>
<class 'generator'>
0
1 *
2 *
3 *
4 *
1
2
3
4
y = (i for i in range(5))
print(type(y))
print(next(y))
print(next(y))
<class 'generator'>
0
1
  • 普通的函数调用fn(),函数会立即执行完毕,但是生成器函数可以使用next函数多次执行
  • 生成器函数等价于生成器表达式,只不过生成器函数可以更加的复杂
    举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def gen():
print('line 1')
yield 1
print('line 2')
yield 2
print('line 3')
return
next(gen()) # line 1
next(gen()) # line 1
g = gen()
print(next(g)) # line 1
print(next(g)) # line 2
# print(next(g)) # StopIteration
print(next(g,'End')) # 如果生成器对象中没有元素,就给个缺省值
line 1
line 1
line 1
1
line 2
2
line 3
End
  • 在生成器函数中,使用多个yield语句,执行一次后会暂停执行,把yield表达式的值返回
  • 再次执行会执行到下一个yield语句
  • return语句依然可以终止函数执行,但return语句的返回值不能被获取到
  • return会导致无法获取到下一个值,抛出StopIteration异常
  • 如果函数没有显示的return语句,如果生成器函数执行到结尾,一样会抛出StopIteration异常

生成器函数的执行步骤

  • 包含yield语句的生成器函数生成 生成器对象 时,生成器函数的函数体不会立即被执行
  • next(generator)会从函数的当前位置向后执行到之后碰到的第一个yoeld语句,会弹出yield的返回值,并暂停函数的执行
  • 再次调用next()函数,会执行两个yield之间的语句
  • 如果没有多余的yield语句可以被执行,继续调用next()函数,就会抛出StopIteration异常

生成器的应用场景

  1. 无限循环
1
2
3
4
5
6
7
8
9
10
11
12
def counter():
i = 0
while True:
i += 1
yield i

def inc(c):
return next(c)

c = counter()
print(inc(c))
print(inc(c))
1
2
1
2
3
4
5
6
7
8
9
10
11
12
def counter():
i = 0
while True:
i += 1
yield i
def inc():
c = counter()
return next(c)

print(inc())
print(inc())
print(inc())
1
1
1
  1. 计数器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def inc():
def counter():
i = 0
while True:
i += 1
yield 1
c = counter()
return lambda:next(c)
# lambda表达式是一个匿名函数
# return返回的是一个匿名函数

foo = inc()
print(foo())
print(foo())
1
1
  1. 处理递归问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def fib():
x = 0
y = 1
while True:
yield y
x,y = y,x+y

foo = fib()
for _ in range(5):
print(next(foo))

for _ in range(10):
next(foo)
print(next(foo))
1
1
2
3
5
987

协程coroutine

  • 协程是生成器的高级用法
  • 比进程、线程更加轻量级
  • 是在用户空间调度函数的一种实现
  • Python3 的 asyncio 就是协程实现的,已经加入到标准库了
  • Python3.5 使用 async、await关键字直接原生支持协程
  • 协程调度器的实现思路

    1. 有两个生成器A、B
    2. next(A)后,A执行到了yield语句后暂停,然后执行next(B),B执行到yield语句后也暂停,然后再次调用next(A),在调用next(B),周而复始,就实现了调度器的效果了
  • 协程是一种非抢占式的调度

yield from

举例说明

1
2
3
4
5
6
7
def inc():
for x in range(1000):
yield x
foo = inc()
print(next(foo))
print(next(foo))
print(next(foo))
0
1
2

等价于下面的代码

1
2
3
4
5
6
7
def inc():
yield from range(1000)

foo = inc()
print(next(foo))
print(next(foo))
print(next(foo))
0
1
2
  • yield from 是Python 3.3 才出现的新语法
  • yield from iterable 是 for item in iterable:tield item 形式的语法糖
  • 可从可迭代对象中一个个获取元素
    例如:
1
2
3
4
5
6
7
8
9
10
11
def counter(n):  # 生成器,迭代器
for x in range(n):
yield x

def inc(n):
yield from counter(n)

foo = inc(10)
print(next(foo))
print(next(foo))
print(next(foo))
0
1
2
-------------本文结束感谢您的阅读-------------
0%