3.1 认识生成器
生成器是一些返回迭代器对象的函数。这些迭代器在被请求时生成 值,这样可以处理潜在的大型数据集,而无需将它们完全加载到内存中。
有几种方式可以创建生成器,下面会介绍其中最流行的一些方法。
基于函数的生成器
生成器是通过在函数中使用关键字yield创建的。当调用包含yield的函数时,它返回一个生成器对象,但不立即执行函数内部的代码。相反,执行被暂停在yield表达式处,并在每次调用生成器对象的__next__()方法时恢复。
def count_up_to(max):
count = 1
while count <= max:
yield count
count += 1
counter = count_up_to(5)
print(next(counter)) # 输出: 1
print(next(counter)) # 输出: 2
print(next(counter)) # 输出: 3
print(next(counter)) # 输出: 4
print(next(counter)) # 输出: 5
如果函数中有yield语句,那么Python会创建一个管理函数执行状态的生成器对象,而不是传统的函数执行。
生成器表达式
生成器表达式类似于列表解析(List Comprehension), 但使用圆括号而不是方括号。它们同样返回生成器对象。
squares = (x ** 2 for x in range(10))
print(next(squares)) # 输出: 0
print(next(squares)) # 输出: 1
print(next(squares)) # 输出: 4
你更喜欢哪种方式?
3.2 生成器的优势
内存使用效率高
生成器按需计算值,这样可以处理大数据,而无需将它们完全加载到内存中。这使生成器成为处理大型数据集或数据流的理想选择。
def large_range(n):
for i in range(n):
yield i
for value in large_range(1000000):
# 逐个处理值
print(value)
惰性计算
生成器执行惰性计算,这意味着它们仅在需要时计算 值。这样可以避免不必要的计算并提高性能。
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
for _ in range(10):
print(next(fib))
简洁语法
生成器提供了简洁的语法用于创建迭代器,简化了代码的编写和阅读。
3.3 使用生成器
标准库中生成器的使用示例
Python标准库中的许多函数都使用生成器。 例如,函数range()返回一个生成器对象, 它生成一系列数字。
for i in range(10):
print(i)
是的,世界再也不会一样了。
创建无限序列
生成器允许创建无限序列,这在各种场景中可能有用,比如生成无限数据流。
def natural_numbers():
n = 1
while True:
yield n
n += 1
naturals = natural_numbers()
for _ in range(10):
print(next(naturals))
使用send()和close()
生成器对象支持send()和 close()方法,它们允许将值发送回 生成器并终止其执行。
def echo():
while True:
received = yield
print(received)
e = echo()
next(e) # 启动生成器
e.send("Hello, world!") # 输出: Hello, world!
e.close()
3.4 生成器在实践中
生成器与异常
生成器可以处理异常,这使得它们成为编写更稳健代码的强大工具。
def controlled_execution():
try:
yield "Start"
yield "Working"
except GeneratorExit:
print("Generator closed")
gen = controlled_execution()
print(next(gen)) # 输出: Start
print(next(gen)) # 输出: Working
gen.close() # 输出: Generator closed
我们将在后面的讲座中讨论异常处理,但我想你会发现生成器在这方面表现出色。
嵌套生成器
生成器可以嵌套,这允许创建复杂的迭代结构。
def generator1():
yield from range(3)
yield from "ABC"
for value in generator1():
print(value)
# 输出
0
1
2
A
B
C
解释:
yield from: 这种结构用于将部分操作委托给另一个生成器,简化代码并提高可读性。
生成器与性能
使用生成器可以显著提高程序的性能,因为它减少了内存的使用并更有效地执行迭代。
列表和生成器的比较示例
import time
import sys
def memory_usage(obj):
return sys.getsizeof(obj)
n = 10_000_000
# 使用列表
start_time = time.time()
list_comp = [x ** 2 for x in range(n)]
list_time = time.time() - start_time
list_memory = memory_usage(list_comp)
# 使用生成器
start_time = time.time()
gen_comp = (x ** 2 for x in range(n))
gen_result = sum(gen_comp) # 计算总和以便比较结果
gen_time = time.time() - start_time
gen_memory = memory_usage(gen_comp)
print(f"列表:")
print(f" 时间: {list_time:.2f} 秒")
print(f" 内存: {list_memory:,} 字节")
print(f"\n生成器:")
print(f" 时间: {gen_time:.2f} 秒")
print(f" 内存: {gen_memory:,} 字节")
列表:
时间: 0.62 秒
内存: 89,095,160 字节
生成器:
时间: 1.13 秒
内存: 200 字节
GO TO FULL VERSION