Python中的生成器
本文介绍了Python中的生成器。
YouTube Video
Python中的生成器
概述
Python的生成器是一种迭代器,是高效执行重复处理的强大功能。它们可以让你在处理大量数据时编写内存高效的代码。
什么是生成器?
Python中的生成器是一种特殊的函数,使用 yield 关键字定义,每次产生一个值。其特点是在保留当前状态的同时暂停执行,之后可以继续执行。
yield 的基础用法
yield 是一个关键字,返回一个值的同时暂停函数的执行。
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6gen = simple_generator()
7
8print(next(gen)) # 1
9print(next(gen)) # 2
10print(next(gen)) # 3- 调用该函数时会返回一个生成器对象,该对象会依次生成值。
- 如果在没有下一个值时调用
next(),会发生StopIteration错误。
next() 和 StopIteration
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6gen = simple_generator()
7
8try:
9 while True:
10 value = next(gen)
11 print(value)
12except StopIteration:
13 print("Finished")- 通过像这样显式地处理
StopIteration错误,你可以检测生成器何时结束。
send(value)
send(value) 的调用会恢复生成器的执行,并将 value 发送到 yield 表达式的位置。发送的值在生成器端可以作为 yield 表达式的返回值接收。第一次调用时,send(value) 不能发送除 None 以外的任何值,因此必须使用 next() 或 send(None)。
1def gen():
2 x = yield 1
3 print(f"x = {x}")
4 y = yield 2
5 print(f"y = {y}")
6
7g = gen()
8print(next(g)) # -> 1 (value from yield 1)
9print(g.send(10)) # -> x = 10, 2 (value from yield 2)
10print(g.send(20)) # -> y = 20, StopIteration occurs- 使用
send(10)时,生成器中的yield变成返回 10 的表达式,10 会被赋值给x。
throw()
调用 throw 会恢复生成器的执行,并在暂停的 yield 位置抛出异常。你可以在生成器内部处理异常以继续进行处理。如果异常没有被捕获,会向外传播并终止生成器。
1def gen():
2 try:
3 yield 1
4 except ValueError as e:
5 print(f"Caught: {e}")
6 yield "recovered"
7
8g = gen()
9print(next(g)) # -> 1
10print(g.throw(ValueError("boom"))) # -> Caught: boom, "recovered"- 在此代码中,通过调用
throw向生成器注入异常。在生成器端,异常被处理并返回recovered。
close()
调用 close() 会终止生成器。在生成器内部,你可以使用 finally 进行清理操作。在调用 close() 之后,再调用 next() 或 send() 会引发 StopIteration 错误。
1def gen():
2 try:
3 yield 1
4 finally:
5 print("Cleaning up...")
6
7g = gen()
8print(next(g)) # -> 1
9g.close() # -> Cleaning up...- 此代码显示,调用
close()会终止生成器并触发finally中的清理过程。
yield from
yield from 是用于委托给子生成器的语法。这是一种在生成器内部调用另一个生成器,并将其所有值传递到外部作用域的简单方法。
1def sub_gen():
2 yield 1
3 yield 2
4
5def main_gen():
6 yield from sub_gen()
7 yield 3
8
9print(list(main_gen())) # -> [1, 2, 3]- 这段代码使用
yield from将子生成器的所有值委托给外部生成器,然后再生成3。
与迭代器的关系
生成器内部实现了 __iter__() 和 __next__(),因此它们是迭代器的一种。因此,它们完全兼容如 for 循环等可迭代操作。
与 for 循环的结合
在Python中,for 循环内部会自动使用 next() 来获取值。
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6for value in simple_generator():
7 print(value)使用这种方式,StopIteration 的处理也是自动完成的。
创建无限生成器
1def count_up(start=0):
2 while True:
3 yield start
4 start += 1
5
6counter = count_up()
7print(next(counter)) # 0
8print(next(counter)) # 1可以创建无限循环,但在使用时需要注意。
生成器表达式
生成器表达式使用圆括号编写,可以用类似列表推导式的语法定义生成器。
1# List comprehension (generates the entire list at once)
2squares_list = [x**2 for x in range(5)]
3print(squares_list)
4
5# Generator expression
6squares_gen = (x**2 for x in range(5))
7for square in squares_gen:
8 print(square)与列表推导式不同,它们不会一次性将所有元素加载到内存,因此更加节省内存。
生成器中的错误处理
生成器内部可能会发生异常。在这种情况下,你可以像普通的Python代码一样使用 try-except。
1def safe_divide_generator(numbers, divisor):
2 """Yields results of dividing numbers by a given divisor safely."""
3 for number in numbers:
4 try:
5 yield number / divisor # Attempt to divide and yield result.
6 except ZeroDivisionError:
7 yield float('inf') # Return infinity if division by zero occurs.
8
9# Example usage
10numbers = [10, 20, 30]
11gen = safe_divide_generator(numbers, 0) # Create generator with divisor as 0.
12for value in gen:
13 print(value) # Output: inf, inf, inf在这个例子中,对除零错误进行了适当的异常处理。
生成器的堆栈跟踪
如果生成器内部发生异常,则在生成器恢复时会抛出该异常。
1def error_generator():
2 """A generator that yields values and raises an error."""
3 yield 1
4 raise ValueError("An error occurred") # Raise a ValueError intentionally.
5 yield 2
6
7gen = error_generator()
8print(next(gen)) # Output: 1 (first value yielded)
9try:
10 print(next(gen)) # Attempt to get the next value, which raises an error
11except ValueError as e:
12 print(e) # Output: An error occurred (exception message is printed)- 该生成器首先返回
1。恢复时抛出的错误会被捕获,并显示为错误信息。
生成器的使用示例
逐行读取文件(适合大型文件)
1def read_large_file(filepath):
2 with open(filepath, 'r') as f:
3 for line in f:
4 yield line.strip()- 该函数通过迭代器逐行读取文本文件,去除每行的空白字符,并作为生成器返回,从而可以低内存消耗地处理大文件。
斐波那契数列生成器
1def fibonacci(limit):
2 a, b = 0, 1
3 while a < limit:
4 yield a
5 a, b = b, a + b
6
7for n in fibonacci(100):
8 print(n)- 该代码使用生成器顺序生成小于上限的斐波那契数,并用
for循环输出。
应用场景
生成器还可以用于以下场景。
- 对大型CSV或日志文件进行顺序处理
- API分页处理
- 处理流式数据(如Kafka、物联网设备)
总结
| 概念 | 关键点 |
|---|---|
yield |
暂停并返回一个值 |
| 生成器函数 | 包含 yield 的函数,调用时返回一个迭代器 |
| 优点 | 节省内存,非常适合处理大型数据集 |
| 生成器表达式 | 允许像 (x for x in iterable) 这样的简洁语法 |
通过使用生成器,你可以高效地处理大型数据集,同时节省内存并保持代码简洁。
您可以在我们的YouTube频道上使用Visual Studio Code跟随上述文章进行学习。 请也查看我们的YouTube频道。