生成器

Python SELF ZH
第 13 级 , 课程 4
可用

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 字节
1
调查/小测验
系统函数第 13 级,课程 4
不可用
系统函数
系统函数
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION