generator 生成器

2019年8月27日 / 76次阅读 / Last Modified 2021年6月10日
语法

Python的生成器(generator)是一种迭代器,可以通过next函数访问,也可以在for...in...循环中遍历。生成器有自己的特殊的语法,有点像推导式(List Comprehension),也可以在自定义的函数使用yield关键词。

生成器的出现,跟迭代器一样,也是为了在内存使用方面更友好。 有时候,序列或集合内的元素的个数非常巨大,如果全都制造出来并放入内存,对计算机的压力是非常大的。假设我们需要获取一个10**20次方如此巨大的数据序列,把每一个数都生成出来,并放在一个内存的列表内,这是很粗暴的方式,有如此大的内存么?如果元素可以按照某种算法推算出来,需要计算到哪里,就计算到哪里,就可以在循环的过程中不断推算出后续的元素,而不必创建完整的元素集合,从而节省大量的空间。

用括号(())创建生成器

>>> g = (x for x in range(3))
>>> g
<generator object  at 0x7ff567ff4840>
>>> next(g)
0
>>> next(g)
1
>>> next(g)
2
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> for i in g:
...     print(i)
...
>>>

生成器通过与推导式(List Comprehension)相同的语法(放在括号内)被创造出来,然后通过next函数访问其元素,知道出现StopIteration异常。此时,生成器中的元素已经被清除。

用圆括号括起来的叫做生成器表达式(generator expression)。Some simple generators can be coded succinctly as expressions using a syntax similar to list comprehensions but with parentheses instead of square brackets. These expressions are designed for situations where the generator is used right away by an enclosing function. Generator expressions are more compact but less versatile than full generator definitions and tend to be more memory friendly than equivalent list comprehensions.

>>> sum(i*i for i in range(10))                 # sum of squares
285

>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec))         # dot product
260
>>> unique_words = set(word for line in page  for word in line.split())
>>> valedictorian = max((student.gpa, student.name) for student in graduates)
>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1, -1, -1))
['f', 'l', 'o', 'g']

用yield关键词自定义生成器函数

这是一个更加灵活的创建Python生成器的方式,我先给出示例代码:

>>> def testg():
...     for i in range(3):
...         yield i
...     return
...
>>> for j in testg():
...     print(j)
...
0
1
2

testg函数就是一个生成器函数,每当代码执行到yield语句的时候,就像return一样,testg返回一个值,并且记住了这个位置,下一次再进入testg函数的时候,从上次yield返回的位置开始继续执行,直到下一个yield,或者最后的真正的return。

testg函数被放在了for...in...语句中调用,确保了每一次testg yield一个值,循环体就开始执行,下一次循环之前,会再次进入testg函数,等待下一个yield。这就是自定义的生成器函数。可以看出,testg函数将一个序列的每一个值,一次一个的返回给上层代码,但是并没有讲每个值都保存下来,这样就起到了节省内存的作用。

yield语句还可以返回复杂的tuple,这正是generator更加灵活的地方。

>>> def g2():
...     for i in range(3):
...         yield i,i**2,i**3
...     return
...
>>> for a,b,c in g2():  
...     print(a,b,c)
...
0 0 0
1 1 1
2 4 8
>>>
>>> k = g2()  # another use case
>>> next(k)
(0, 0, 0)
>>> next(k)
(1, 1, 1)
>>> next(k)
(2, 4, 8)
>>> next(k)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

yield一下子吐出来3个值,全部在外层的for循环中被处理。生成器还可以直接赋值给一个变量,用next函来访问,效果跟在for循环中是一样的。

以上就是对Python生成器的介绍,希望对你的代码有帮助。

-- EOF --

本文链接:https://www.pynote.net/archives/967

留言区

《generator 生成器》有1条留言

您的电子邮箱地址不会被公开。 必填项已用*标注

  • 麦新杰

    在生成器中,不要使用return返回数据! [回复]


前一篇:
后一篇:

More


©Copyright 麦新杰 Since 2019 Python笔记

go to top