生成器
- 一种能够动态(循环一次计算一次返回一次)提供数据的可迭代对象。在循环过程中,按照某种算法推算数据,不必创建容器存储完整的结果,从而节省内存空间。数据量越大,优势越明显。以上作用也称之为延迟操作或惰性操作,通俗的讲就是在需要的时候才计算结果,而不是一次构建出所有结果。
生成器和迭代器
- 所有的生成器都是迭代器,因为生成器完全实现了迭代器接口;但两者也有所不同,迭代器用于从可迭代对象(如集合)中取出元素,而生成器 “凭空” 生成元素,但大多数情况下把生成器和迭代器视作同一概念。
生成器函数
- 只要 Python 函数的定义体中有 yield 关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象。也就是说,生成器函数是生成器工厂。
# 定义一个生成器函数
def gen_123():
yield 1
yield 2
yield 3
# 调用 gen_123 函数会返回一个生成器对象,而不执行
g = gen_123()
print(type(g)) # <class 'generator'>
for i in g:
print(i, end=" ") # 1 2 3
生成器函数执行过程
(1) 调用生成器函数会自动创建迭代器对象。
(2) 调用迭代器对象的__next__()方法时才执行生成器函数。
(3) 每次执行到yield语句时返回数据,暂时离开。
(4) 待下次调用__next__()方法时继续从离开处继续执行。
生成器创建迭代器对象的规则
– 将yield关键字以前的代码放在next方法中。
– 将yield关键字后面的数据作为next方法的返回值。
# 由于生成器完全实现了迭代器的接口,因此可以调用 next(g) 获取下一个元素
g = gen_123()
print(g.next) # 1
print(g.next) # 2
print(g.next) # 3
print(g.next) # 报错 StopIteration:
生成器函数“返回值” 和 return 返回值
- 与其说生成器函数的 “返回值” ,更准确的说应该是产出或生成值。调用生成器函数返回生成器,生成器产出或生成值。生成器不会以常规的方式“返回”值,或者说生成器函数每次 “返回值” 的时候都会说 “我还会回来的” ,直到函数定义体执行完毕。生成器函数定义体中的使用 return 语句会触发生成器对象抛出 StopIteration 异常。但生成器函数中的 return 不是必须的,生成器函数的定义体执行完毕后,生成器对象会抛出 StopIteration 异常从而结束程序。
另一个句法:yield from
- 如果生成器函数需要产出另一个生成器生成的值,传统的解决方法是使
用嵌套的 for 循环。如:
def get_item(*iterables):
for i in iterables:
for j in i:
yield j
a = ['A', 'B', 'C']
b = 'efg'
g = get_item(a, b)
for item in g:
print(item, end=" ")
>>> A B C e f g
- 但引入句法 yield from 可改为:
def get_item(*iterables):
for i in iterables:
yield from i
a = ['A', 'B', 'C']
b = 'efg'
g = get_item(a, b)
for item in g:
print(item, end=" ")
>>> A B C e f g
其中 yield from i 完全代替了内层的 for 循环。除了代替循环之外,yield from 还会创建通道,把内层生成器直接与外层生成器的客户端联系起来。把生成器当成协程使用时,这个通道不仅能为客户端代码生成值,还能使用客户端代码提供的值。
生成器表达式
1.定义:用推导式形式创建生成器对象。
- 生成器表达式背后遵守了迭代器协议,可以逐个地产出元素,而不是先建立一个完整的列表,然后再把这个列表传递到某个构造函数里。这样的方式显然能够节省内存。
2.语法:生成器表达式的语法跟列表推导差不多,只不过把方括号换成圆括号而
已。
变量 = (表达式 for 变量 in 可迭代对象 [if 真值表达式])
>>> colors = ['black', 'white']
>>> sizes = ['S', 'M', 'L']
>>> for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):
...
print(tshirt)
...
black S
black M
black L
white S
white M
white L