Python generator和yield介绍
Python生成器(generator)并不是一个晦涩难懂的概念。相比于MetaClass和Closure等概念,其较为容易理解和掌握。但相对于程序结构:顺序、循环和分支而言其又不是特别的直观。无论学习任何的东西,概念都是非常重要的。正确树立并掌握一些基础的概念是灵活和合理运用的前提,本文将以一种通俗易懂的方式介绍一下generator和yield表达式。
1. Iterator与Iterable
首先明白两点:
- Iterator(迭代器)是可迭代对象;
- 可迭代对象并不一定是Iterator;
比较常见的数据类型list、tuple、dict等都是可迭代的,属于collections.Iterable类型;
迭代器不仅可迭代还可以被内置函数next调用,属于collections.Iterator类型;
迭代器是特殊的可迭代对象,是可迭代对象的一个子集。
将要介绍的gererator(生成器)是types.GeneratorType类型,也是collections.Iterator类型。
也就是说生成器是迭代器,可被next调用,也可迭代。
三者的包含关系:(可迭代(迭代器(生成器)))
迭代器:可用next()函数访问的对象;
生成器:生成器表达式和生成器函数;
2. Python生成器
python有两种类型的生成器:生成器表达式和生成器函数。
由于生成器可迭代并且是iterator,因此可以通过for和next进行遍历。
2.1 生成器表达式
把列表生成式的[]改成()便得到生成器表达式。
>>> gen = (i + i for i in xrange(10))
>>> gen
<generator object <genexpr> at 0x0000000003A2DAB0>
>>> type(gen)
<type 'generator'>
>>> isinstance(gen, types.GeneratorType) and isinstance(gen, collections.Iterator) and isinstance(gen, collections.Iterable)
True
>>>
2.2 生成器函数
python函数定义中有关键字yield,该函数便是一个生成器函数,函数调用返回的是一个generator.
def yield_func():
for i in xrange(3):
yield i
gen_func = yield_func()
for yield_val in gen_func:
print yield_val
生成器函数每次执行到yield便会返回,但与普通函数不同的是yield返回时会保留当前函数的执行状态,再次被调用时可以从中断的地方继续执行。
2.3 next与send
通过for和next可以遍历生成器,而send则可以用于向生成器函数发送消息。
def yield_func():
for i in xrange(1, 3):
x = yield i
print 'yield_func',x
gen_func = yield_func()
print 'iter result: %d' % next(gen_func)
print 'iter result: %d' % gen_func.send(100)
结果:
iter result: 1
yield_func 100
iter result: 2
简单分析一下执行过程:
- line_no 5 调用生成器函数yield_func得到函数生成器gen_func;
- line_no 6 使用next调用gen_func,此时才真正的开始执行yield_func定义的代码;
- line_no 3 执行到yield i,函数yield_func暂停执行并返回当前i的值1.
- line_no 6 next(gen_func)得到函数yield_func执行到yield i返回的值1,输出结果iter result: 1;
- line_no 7 执行gen_func.send(100);
- line_no 3 函数yield_func继续执行,并将调用者send的值100赋值给x;
- line_no 4 输出调用者send接收到的值;
- line_no 3 执行到yield i,函数yield_func暂停执行并返回当前i的值2.
- line_no 7 执行gen_func.send(100)得到函数yield_func运行到yield i返回的值2,输出结果iter result: 2;
如果在上面代码后面再加一行:
print 'iter result: %d' % next(gen_func)
结果:
iter result: 1
yield_func 100
iter result: 2
yield_func None
File "G:\Cnblogs\Alpha Panda\Main.py", line 22, in <module>
print 'iter result: %d' % next(gen_func)
StopIteration
yield_func只会产生2个yield,但是我们迭代调用了3次,会抛出异常StopIteration。
next和send均会触发生成器函数的执行,使用for遍历生成器函数时不要用send。原因后面解释。
2.4 生成器返回值
使用了yield的函数严格来讲已经不是一个函数,而是一个生成器。因此函数中yield和return是不能同时出现的。
SyntaxError: 'return' with argument inside generator
生成器只能通过yield将每次调用的结果返回给调用者。
2.5 可迭代对象转成迭代器
list、tuple、dict等可迭代但不是迭代器的对象可通过内置函数iter转化为iterator,便可以通过next进行遍历;
这样的好处是可以统一使用next遍历所有的可迭代对象;
tup = (1,2,3)
for ele in tup:
print ele + ele
上面的代码等价于:
tup_iterator = iter(tup)
while True:
try:
ele = next(tup_iterator)
except StopIteration:
break
print ele + ele
for循环使用next遍历一个迭代器,混合使用send可能会导致混乱的遍历流程。
其实到这里生成器相关的概念基本已经介绍完成了,自己动手过一遍应该能弄明白了。为了更加深刻的体会生成器,下面我们在往前走一步。
3. range与xrange
在Python 2中这两个比较常用,看一下两者的区别:
- range为一个内置函数,xrange是一个类;
- 前者返回一个list,后者返回一个可迭代对象;
- 后者遍历操作快于前者,且占用更少内存;
这里xrange有点类似于上面介绍的生成器表达式,虽然xrange返回的并不是生成器,但两者均返回并不包含全部结果可迭代对象。
3.1 自定义xrange的Iterator版本
作为一个iterator:
The iterator objects themselves are required to support the following two methods, which together form the iterator protocol:
iterator.__iter__()Return the iterator object itself. This is required to allow both containers and iterators to be used with the
forandinstatements. This method corresponds to thetp_iterslot of the type structure for Python objects in the Python/C API.
iterator.next()Return the next item from the container. If there are no further items, raise the
StopIterationexception. This method corresponds to thetp_iternextslot of the type structure for Python objects in the Python/C API.
下面我们自定义class my_xrange:
class my_xrange(object):
def __init__(self, start, stop = None, step = 1):
""" 仅仅为了演示,假设start, stop 和 step 均为正整数 """
self._start = 0 if stop is None else start
self._stop = start if stop is None else stop
self._step = step
self._cur_val = self._start def __iter__(self):
return self def next(self):
if self._start <= self._cur_val < self._stop:
cur_val = self._cur_val
self._cur_val += self._step
return cur_val
raise StopIteration
测试结果:
import collections
myxrange = my_xrange(0, 10, 3)
res = []
for val in myxrange:
res.append(val)
print res == range(0, 10, 3) # True
print isinstance(myxrange, collections.Iterator) # True
print isinstance(myxrange, types.GeneratorType) # False
3.2 使用函数生成器
下面使用函数生成器定义一个generator版的xrange。
def xrange_func(start, stop, step = 1):
""" 仅仅为了演示,假设start, stop 和 step 均为正整数 """
cur_val = start
while start <= cur_val and cur_val < stop:
yield cur_val
cur_val += step
isinstance(myxrange, collections.Iterator) and isinstance(myxrange, types.GeneratorType) is True
上面两个自定义xrange版本的例子,均说明生成器以及迭代器保留数列生成过程的状态,每次只计算一个值并返回。这样只要占用很少的内存即可表示一个很大的序列。
4. 应用
不管是迭代器还是生成器,对于有大量有规律的数据产生并需要遍历访问的情景均适用,占用内存少而且遍历的速度快。其中一个较为经典的应用为斐波那契数列(Fibonacci sequence)。
这里以os.walk遍历目录为例来说明yield的应用。如果我们需要遍历一个根目录下的所有文件并根据需要进行增删改查。可能会遇到下列的问题:
预先遍历且缓存结果,但是目录下文件可能很多,而且会动态改变;如果不缓存,多个地方可能会频繁的需要访问这一结果导致效率低下。
这时候可以使用yield定义一个生成器函数。
def get_all_dir_files(target_dir):
for root, dirs, files in os.walk(target_dir):
for file in files:
file_path = os.path.join(root, file)
yield os.path.realpath(file_path) def file_factory(file):
""" do something """ target_dir = './'
all_files = get_all_dir_files(target_dir)
for file in all_files:
file_factory(file)
限于篇幅,就先介绍到这里,希望本文能让你对生成器有一个新的认识。
Python generator和yield介绍的更多相关文章
- Python generator 的yield (enumerate)
生成杨辉三角 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 def triangles(max): L = [1,] while len(L) - 1 < ...
- python generator与coroutine
python generator与coroutine 协程 简单介绍 协程,又称微线程,纤程,英文名Coroutine.协程是一种用户态的轻量级线程,又称微线程.协程拥有自己的寄存器上下文和栈,调度 ...
- 关于Python中的yield
关于Python中的yield 在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,f ...
- Python:笔记(7)——yield关键字
Python:笔记(7)——yield关键字 yield与生成器 所谓生成器是一个函数,它可以生成一个值的序列,以便在迭代中使用.函数使用yield关键字可以定义生成器对象. 一个例子 我们调用该函数 ...
- python协程--yield和yield from
字典为动词“to yield”给出了两个释义:产出和让步.对于 Python 生成器中的 yield 来说,这两个含义都成立.yield item 这行代码会产出一个值,提供给 next(...) 的 ...
- Python生成器(yield)
对于调用一个普通的Python函数,一般是从函数的第一行代码开始执行,结束于return语句.异常或者函数所有语句执行完毕.一旦函数将控制权交还给调用者,就意味着全部结束.函数中做的所有工作以及保存在 ...
- python Scrapy安装和介绍
python Scrapy安装和介绍 Windows7下安装1.执行easy_install Scrapy Centos6.5下安装 1.库文件安装yum install libxslt-devel ...
- RUF MVC5 Repositories Framework Generator代码生成工具介绍和使用
RUF MVC5 Repositories Framework Generator代码生成工具介绍和使用 功能介绍 这个项目经过了大半年的持续更新到目前的阶段基本稳定 所有源代码都是开源的,在gith ...
- (转) Python Generators(生成器)——yield关键字
http://blog.csdn.net/scelong/article/details/6969276 生成器是这样一个函数,它记住上一次返回时在函数体中的位置.对生成器函数的第二次(或第 n 次) ...
随机推荐
- BZOJ4554: [Tjoi2016&Heoi2016]游戏 luoguP2825 loj2057
题面描述:尽可能多的放置符合要求的炸弹. 分析: 在i,j处放置炸弹,则在第i行,上一个硬石头之后,下一个硬石头之前,第j列,上一个硬石头之后,下一个硬石头之前,不能再次放置炸弹. 首先,这个题,一看 ...
- Go中原始套接字的深度实践
1. 介绍 2. 传输层socket 2.1 ICMP 2.2 TCP 2.3 传输层协议 3. 网络层socket 3.1 使用Go库 3.2 系统调用 3.3 网络层协议 4. 总结 4.1 参考 ...
- 贝叶斯深度学习(bayesian deep learning)
本文简单介绍什么是贝叶斯深度学习(bayesian deep learning),贝叶斯深度学习如何用来预测,贝叶斯深度学习和深度学习有什么区别.对于贝叶斯深度学习如何训练,本文只能大致给个介绍. ...
- 小白学习Python之路---re模块学习和挑战练习
本节大纲: 1.正则表达式 2.re模块的学习 3.速记理解技巧 4.挑战练习--开发一个简单的python计算器 5.心得总结 6.学习建议 正则表达式: 正则表达式,又称规则表达式.(英语:Reg ...
- 大数据技术之_19_Spark学习_02_Spark Core 应用解析小结
1.RDD 全称 弹性分布式数据集 Resilient Distributed Dataset它就是一个 class. abstract class RDD[T: ClassTag]( @tra ...
- 基本服务器的AAA实验
一.实验拓扑 二.网络地址分配 三.不同网段互相PING通 PC-A ping PC-B PC-A ping PC-C PC-B ping PC-C 四.配置过程 (1)在路由器R1上配置本地用户账号 ...
- Visio打开或取消箭头的自动吸附和自动连接
在用Visio画图时Visio的自动对齐.自动连接.自动吸附功能确实能带了很多便利.但在画连接线时,Visio总是自动连接箭头与图形的固定节点,想要微调一下连接位置,就显得很不方便,需要关闭自动连接功 ...
- Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFa
Springboot通过application启动报错 2019-01-25 14:02:33.810 ERROR [restartedMain] org.springframework.boot.S ...
- 关于ASL(平均查找长度)的简单总结
ASL(Average Search Length),即平均查找长度,在查找运算中,由于所费时间在关键字的比较上,所以把平均需要和待查找值比较的关键字次数成为平均查找长度. 它的定义是这样的: 其中n ...
- 折腾Java设计模式之解释器模式
解释器模式 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端可以使用这个解释器来解释这个语言中的句子. 意图 给定一个语言,定义它的文法表 ...