Python有个yield
语法,这种语法叫做生成器.例如:
def test(N):
for i in range(N):
yield i ** 2
test(5)
我们可以使用for
循环迭代它:
for item in test(5):
print(item)
不使用yeild的实现方案:
def test(N):
res = []
for i in range(N):
res.append(i*i)
return res
for item in test(5):
print(item)
首先你会发现生成器的第一个优点,它会使代码变得简洁,清晰.
其次,不使用yield
语句函数会将列表一次性返回,而使用yield
语句一次只返回一个结果,然后挂起函数的状态,下次从离开的地方继续执行.假如列表很大的话,返回整个列表会占用很大内存,而生成器用到哪个值的时候现去计算,比较节约内存,这在处理大数据的时候非常有优势.这是生成器的第二个优点.
除了yield
语法外,Python还可以以表达式的形式建立生成器.先来看通常建立列表的方法:
squares = [x**2 for x in range(5)]
squares
将列表推导的中括号,替换成圆括号,就是一个生成器表达式:
squares = (x**2 for x in range(5))
squares
和使用yield
语法是相同,没有直接返回列表,而是返回一个生成器对象,当我们迭代的时候才会去计算其中的值.
另外,生成器实现了迭代器协议,所以可以使用next()
函数调用下一个值:
next(squares)
next(squares)
next(squares)
Python的大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:
sum(x ** 2 for x in range(4))
而不用多此一举的先构造一个列表:
sum([x ** 2 for x in range(4)])
最后,让我们用一个例子来更好的理解生成器延迟计算带来的强悍效果.
sum([i for i in range(10000000000)])
sum(i for i in range(10000000000))
对于第一个表达式,你会发现内存几乎接近饱和,对于第二个表达式,内存几乎没有什么消耗.有了生成器再也不用担心内存不够用了有木有.