简介
在这个实验中,你将学习如何识别 Python 中的生成器函数,并理解常规函数和生成器之间的基本区别。掌握这些知识对于编写高效且节省内存的代码至关重要,尤其是在处理大型数据集或无限序列时。
本实验将引导你区分函数和生成器,使用 inspect.isgeneratorfunction
检查它们的类型,并测试是否存在 yield
关键字。你将创建一个 Python 脚本来说明这些差异,观察函数如何返回完整结果,而生成器如何迭代地生成值,并根据需要暂停和恢复执行。
在这个实验中,你将学习如何识别 Python 中的生成器函数,并理解常规函数和生成器之间的基本区别。掌握这些知识对于编写高效且节省内存的代码至关重要,尤其是在处理大型数据集或无限序列时。
本实验将引导你区分函数和生成器,使用 inspect.isgeneratorfunction
检查它们的类型,并测试是否存在 yield
关键字。你将创建一个 Python 脚本来说明这些差异,观察函数如何返回完整结果,而生成器如何迭代地生成值,并根据需要暂停和恢复执行。
在这一步中,你将学习 Python 常规函数和生成器之间的关键区别。理解这种区别对于编写高效且节省内存的代码至关重要,尤其是在处理大型数据集或无限序列时。
函数:
函数是执行特定任务的代码块。调用函数时,它会执行其代码,可能会进行计算,并返回一个值(如果没有显式的 return
语句,则返回 None
)。函数的状态在两次调用之间不会保留。
生成器:
生成器是一种特殊类型的函数,它使用 yield
关键字而非 return
。调用生成器时,它会返回一个迭代器对象。每次你从迭代器请求一个值时,生成器会一直执行,直到遇到 yield
语句。然后,生成器会暂停,保存其状态,并生成该值。下次请求值时,生成器会从上次暂停的位置继续执行。
让我们通过一个示例来说明这一点。首先,使用 VS Code 编辑器在你的 ~/project
目录中创建一个名为 function_vs_generator.py
的文件。
## ~/project/function_vs_generator.py
## 常规函数
def square_numbers_function(numbers):
result = []
for number in numbers:
result.append(number * number)
return result
## 生成器函数
def square_numbers_generator(numbers):
for number in numbers:
yield number * number
## 示例用法
numbers = [1, 2, 3, 4, 5]
## 使用函数
function_result = square_numbers_function(numbers)
print("Function Result:", function_result)
## 使用生成器
generator_result = square_numbers_generator(numbers)
print("Generator Result:", list(generator_result)) ## 将生成器转换为列表以便打印
现在,执行 Python 脚本:
python ~/project/function_vs_generator.py
你应该会看到以下输出:
Function Result: [1, 4, 9, 16, 25]
Generator Result: [1, 4, 9, 16, 25]
函数和生成器产生的结果相同。然而,关键区别在于它们实现这一结果的方式。函数会计算所有的平方值,并将它们存储在一个列表中,然后再返回。而生成器则是在每次请求时逐个生成平方值。
为了进一步说明这种区别,让我们修改脚本以打印返回对象的类型:
## ~/project/function_vs_generator.py
## 常规函数
def square_numbers_function(numbers):
result = []
for number in numbers:
result.append(number * number)
return result
## 生成器函数
def square_numbers_generator(numbers):
for number in numbers:
yield number * number
## 示例用法
numbers = [1, 2, 3, 4, 5]
## 使用函数
function_result = square_numbers_function(numbers)
print("Function Result Type:", type(function_result))
## 使用生成器
generator_result = square_numbers_generator(numbers)
print("Generator Result Type:", type(generator_result))
再次执行脚本:
python ~/project/function_vs_generator.py
输出将是:
Function Result Type: <class 'list'>
Generator Result Type: <class 'generator'>
这清楚地表明,函数返回一个 list
,而生成器返回一个 generator
对象。生成器具有内存效率,因为它们不会一次性将所有值存储在内存中,而是按需生成值。
inspect.isgeneratorfunction
检查类型在上一步中,你学习了函数和生成器之间的基本区别。现在,让我们探讨如何使用 inspect.isgeneratorfunction()
方法以编程方式确定一个函数是否为生成器函数。当你处理的代码中可能不清楚某个函数的实现细节时,这尤其有用。
Python 中的 inspect
模块提供了用于内省(即检查函数、类、模块等对象的内部特征)的工具。inspect.isgeneratorfunction()
方法专门用于检查给定对象是否为生成器函数。
让我们修改 ~/project
目录下的 function_vs_generator.py
文件,以包含此检查。在 VS Code 中打开该文件,并添加以下代码:
## ~/project/function_vs_generator.py
import inspect
## 常规函数
def square_numbers_function(numbers):
result = []
for number in numbers:
result.append(number * number)
return result
## 生成器函数
def square_numbers_generator(numbers):
for number in numbers:
yield number * number
## 检查是否为生成器函数
print("Is square_numbers_function a generator function?", inspect.isgeneratorfunction(square_numbers_function))
print("Is square_numbers_generator a generator function?", inspect.isgeneratorfunction(square_numbers_generator))
在这段代码中,我们首先导入 inspect
模块。然后,使用 inspect.isgeneratorfunction()
检查 square_numbers_function
和 square_numbers_generator
。
现在,执行 Python 脚本:
python ~/project/function_vs_generator.py
你应该会看到以下输出:
Is square_numbers_function a generator function? False
Is square_numbers_generator a generator function? True
这证实了 inspect.isgeneratorfunction()
能够正确识别生成器函数。
当你需要根据函数是生成器函数还是常规函数来进行不同处理时,此方法非常有用。例如,你可能想要迭代生成器的结果,但只需调用常规函数。
yield
关键字的使用在这一步中,我们将重点关注 yield
关键字的存在如何从根本上定义一个生成器函数。当且仅当一个函数至少包含一个 yield
语句时,它才被视为生成器函数。让我们创建一个简单的测试来验证这一点。
使用 VS Code 打开你 ~/project
目录下的 function_vs_generator.py
文件。我们将添加一个不使用 yield
的函数,并观察它在被当作潜在生成器时的表现。
## ~/project/function_vs_generator.py
import inspect
## 常规函数
def square_numbers_function(numbers):
result = []
for number in numbers:
result.append(number * number)
return result
## 生成器函数
def square_numbers_generator(numbers):
for number in numbers:
yield number * number
## 返回列表的函数,而非生成器
def return_list(numbers):
return [x for x in numbers]
## 检查是否为生成器函数
print("Is square_numbers_function a generator function?", inspect.isgeneratorfunction(square_numbers_function))
print("Is square_numbers_generator a generator function?", inspect.isgeneratorfunction(square_numbers_generator))
print("Is return_list a generator function?", inspect.isgeneratorfunction(return_list))
在这段更新后的代码中,我们添加了一个 return_list
函数,它使用列表推导式简单地返回一个列表。该函数不包含 yield
关键字。然后,我们使用 inspect.isgeneratorfunction()
来检查 return_list
是否为生成器函数。
现在,执行 Python 脚本:
python ~/project/function_vs_generator.py
你应该会看到以下输出:
Is square_numbers_function a generator function? False
Is square_numbers_generator a generator function? True
Is return_list a generator function? False
正如预期的那样,return_list
未被识别为生成器函数,因为它没有使用 yield
关键字。这表明 yield
关键字对于在 Python 中定义生成器至关重要。没有它,函数就像常规函数一样,返回一个值(或 None
),并且在两次调用之间不会保留其状态。
在这个实验中,你学习了 Python 常规函数和生成器之间的根本区别。函数执行一段代码并返回一个值,而生成器使用 yield
关键字返回一个迭代器对象,该对象按需生成值,并在两次调用之间暂停并保存其状态。
你了解了生成器在处理大数据集时是如何节省内存的,因为它们一次只生成一个值,而不像函数那样将所有值都存储在内存中。本实验通过一个使用函数和生成器对数字进行平方运算的实际示例展示了这种区别。