在 Python 中,生成器是一个函数,它返回一个迭代器,该迭代器在被迭代时会产生一个值序列。
当我们想要生成一个庞大的值序列,但又不想一次性将它们全部存储在内存中时,生成器就非常有用。
创建 Python 生成器
在 Python 中,与定义普通函数类似,我们可以使用 def
关键字来定义一个生成器函数,但我们使用 yield
语句而不是 return
语句。
def generator_name(arg):
# statements
yield something
这里,yield
关键字用于从生成器中产生一个值。
当生成器函数被调用时,它不会立即执行函数体。相反,它会返回一个生成器对象,可以通过迭代该对象来产生值。
示例:Python 生成器
这是一个生成器函数的示例,它会产生一个数字序列,
def my_generator(n):
# initialize counter
value = 0
# loop until counter is less than n
while value < n:
# produce the current value of the counter
yield value
# increment the counter
value += 1
# iterate over the generator object produced by my_generator
for value in my_generator(3):
# print each value produced by generator
print(value)
输出
0 1 2
在上面的示例中,my_generator()
生成器函数接受一个整数 n
作为参数,并使用 while 循环产生一个从 0 到 n-1
的数字序列。
yield
关键字用于从生成器中产生一个值,并暂停生成器函数的执行,直到请求下一个值为止。
for
循环遍历由 my_generator()
产生的生成器对象,而 print 语句则打印出生成器产生的每个值。
我们也可以像调用任何其他函数一样,通过调用生成器函数来创建一个生成器对象,如下所示:
generator = my_range(3)
print(next(generator)) # 0
print(next(generator)) # 1
print(next(generator)) # 2
注意:要了解更多信息,请访问 range() 和 for 循环()。
Python 生成器表达式
在 Python 中,生成器表达式是创建生成器对象的一种简洁方式。
它类似于列表推导式,但它创建的是一个生成器对象,而不是列表。这个生成器对象可以被迭代以产生生成器中的值。
生成器表达式语法
生成器表达式具有以下语法:
(expression for item in iterable)
这里,expression
是将为 iterable
中的每个项返回的值。
生成器表达式创建了一个生成器对象,该对象在被迭代时,会为 iterable
中的每个项一次一个地产生 expression
的值。
示例 2:Python 生成器表达式
# create the generator object
squares_generator = (i * i for i in range(5))
# iterate over the generator and print the values
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))))
# Output: 4895
这种管道化方式高效且易于阅读(而且,是的,酷多了!)。