Python 中的生成器函数是一种特殊类型的函数,可用于创建迭代器。它们根据需要即时生成一系列值,而不是像常规函数那样一次性返回一个值。这使得它们比其他生成迭代器的方法更省内存且更快。
生成器的简介
使用生成器函数的好处包括提高性能效率、更好地管理内存以及处理大型和无限数据集的能力。
示例 1:常规 Python 函数
def power(n):
result = []
for i in range(n):
result.append(2**i)
return result
print(power(5)) # Output: [1, 2, 4, 8, 16]
示例 2:Python 生成器函数
def power(n):
for i in range(n):
yield 2**i
print(list(power(5))) # Output: [1, 2, 4, 8, 16]
在第二个示例中,生成器函数用于创建一个迭代器,该迭代器根据需要即时生成每个值,而不是像第一个示例那样在内存中创建和存储值列表。对于可能不需要一次性存储在内存中的大型数据集或计算,这可能是一种更高效的工作方式。
生成器函数的语法和结构
它使用 yield
关键字返回一个值并暂时挂起函数的执行。Python 中生成器函数的语法与常规函数类似,但增加了 yield
语句。
Python 中生成器函数的语法
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
在此示例中,生成器函数 count_up_to()
生成一系列数字,从 1
到给定的值 n
。调用时,它返回一个生成器对象,可以对其进行迭代以获取序列中的下一个
值。
生成器函数的另一个示例是 string_generator()
函数,它将一个字符串作为输入,并使用 yield 语句一次返回字符串的每个字符。
def string_generator(string):
for char in string:
yield char
生成器函数 string_generator()
创建一个新的生成器对象,该对象从输入字符串中一次生成一个字符。yield 语句用于暂时暂停函数的执行并返回当前字符,然后再恢复执行。
理解生成器函数中的 yield 语句
Python 中的生成器函数是一种特殊类型的 Python 函数,它可以返回一个迭代器对象。这些迭代器对象可用于即时生成一系列值,而不是一次性计算所有值并将其存储在列表中。yield 语句是生成器函数的关键部分,它允许函数生成一个值并暂时暂停其执行。
示例 1:Python 中的简单生成器函数
def simple_generator():
yield 'Hello'
yield 'World'
yield '!'
在这个示例中,simple_generator()
函数有三个 yield 语句,它们将生成三个值:Hello
、World
和 !
。当调用该函数时,它不会立即执行其代码;相反,它返回一个迭代器对象。每次调用迭代器的 __next__()
方法时,该函数都会执行直到遇到 yield
语句。在这一点上,该函数将暂停其执行并将值返回给调用者。下次调用迭代器的 __next__()
方法时,该函数将从中断处恢复执行并继续执行,直到到达下一个 yield
语句或函数的末尾。
示例 2:Python 中带参数的生成器函数
def even_numbers(maximum):
i = 0
while i < maximum:
if i % 2 == 0:
yield i
i += 1
在这个示例中,even_numbers()
生成器函数采用一个 maximum
参数,指示要生成的偶数的最大数量。该函数使用 while
循环从 0 迭代到 maximum
,并使用 if
语句检查当前数字是否为偶数。如果该数字为偶数,则该函数会生成该值。该函数将继续生成偶数,直到达到 maximum
限制,或直到不再调用迭代器的 __next__()
方法。
总体而言,Python 中的生成器 函数 是一个强大的工具,可用于动态生成值序列,这可以节省计算内存,并比生成大型数据序列的传统方法提供更好的性能。
Python 中生成器与常规函数之间的差异
Python 中的生成器函数是一种特殊类型的函数,它允许我们返回一个迭代器对象。生成器函数返回一个可以迭代的生成器对象。另一方面,常规函数返回一个值然后退出。
以下是 Python 函数和生成器函数之间的一些差异
-
执行:常规 Python 函数运行直到到达末尾或 return 语句。另一方面,生成器函数生成一个值,然后进入挂起状态,直到请求另一个值。
-
内存使用:常规函数可以返回一个大型输出,这会消耗大量内存。相比之下,生成器函数使用最少的内存,因为它们在需要时惰性计算值。
以下是一个常规 Python 函数的示例
def square_numbers(nums):
result = []
for i in nums:
result.append(i * i)
return result
此函数将数字列表作为输入,并返回其平方列表。
以下是一个 Python 中的生成器函数示例
def square_numbers(nums):
for i in nums:
yield i * i
此生成器函数也采用数字列表作为输入,并生成它们的平方作为输出。
总之,虽然常规 Python 函数用于返回一个值然后退出,但生成器函数旨在生成一个可以迭代的值序列。
生成器函数的常见用例
Python 中生成器函数的常见用例包括
-
解析大型文件或数据集 - 生成器函数可用于一次读取文件或数据集的块,而不是一次将整个文件加载到内存中。
-
生成无限序列 - 生成器函数可用于生成无限的数字序列,例如斐波那契数列,而无需程序员创建大型列表或 数组。
示例:分块读取大型文件的函数
def read_chunks(file_path, chunk_size=1024):
with open(file_path, "r") as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
read_chunks()
函数以 chunk_size
大小的块读取文件,并生成每个块,直到到达文件末尾。这允许程序员处理大型文件,而无需将整个文件加载到内存中。
使用生成器函数的高级技术
通过利用下面讨论的高级技术,您可以在代码中操作和优化生成器函数的输出。
惰性执行
生成器函数的主要优点之一是能够延迟执行,直到实际需要输出为止。这可以通过避免生成和将所有输出存储在内存中来显著提高代码的性能。
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
gen = fibonacci(10) # Does not execute any code.
for num in gen:
print(num) # Executes code as needed.
使用生成器的多线程
您甚至可以将生成器与线程结合起来异步执行代码,允许多个进程同时执行,进一步提高代码的性能。
from threading import Thread
import time
def countdown(num):
print(f"Starting countdown for {num}")
for i in range(num, 0, -1):
print(i)
time.sleep(1)
def generate_counts():
for i in range(5, 0, -1):
yield Thread(target=countdown, args=(i,))
threads = list(generate_counts())
for thread in threads:
thread.start()
for thread in threads:
thread.join()
在这个示例中,我们创建了一个生成器函数,该函数使用 Python 中的 Thread
模块创建多个线程。countdown
函数在每个生成的线程内执行,从指定的值异步倒计时。通过同时利用生成器函数和线程,我们可以创建更有效和高性能的代码,同时利用多个处理器。
编写高效且有效的生成器函数的最佳实践和提示
-
生成大量数据序列时,使用生成器函数而不是列表解析或循环,这是因为生成器函数会按需生成值,而列表解析或循环会在返回之前在内存中创建整个序列。
-
在生成器函数中生成值时,使用
yield
关键字而不是return
。这允许函数暂停执行并返回一个值,而无需终止函数。然后可以从函数先前停止的位置恢复函数。 -
使用
next()
函数 来推进生成器函数生成的序列。此函数检索函数生成的下一个值,并向前移动函数的执行状态。 -
使用
send()
函数 将值送回生成器函数并恢复其执行。此函数允许客户端代码将值传递到生成器函数中,然后生成器函数可以使用这些值生成新值。
示例:生成几何序列中值的生成器函数
def geometric_sequence(start, factor):
value = start
while True:
yield value
value *= factor
# Usage:
g = geometric_sequence(2, 3)
print(next(g)) # Prints 2
print(next(g)) # Prints 6
print(next(g)) # Prints 18
print(next(g)) # Prints 54
print(next(g)) # Prints 162
# ...
在示例中,生成器函数生成一个无限值序列。但是,yield
关键字允许函数按需生成值,并且客户端代码可以一次使用一个值,而无需将整个序列存储在内存中。