Python 生成器
- 生成器是内存高效的迭代器:在Python中,生成器是一种特殊的函数,用于按需产生序列值,避免一次性占用大量内存。
- 使用
yield
关键字:生成器通过yield
语句返回值,它使函数暂停执行并在下次迭代时恢复,允许逐个产生值。 - 生成器表达式简洁实用:生成器表达式提供了一种简洁的方式来创建生成器对象,类似于列表推导式,但不会立即产生整个序列。
在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
作为参数,并产生从0到n-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
这里,我们创建了一个生成器对象,当迭代时,它将产生数字0到4的平方。
然后,为了迭代生成器并获取值,我们使用了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
这种流水线化既高效又易于阅读(而且是,更酷!)。