生成器的概念
在 Python 中,生成器是一种特殊的迭代器,可以通过 yield
关键字实现。生成器一次产生一个值,并保留当前状态,以供下次调用时恢复使用。
生成器在某些场景下比列表更有效率,因为它们按需生成值,而不是一次性生成所有值,从而节省内存空间。
yield关键字
关键字 yield
在 Python 中用于定义生成器。它的作用与 return
类似,但不是终止函数或方法,而是暂停函数或方法的执行,并返回一个中间结果。
当生成器被调用时,yield
会将产生的值返回给调用方,并把当前状态保存下来。下次调用时,生成器会从上一次离开的位置继续执行。
使用yield创建生成器
使用 yield
关键字创建生成器非常简单。我们来看一个例子:
def my_generator():
yield 1
yield 2
yield 3
在上面的代码中,我们定义了一个名为 my_generator
的函数,使用 yield
关键字创建了一个生成器。它会依次生成值 1、2 和 3。
下面是如何使用这个生成器:
gen = my_generator()
for value in gen:
print(value)
上面的代码会输出:
1
2
3
生成器表达式
除了使用函数与 yield
创建生成器,还可以使用生成器表达式来快速创建生成器。生成器表达式与列表推导式类似,但它们返回一个生成器,而不是一个列表。
下面是一个例子:
gen = (i for i in range(10))
for value in gen:
print(value)
上面的代码会输出:
0
1
2
3
4
5
6
7
8
9
在函数中使用yield from语法
Python 3.3 引入了 yield from
语法,用于在生成器内部调用另一个生成器。使用 yield from
可以简化代码并提高可读性。
下面是一个使用 yield from
的示例:
def sub_generator():
yield 'foo'
yield 'bar'
def my_generator():
yield from sub_generator()
yield 'spam'
gen = my_generator()
for value in gen:
print(value)
上面的代码会输出:
foo
bar
spam
在上面的代码中,我们定义了一个名为 sub_generator
的生成器,它会依次生成值 'foo' 和 'bar'。
然后我们在 my_generator
中使用 yield from
调用 sub_generator
并生成它的返回值。最终生成器会依次生成值 'foo'、'bar' 和 'spam'。
生成器方法
Python 提供了一些方法帮助我们操作生成器。下面是一些常用的生成器方法:
next()
next()
是生成器对象的一个方法,用于从生成器中获取下一个值。当生成器没有值可供生成时,next()
会引发 StopIteration
异常。
下面是一个使用 next()
的例子:
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator()
print(next(gen)) # 输出 1
print(next(gen)) # 输出 2
print(next(gen)) # 输出 3
print(next(gen)) # 引发 StopIteration 异常
send()
除了使用 next()
获取下一个值外,还可以使用 send()
方法向生成器中发送一个值。这个值会成为生成器中当前 yield
表达式的结果。
下面是一个使用 send()
的例子:
def my_generator():
x = yield
print('Received:', x)
gen = my_generator()
next(gen) # 必须在第一次 send() 之前调用 next()
gen.send('Hello, world!') # 输出 Received: Hello, world!
throw()
throw()
方法用于向生成器中抛出一个异常。这会在当前的 yield
表达式处引发一个异常。
下面是一个使用 throw()
的例子:
def my_generator():
try:
while True:
x = yield
print('Received:', x)
except ValueError:
print('Got ValueError')
gen = my_generator()
next(gen) # 必须在第一次 throw() 之前调用 next()
gen.throw(ValueError) # 输出 Got ValueError
close()
使用 close()
方法可以关闭生成器。这会抛出一个 GeneratorExit
异常,以便让生成器有机会清理资源。
下面是一个使用 close()
的例子:
def my_generator():
try:
while True:
x = yield
print('Received:', x)
except GeneratorExit:
print('Closed')
gen = my_generator()
next(gen) # 必须在第一次 close() 之前调用 next()
gen.close() # 输出 Closed
使用生成器优化代码
在某些场景下,使用生成器可以有效地优化代码并降低内存使用。下面是一些使用生成器的常见用例:
无限序列
生成器非常适合生成无限序列,并且它们不需要占用大量内存空间。
下面是一个例子,生成器会生成斐波那契数列:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
gen = fibonacci()
for i in range(10):
print(next(gen))
上面的代码会输出:
0
1
1
2
3
5
8
13
21
34
流式处理数据
生成器非常适合流式处理数据,因为它们可以每次处理一个元素而不必将整个数据集都保存在内存中。
下面是一个例子,每次从文件中读取一行:
def read_file(filename):
with open(filename, 'r') as f:
for line in f:
yield line
gen = read_file('data.txt')
for line in gen:
print(line)
过滤数据
生成器非常适合从序列中过滤数据,并且它们可以根据需要生成结果。
下面是一个例子,生成器会过滤掉所有偶数:
def odd_numbers(numbers):
for number in numbers:
if number % 2 == 1:
yield number
gen = odd_numbers([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
for number in gen:
print(number)
上面的代码会输出:
1
3
5
7
9
总结
生成器是 Python 中一种特殊的迭代器,可以通过 yield
关键字实现。使用生成器可以在某些场景下优化代码并降低内存使用。Python 还提供了一些方法帮助我们操作生成器,如 next()
、send()
、throw()
和 close()
。
如果你想从一个序列中过滤数据、流式处理数据,或者生成无限序列,那么生成器就是一个非常好的选择。