Python 生成器
Python 生成器是什么
Python 生成器是返回可迭代对象并用于创建迭代器的功能。它同时遍历所有的项目。生成器也可以是一个表达式,其语法与Python的列表推导类似。
在Python中创建迭代器有很多复杂性;我们需要实现 __iter__()
和 __next__()
方法来跟踪内部状态。
创建迭代器是一个冗长的过程。这就是生成器在简化这个过程中发挥关键作用的原因。如果在迭代中找不到值,它会引发 StopIteration 异常。
如何在Python中创建生成器函数
在Python中,创建生成器并不难。它类似于典型的功能,以def关键字为特征,并使用yield关键字而不是return。或者我们可以说,如果任何功能的主体包含yield语句,它就会自动成为生成器函数。考虑以下示例:
def simple():
for i in range(10):
if(i%2==0):
yield i
#Successive Function call using for loop
for i in simple():
print(i)
输出:
0
2
4
6
8
yield vs. return
yield表达式负责控制生成器函数的执行流程。通过保存所有状态并向调用者进行yield操作,它终止了函数的执行。当调用下一个生成器函数时,它会重新开始执行。在生成器函数中,我们可以使用多个yield语句。
return语句返回一个值并结束整个函数,而且在函数中只能使用一个return语句。
使用多个yield语句
我们可以在生成器函数中使用多个yield语句。考虑以下示例。
def multiple_yield():
str1 = "First String"
yield str1
str2 = "Second string"
yield str2
str3 = "Third String"
yield str3
obj = multiple_yield()
print(next(obj))
print(next(obj))
print(next(obj))
输出:
First String
Second string
Third String
生成器函数和普通函数之间的区别
典型能力只包含一个返回语句,而生成器函数可以包含一个或多个yield语句。
当调用生成器函数时,普通函数立即停止并将控制权交给调用者。
局部变量的状态在调用之间保持不变。
当函数终止时,会自动引发StopIteration异常。
生成器表达式
我们可以在不使用用户定义函数的情况下轻松创建生成器表达式。它类似于lambda函数,可以创建一个匿名函数;生成器表达式通过生成器的表达式创建一个匿名的生成器函数。
生成器表达式的表示方式类似于Python的列表推导式。唯一的区别是圆括号取代了方括号。生成器表达式每次只计算一个元素,而列表推导式会计算整个列表。
考虑以下示例:
list = [1,2,3,4,5,6,7]
# List Comprehension
z = [x**3 for x in list]
# Generator expression
a = (x**3 for x in list)
print(a)
print(z)
输出:
<generator object <genexpr> at 0x01BA3CD8>
[1, 8, 27, 64, 125, 216, 343]
在上面的程序中,列表推导式返回了元素的立方列表,而生成器表达式则返回了计算值的引用。不需要应用for循环,我们也可以在生成器对象上调用next()。让我们考虑另一个示例:
list = [1,2,3,4,5,6]
z = (x**3 for x in list)
print(next(z))
print(next(z))
print(next(z))
print(next(z))
输出:
1
8
27
64
注意:- 当我们调用next()时,Python会在我们将其作为参数传递给的函数上调用next()。
在上述程序中,我们使用了 next() 函数,它返回列表的下一个项目。
示例: 编写一个程序,使用生成器打印给定数字的表。
def table(n):
for i in range(1,11):
yield n*i
i = i+1
for i in table(15):
print(i)
输出:
15
30
45
60
75
90
105
120
135
150
在上面的示例中,生成器函数正在使用for循环进行迭代。
生成器的优点
生成器有各种优点。以下是其中的一些:
1. 实现简单
与迭代器相比,生成器的实现更简单。在迭代器中,我们需要实现iter()和next()函数。
2. 节省内存
对于许多序列来说,生成器能够高效地利用内存。而生成器函数则计算值并挂起执行,而普通函数会从列表返回一个序列,首先在返回结果之前在内存中创建整个序列。它会在进行逐步调用时继续执行。无限连续的生成器是内存优化的一个很好的示例。让我们在下面的示例中使用sys.getsizeof()函数来进行讨论。
import sys
# List comprehension
nums_squared_list = [i * 2 for i in range(1000)]
print(sys.getsizeof("Memory in Bytes:"nums_squared_list))
# Generator Expression
nums_squared_gc = (i ** 2 for i in range(1000))
print(sys.getsizeof("Memory in Bytes:", nums_squared_gc))
输出:
Memory in Bytes: 4508
Memory in Bytes: 56
我们可以从上面的输出中观察到,列表推导式使用了4508字节的内存,而生成器表达式只使用了56字节的内存。这意味着生成器对象比列表推导式更高效。
3. 使用生成器进行数据管道操作
信息管道可以处理大量数据集或信息流,而不需要额外的计算机内存。
假设我们有一个著名餐馆的日志文件。日志文件有一个部分(第四部分)记录了每天售出的汉堡数量,我们想要统计出四年内售出的汉堡总数。在这种情况下,生成器可以创建一个使用一系列操作的管道。代码如下:
with open('sells.log') as file:
burger_col = (line[3] for line in file) per_hour = (int(x) for x in burger_col if x != 'N/A')
print("Total burgers sold = ",sum(per_hour))
4. 生成无限序列
生成器可以生成无限的项。无限序列无法存储在内存中,由于生成器每次只生成一个项,可以考虑以下示例:
def infinite_sequence():
num = 0
while True:
yield num
num += 1
for i in infinite_sequence():
print(i)
输出:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.........
..........
315
316
317
Traceback (most recent call last):
File "C:\Users\DEVANSH SHARMA\Desktop\generator.py", line 33, in <module>
print(i)
KeyboardInterrupt
在本教程中,我们学习了Python生成器。