Python 生成器以及yield关键字
2020-04-12 本文已影响0人
小黑天天快乐
-->>>我的原文链接
一、生成器
首先提一个问题:
range(5) 和list(range(5)) 有何区别?
大家可以尝试在Python shell里键入这两个命令看看是何结果。没有意外的话,前者返回range(0,5)
,后者返回[0, 1, 2, 3, 4]
。说明前者是一个range对象,后者是一个列表。
这还不是关键,我们分别定义一个range和相应的列表,看看前后内存占用的变化。
>>> print(psutil.Process(os.getpid()).memory_info().rss)
18161664
>>> x = list(range(10000000))
>>> print(psutil.Process(os.getpid()).memory_info().rss)
423337984 # 内存占用明显提升
>>> y = range(100000000000)
>>> print(psutil.Process(os.getpid()).memory_info().rss)
423337984 # 内存占用几乎不变
这其实就是生成器和普通列表的区别了。生成器在内存当中存储的是序列生成的逻辑,而我们一般用的列表,则是实实在在地把列表数据项放在内存里了。
应该可以体会到,当我们进行大数据的处理时,这是有显著差异的。
除此之外,我们再来看看处理时间,应该不难猜到,原理同上。
import time
t1 = time.time()
x = range(1000000)
t2 = time.time()
y = list(range(1000000))
t3 = time.time()
print(f'Time consumed for using range: {t2-t1}')
print(f'Time consumed for using list: {t3-t2}')
# Output
Time consumed for using range: 2.62e-06
Time consumed for using list: 0.031
确实,使用range几乎不占用任何时间,而用list则需要占用大量的时间。当然聪明的人一眼能看出,range只是定义了生成逻辑,真正提取数据项的时候还是需要时间的嘛,没错,但是这两个逻辑是不一样的。使用range的时候,我们每生成一项就可以对这个数据进行一些操作,但如果使用list,必须得等整个列表生成完毕才能操作。这就是为什么我们写for循环的时候都是写for i in range(x)
的原因了。
二、yield关键字
yield关键字用于函数中,将该函数的返回结果变成生成器。看下面例子,给定一个数组x,需要返回一个新数组,其中每一项为原数组的平方:
# 返回list
def SquareList(x):
result = []
for i in x:
result.append(i * i)
return result
# 返回生成器
def SqaureGen(x):
for i in x:
yield i * i
gen = SqaureGen([1,2,3,4])
for i in gen:
print(i)
# Output:
1
4
9
16
其中SquareGen函数通过yield关键字返回了一个生成器,效果同第一节解释的情况。