跳到主要内容

Python 生成器

提示
  1. 生成器是内存高效的迭代器:在Python中,生成器是一种特殊的函数,用于按需产生序列值,避免一次性占用大量内存。
  2. 使用yield关键字:生成器通过yield语句返回值,它使函数暂停执行并在下次迭代时恢复,允许逐个产生值。
  3. 生成器表达式简洁实用:生成器表达式提供了一种简洁的方式来创建生成器对象,类似于列表推导式,但不会立即产生整个序列。

在Python中,生成器是一种函数,它返回一个迭代器,在迭代时产生一系列的值。

当我们想生成大量序列值,但又不想一次性将它们全部存储在内存中时,生成器就非常有用。

创建Python生成器

在Python中,我们可以使用def关键字来定义生成器函数,类似于定义普通函数,但使用yield语句而不是return语句。

def generator_name(arg):
# 语句
yield something

这里,yield关键字用于从生成器产生一个值。

当调用生成器函数时,它不会立即执行函数体。相反,它返回一个可以被迭代以产生值的生成器对象。

示例:Python生成器

下面是一个生成器函数的例子,它产生一系列数字,

def my_generator(n):

# 初始化计数器
value = 0

# 循环直到计数器小于n
while value < n:

# 产生当前计数器的值
yield value

# 增加计数器
value += 1

# 迭代由my_generator产生的生成器对象
for value in my_generator(3):

# 打印生成器产生的每个值
print(value)

输出

0
1
2

在上面的例子中,my_generator()生成器函数接受一个整数n作为参数,并产生从0n-1的一系列数字。

yield关键字用于从生成器产生一个值,并暂停生成器函数的执行,直到请求下一个值。

for循环迭代由my_generator()产生的生成器对象,print语句打印生成器产生的每个值。

我们也可以通过调用函数来从生成器函数创建一个生成器对象,就像调用任何其他函数一样,

generator = my_range(3)
print(next(generator)) # 0
print(next(generator)) # 1
print(next(generator)) # 2

Python生成器表达式

在Python中,生成器表达式是创建生成器对象的一种简洁方式。

它类似于列表推导式,但不是创建列表,而是创建一个可以被迭代以产生生成器中值的生成器对象。

生成器表达式语法

生成器表达式具有以下语法,

(expression for item in iterable)

这里,expression是对于iterable中每个项目将被返回的值。

生成器表达式创建一个生成器对象,当迭代时,它为iterable中的每个项目产生expression的值。

示例2:Python生成器表达式

# 创建生成器对象
squares_generator = (i * i for i in range(5))

# 迭代生成器并打印值
for i in squares_generator:
print(i)

输出

0
1
4
9
16

这里,我们创建了一个生成器对象,当迭代时,它将产生数字04的平方。

然后,为了迭代生成器并获取值,我们使用了for循环。

Python生成器的使用

生成器作为一种强大的实现,有几个原因。

1. 易于实现

与它们的迭代器类相比,生成器可以以清晰简洁的方式实现。以下是使用迭代器类实现2的幂序列的示例。

class PowTwo:
def __init__(self, max=0):
self.n = 0
self.max = max

def __iter__(self):
return self

def __next__(self):
if self.n > self.max:
raise StopIteration

result = 2 ** self.n
self.n += 1
return result

上面的程序既长又复杂。现在,让我们使用生成器函数来做同样的事情。

def PowTwoGen(max=0):
n = 0
while n < max:
yield 2 ** n
n += 1

由于生成器自动跟踪细节,实现更加简洁明了。

2. 内存高效

返回序列的普通函数会在返回结果之前在内存中创建整个序列。如果序列中的项数非常多,这将是一种浪费。

实现这种序列的生成器在内存方面更友好,它一次只产生一个项目,因此更受青睐。

3. 表示无限流

生成器是表示无限数据流的绝佳媒介。无限流不能存储在内存中,由于生成器一次只产生一个项目,它们可以表示无限数据流。

以下生成器函数可以生成所有偶数(至少在理论上)。

def all_even():
n = 0
while True:
yield n
n += 2

4. 生成器流水线

可以使用多个生成器来流水线处理一系列操作。这最好用一个例子来说明。

假设我们有一个生成斐波那契数列中的数字的生成器。我们还有一个平方数字的生成器。

如果我们想找出斐波那契数列中数字的平方和,我们可以通过将生成器函数的输出流水线化来以以下方式做到。

如果我们想要计算斐波那契数列中数字的平方和,我们可以通过将生成器函数的输出进行管道连接的方式来实现。

def fibonacci_numbers(nums):
x, y = 0, 1
for _ in range(nums):
x, y = y, x+y
yield x

def square(nums):
for num in nums:
yield num**2

print(sum(square(fibonacci_numbers(10))))

# 输出: 4895

这种流水线化既高效又易于阅读(而且是,更酷!)。