Python 中的生成器是一种方便地创建迭代器的方法。它们允许我们遍历一系列值,这意味着值是在需要时生成而不是存储在内存中,这对于大数据集或无限序列尤其有用。
Python 中的生成器是一种特殊类型的函数,返回一个迭代器对象。它的定义看起来类似于普通的 Python 函数,同样以 def
关键字开头。然而,与在末尾使用 return
语句不同,生成器使用 yield
关键字。
语法
以下是生成器函数的语法:
def generator():
...
...
yield obj
it = generator()
next(it)
...
创建生成器
在 Python 中创建生成器主要有两种方式:
使用生成器函数
生成器函数使用 yield
语句一次性返回值。每当调用生成器的 __next__()
方法时,生成器就会从上次 yield
语句之后的位置恢复执行。下面是一个创建生成器函数的例子。
def count_up_to(max_value):
current = 1
while current <= max_value:
yield current
current += 1
counter = count_up_to(5)
for number in counter:
print(number)
输出:
1
2
3
4
5
使用生成器表达式
生成器表达式提供了一种紧凑的方式创建生成器。它们使用与列表推导类似的语法,但是使用圆括号 ()
而不是方括号 []
。
gen_expr = (x * x for x in range(1, 6))
for value in gen_expr:
print(value)
输出:
1
4
9
16
25
生成器中的异常处理
我们可以创建一个生成器,并使用带有 StopIteration
异常处理的 while
循环来迭代它。下面代码中的函数是一个生成器,它依次生成从 1 到 5 的整数。
当这个函数被调用时,它返回一个迭代器。对 next()
方法的每次调用都会将控制权传递回生成器并获取下一个整数。
def generator(num):
for x in range(1, num+1):
yield x
return
it = generator(5)
while True:
try:
print(next(it))
except StopIteration:
break
输出:
1
2
3
4
5
普通函数 vs 生成器函数
Python 中的普通函数和生成器函数具有不同的用途并且表现出不同的行为。理解它们之间的差异对于有效地利用它们非常重要。
普通函数计算并返回一个单一值或一组值(无论是在列表还是元组中),而一旦返回后,函数的执行就完成,并且所有局部变量都被丢弃;而生成器函数通过在每次 yield
之间暂停和恢复状态来逐个生成值。它使用 yield
语句而不是 return
。
示例
在这个例子中,我们创建了一个普通函数来生成斐波那契数列,然后使用循环遍历这个列表。
def fibonacci(n):
fibo = []
a, b = 0, 1
while True:
c = a + b
if c >= n:
break
fibo.append(c)
a, b = b, c
return fibo
f = fibonacci(10)
for i in f:
print(i)
输出:
1
2
3
5
8
示例
在上面的例子中,我们使用普通函数创建了斐波那契数列。当我们想要在一个列表中收集所有的斐波那契数,然后使用循环遍历这个列表时,想象一下我们想要生成到一个大数为止的斐波那契数列。
在这种情况下,所有数字都必须收集在列表中,这需要大量的内存。这时生成器就很有用了,因为它一次只生成列表中的一个数字,并提供给使用者消费。下面的代码是基于生成器的斐波那契数列解决方案。
def fibonacci(n):
a, b = 0, 1
while True:
c = a + b
if c >= n:
break
yield c
a, b = b, c
return
f = fibonacci(10)
while True:
try:
print(next(f))
except StopIteration:
break
输出:
1
2
3
5
8
异步生成器
异步生成器是一个协程,它返回一个异步迭代器。协程是一个使用 async
关键字定义的 Python 函数,并可以调度和等待其他协程和任务。
就像普通的生成器一样,异步生成器在每次调用 anext()
函数而不是 next()
函数时,在迭代器中生成增量项。
语法
以下是异步生成器的语法:
async def generator():
...
...
yield obj
it = generator()
anext(it)
...
示例
下面的代码演示了一个在每次 async for
循环迭代时生成递增整数的协程生成器。
import asyncio
async def async_generator(x):
for i in range(1, x+1):
await asyncio.sleep(1)
yield i
async def main():
async for item in async_generator(5):
print(item)
asyncio.run(main())
输出:
1
2
3
4
5
示例
现在让我们编写一个用于斐波那契数的异步生成器。为了模拟协程内的某个异步任务,程序在生成下一个数字之前调用 sleep()
方法 1 秒钟。结果是,我们将每隔一秒看到屏幕上打印出的数字。
import asyncio
async def fibonacci(n):
a, b = 0, 1
while True:
c = a + b
if c >= n:
break
await asyncio.sleep(1)
yield c
a, b = b, c
return
async def main():
f = fibonacci(10)
async for num in f:
print(num)
asyncio.run(main())
输出:
1
2
3
5
8