深入浅出 Python Generators
来源:互联网 发布:单片机自动门源程序 编辑:程序博客网 时间:2024/06/16 02:21
原文
- 如何构造generator
- Generator函数和Normal函数的差别
- 在Loop中yield
- Generator Expression Generator表达式
- Generator 的优点
- 简洁
- 节省内存
- 可以利用 Infinite Generator 产生 Infinite Stream
- Pipelining Generators
在上篇文章中介绍了如何用class构造iterator:必须实现__iter__()
和`__next__()
,必须keep internal states,在结束迭代时必须明确raise StopIteration。这一系列操作比较复杂,本篇介绍更简单的方法。
Python generators are a simple way of creating iterators.
Python generator 就是一个可以返回 iterator 的函数
如何构造generator?
在一个函数定义中,至少一处使用了yield,则该函数就成了一个generator。
generator可以包含任意个yield和return语句,区别在于:
- yield语句暂时退出该函数,保存函数的各种状态,下次继续从这里执行。
- 而return语句相当于一个raise StopIteration。
Generator函数和Normal函数的差别
Generator和Normal函数的差别:
- 至少包含一个yield
- 被调用时,它不是立即执行,而是返回一个iterator;一个
next()
会让它执行至第一个yield __iter__()
和__next__()
方法被自动创建- generator function 发生yield时,该function相当于被“暂停”了,然后控制转交给caller。
- Local variables 和其状态被保存
- 最后,当该函数结束时,StopIteration被自动raised。
下面是一个例子:
# A simple generator functiondef my_gen(): n = 1 print('This is printed first') # Generator function contains yield statements yield n n += 1 print('This is printed second') yield n n += 1 print('This is printed at last') yield n
然后进行一些测试:
>>> # It returns an object but does not start execution immediately.>>> a = my_gen()>>> # We can iterate through the items using next().>>> next(a)This is printed first1>>> # Once the function yields, the function is paused and the control is transferred to the caller.>>> # Local variables and theirs states are remembered between successive calls.>>> next(a)This is printed second2>>> next(a)This is printed at last3>>> # Finally, when the function terminates, StopIteration is raised automatically on further calls.>>> next(a)Traceback (most recent call last):...StopIteration>>> next(a)Traceback (most recent call last):...StopIteration
需要注意的是,generator 对象只能被 iterate 一次,如果想再次 iterate,需要重新定义一个该对象a = my_gen()
一般generators用在for loop中:
# A simple generator functiondef my_gen(): n = 1 print('This is printed first') # Generator function contains yield statements yield n n += 1 print('This is printed second') yield n n += 1 print('This is printed at last') yield n# Using for loopfor item in my_gen(): print(item)
运行上面的代码,可以看到的结果是:
This is printed first1This is printed second2This is printed at last3
这种写法也称为 Co-routine,利用 Generator 可以把一些异步的操作写成看似同步一样。具体的例子参见。
在Loop中yield
一般会在一个loop中进行yield来构造一个generator,下面是一个例子:
def rev_str(my_str): length = len(my_str) for i in range(length - 1,-1,-1): yield my_str[i]# For loop to reverse the string# Output:# o# l# l# e# hfor char in rev_str("hello"): print(char)
Generator Expression, Generator表达式
类似lambda function创建了一个anonymous function,generator expression用来创建一个anonymous generator function。
generator expression的语法类似于 list comprehension, 但是要把 [ ] 方括号改成 ( ) 圆括号。
区别在于,list comprehension 创造了整个list,而 generator expression 一次创建一个item。
因此,generator expression 更节省内存。
# Initialize the listmy_list = [1, 3, 6, 10]# square each term using list comprehension# Output: [1, 9, 36, 100][x**2 for x in my_list]# same thing can be done using generator expression# Output: <generator object <genexpr> at 0x0000000002EBDAF8>(x**2 for x in my_list)
可以看到,上面的 generator expression 并不是立马返回一个列表,而是返回了一个 generator 对象,需要使用 next
来产生一个值。
# Intialize the listmy_list = [1, 3, 6, 10]a = (x**2 for x in my_list)# Output: 1print(next(a))# Output: 9print(next(a))# Output: 36print(next(a))# Output: 100print(next(a))# Output: StopIterationnext(a)
注意: Generator expression 可以用在函数括号中,此时其圆括号可以省略掉:
>> sum(x**2 for x in my_list)146>>> max(x**2 for x in my_list)100
Generator 的优点
1. 简洁
对比一下 PowTwo 用 iterator 类的实现和 Generator 的实现:
lass PowTwo: def __init__(self, max = 0): self.max = max def __iter__(self): self.n = 0 return self def __next__(self): if self.n > self.max: raise StopIteration result = 2 ** self.n self.n += 1 return result
同样的功能,Generator 会更加简洁:
def PowTwo(max = 0): n = 0 while n < max: yield 2 ** n n += 1
因为 generator 默认实现了很多细节
2. 节省内存
这一点在比较 list comprehension 和 generator 时已经解释过了。
3. 可以利用 Infinite Generator 产生 Infinite Stream
这一点在上一篇中也解释过了。
4. Pipelining Generators
利用 Generator 可以把一系列操作放入管道中,效率更高。
在下面的例子中,假设一个文件每一行都是一个数字,或者是一个 ‘N/A’ ,我们想把所有的数字加起来:
with open('afile.log') as file: col = (line for line in file) num = (float(x) for x in col if x != 'N/A') print("The sum is: ", sum(num))
问题:是否效率更高?还没验证过。如何验证?
- 深入浅出 Python Generators
- python generators
- python中的生成器(Generators)
- Python中Generators教程
- Python Generators(生成器)
- 深入浅出ES6(三):生成器 Generators
- 深入浅出ES6(十一):生成器 Generators,续篇
- 深入浅出ES6(三):生成器 Generators
- 深入浅出ES6(十一):生成器 Generators,续篇
- 深入浅出ES6(三):生成器 Generators
- python学习之生成器(Generators)
- Working with Generators of Python
- Generators
- Python Generators(生成器)——yield关键字
- Improve Your Python: 'yield' and Generators Explained
- Python: 解释‘yield’和‘Generators(生成器)’
- python ‘yield’和‘Generators(生成器)’
- Python Generators(生成器)——yield关键字
- 如何转载CSDN博客
- 基于UDP的P2P聊天工具 0.3——消息队列和重传
- 广播
- git+maven自动化部署tomcat java项目
- 合并排序
- 深入浅出 Python Generators
- hdu 2159 FATE(二维背包+完全背包)
- Holiday's Accommodatio UVA
- Python 起步-list的小总结
- 初识maven
- 171105-初步自学内联函数和重载函数【连续第十三天】
- 设计模式解决的问题
- MATLAB散点图、表面图、等高线图绘制命令
- jmeter初用错误汇总