(翻译)30天学习Python👨💻第十五天——生成器
今天我探索了Python中关于生成器的所有概念。JavaScript中也有生成器的概念。它是在ES6中引入的,但是我从来没有在实际的JavaScript项目中使用过。今天当我读关于生成器的内容时,我意识到它是非常有用的,用在很多Python的库和框架中。
所以什么是生成器?
生成器
生成器是一种特殊的函数,它们返回一个可迭代的集合,一次只返回一个值,意思是说它可以循环得到一个又一个的值。有时也称为“可暂停”的函数。理论上,生成器听起来非常复杂并且让人疑惑。最好的解释是使用代码示例。我们可以使用Python的内置迭代器range生成一个给定范围内的值。
range_of_numbers = range(100) # 一个生成器
for num in range_of_numbers:
print(num)
# 因为range是迭代器,所以它可以被迭代或者说循环
def my_infinite_generator():
num = 0
while True:
yield num
num +=1
result = my_infinite_generator()
for i in result:
print(i) # 将会不停的打印出值!
所以生成器函数到底是什么?它和普通的函数就什么不同?
函数可以使用返回语句返回一个值。一个函数遇到返回语句时,它返回一个值并且结束程序。而迭代器函数可以在任意位置使用一个特殊的关键字yield返回任意数量的值。当遇到yield语句时,函数将暂停,并将控制权让给函数调用者。
生成器的值可以使用迭代提取,也可以使用迭代器的另一个内置函数next提取。在上面的代码中,生成器的值使用迭代(for循环)打印出来。使用next函数可以手动的完成同样的工作。
def my_infinite_generator():
num = 0
while True:
yield num
num +=1
result = my_infinite_generator()
print(next(result)) # 0
print(next(result)) # 1
print(next(result)) # 2
print(next(result)) # 3
print(next(result)) # 4
注意生成器函数是如何记住num的值并且递增的。当遇到yield语句时它会保存局部变量和它的状态,然后将控制权让给调用者。
def my_generator(max):
num = 0
while num < 3:
yield num
num +=1
result = my_generator(3)
print(next(result)) # 0
print(next(result)) # 1
print(next(result)) # 2
print(next(result)) # 停止迭代
当迭代完成时(在上面的例子中是当num等于3时),生成器函数在下一次调用时将自动抛出一个StopIteration异常。如果使用for循环迭代同一个生成器,循环将会在生成器抛出StopIteration异常时自动停止。for循环会在内部处理并终止。
使用生成器的性能优势
生成器的内存效率更高。当处理大量需要处理才能计算结果的大型数据集时,生成器非常有用。内存是一种有限的资源,只能容纳有限的数据。
举个栗子,如果我们创建一个接受一个数字作为参数的函数打印斐波那契数列的函数,直到遇到这个数字为止。传统的方法看起来是这个样子的
def fibonacci(num):
sequence = []
a,b = 0,1
for item in range(num):
sequence.append(a)
temp = a
a = b
b = temp + b
return sequence
result = fibonacci(20)
print(result) # 打印斐波那契数列
在上面的函数中,整个数列作为一个列表存储在内存中。当这个数字比较小的时候还好,但是当数字很大时,内存使用量急剧增加,如果内存溢出,进程甚至可能被终止。
使用生成器,上面函数的内存效率就会提高了
def fibonacci_generator(num):
a,b = 0,1
for item in range(num):
yield a
temp = a
a = b
b = temp + b
for num in fibonacci_generator(20):
print(num)
组合
生成器可以组合在一起,或者换句话说,它们可以通过管道组合在一起,把不同生成器的结果组合在一起。
举个例子,如果我们创建了一个斐波那契数列,每个值都有平方,那么它们可以使用两个生成器函数组合。
def fibonacci_generator(num):
a,b = 0,1
for item in range(num):
yield a
temp = a
a = b
b = temp + b
def square(nums):
for num in nums:
yield num**2
result = square(fibonacci_generator(5))
for num in result:
print(num) # 0 1 1 4 9
这种组合在计算大型数据集和在各层中对它们应用不同操作时非常有用。
以上就是关于Python生成器的简要介绍。今天对生成器的探索帮助我巩固了围绕这一概念的思维模型,现在我可能也能想到用JavaScript实现它们的方法了。
明天是当前Python挑战第三周的开始,根据我制定的路线图,我将探索Python的另一个重要主题——模块。我相信那一定会很有趣。