【Python注意事项】如何理解python中间generator functions和yield表情
本篇记录自己的笔记Python的generator functions和yield理解表达式。
1. Generator Functions
Python支持的generator functions语法同意我们定义一个行为与iterator类似的函数,它能够被用在须要循环调用的场合。
与普通函数相比,generator functions仅仅是在函数定义中多了1个yield表达式。除此之外,没有其他特别之处。
当generator函数被创建时。python解释器会自己主动为它实现iteration protocol(实现__iter__和next方法,详细可參见python官网文档Iterator Types部分)以支持其被用于须要迭代的场合。
调用普通函数时,被调用函数一般会从函数体第1行開始运行并在遇到return或抛异常时退出,其内部变量也会随之销毁。
而generator functions被调用时,它会返回一个generator object,这个object被循环调用时,每次都会yield一个值,而非return一个值(由此带来的有异于普通函数的行为,会在下一小节介绍yield表达式时做具体说明)。
generator functions具有2个典型特性:
1) 懒惰求值。故节省内存,详见这篇文档Improved Performance部分的说明
2) 具有iterator的行为特性,故它能够被用在for循环中
比如在Python2.x中。若for循环的循环次数很大,为节省内存,我们一般会写出例如以下代码:
for idx in xrange(0, 100000000):
do something
这里的xrange()函数返回的是一个xrange对象(而range()则返回一个list对象),从概念上来看。该xrange对象是一个generator object。由于generator的上述2个特性。xrange object可用于for循环。且无论循环次数多大。它都仅仅占用极少量内存(由于它不会一次性生成包括n个元素的list)
须要注意的是:仅仅有在不须要频繁生成同一份数据集合的情况下(大部分应用都是这类case),generator function才干提供预期的性能优势;否则,其优势也可能转变为劣势。
通过以下的场景。相信能够帮助我们easy地理解这句话。
如果python进程生成1个整数的过程非常耗时,以下是一种调用sum和product的方式:
>>> s = sum(xrange(1000000))
>>> p = product(xrange(1000000))
再对照还有一种实现方式:
>>> nums = list(xrange(1000000))
>>> s = sum(nums)
>>> p = product(nums)
非常显然,第1种方式调用sum和product时会分别生成1次元素为integer的大集合,而第2种方式仅仅会生成1次并存放在内存供兴许使用中。在我们的如果场景下。第2种方式会更节省时间(典型的空间换时间)。只是,如果机器内存受限以至于无法hold住大集合,则仅仅能採用第1种实现方式(时间换空间的折衷方式),即使其性能有损耗。
除支持generator functions外。python还支持generator expressions语法。关于generator的具体说明,强烈建议精读PYTHON-
GENERATOR FUNCTIONS AND EXPRESSIONS这篇文章 。
2. yield表达式
从官网文档可知,yield表达式仅仅能用于generator function的定义体中。也即。仅仅要某函数定义中出现了yield表达式。该函数就成了generator函数。而不再是个普通函数。
我们能够从"yield"的字面含义来理解yield表达式的行为:程序运行至该表达式时,会临时"放弃"继续向下运行的权利,程序控制权会返回给其调用者且由yield表达式"产生"的值也会返回给caller,此外,函数运行yield表达式后挂起时的context(包含那个时刻函数的local scope和local variables等信息)会被保存下来以便兴许恢复运行现场(与CPU处理中断信号的过程很类似)。
当然。仅从字面意思判断yield表达式行为的思路并不严谨,这样解释仅仅是为了辅助理解。以下是PYTHON-
GENERATOR FUNCTIONS AND EXPRESSIONS一文中出现的关于generator functions和yield表达式行为的更严谨解释:
Normal functions return a value and then exit. But generator functions automatically suspend and resume their execution. Because of that, they are often a useful alternative to both computing an entire series
of values up front and manually saving and restoring state in classes. Because the state that generator functions retain when they are suspended includes their local scope, their local variables retain information and make it available when the functions are
resumed.
The primary difference between generator and normal functions is that a generator yields a value, rather than returns a value. The yield suspends the function and sends a value back to the caller while retains
enough state to enable the function immediately after the last yield run. This allows the generator function to produce a series of values over time rather than computing them all at once and sending them back in a list.
为更好地理解上面这段话,能够分析以下这段代码:
>>> def create_counter(n):
print('create_counter()')
while True:
yield n
print('increment n')
n += 1 >>> c = create_counter(2)
>>> c
<generator object create_counter at 0x03004B48>
>>> next(c)
create_counter()
2
>>> next(c)
increment n
3
>>> next(c)
increment n
4
>>>
上面的代码用def定义了名为create_counter的generator function来实现计数器功能。
对象c是由create_counter创建出来的generator object。当调用python built-in函数next()时,它会调用其參数c的__next__方法。
备注:由第1小节的介绍可知,python解释器在创建generator object时会为其自己主动生成支持__next__的代码。
第1次调用next()时,c对象会运行yield n,其行为是函数体被挂起并将n返回给调用者。故能够看到第1次调用next()的输出并未包括"increment n"这个字符串。事实上。函数体挂起并返回时,其运行上下文会被记录下来,仅仅只是这一步我们无法显式感受到而已。
第2次调用next()时。generator function在上次挂起的地方恢复运行环境(本例中为n的值)后继续向下运行,故先输出"increment n"字符串。然后n+1,因为while loop的存在,因此程序又运行到yield n,它又会挂起并返回,故终端输出3
第n次调用next()时。函数行为与第2次调用全然一致,此处不赘述。
真正理解了上面代码的输出后。相信我们也会真正理解yield表达式和generator function的语法行为。
此外。Improve Your Python: 'yield' and Generators Explained这篇文章最后部分对generator和yield表达式做了很精炼且准确的总结,摘录例如以下:
1) generators are used to generate a series of values
2) yield is like the return of generator functions
3) The only other thing yield does is save the "state" of a generator function
4) A generator is just a special type of iterator
5) Like iterators, we can get the next value from a generator using next()
6) for gets values by calling next() implicitly
相信有了本篇笔记前面的介绍。这几点非常easy理解。
以下是一段利用yield语法生成裴波纳契数列的代码。是不是非常pythonic呢? ^_^
#!/bin/env python def fib(threshold):
a, b = 0, 1
while a < threshold:
yield a
a, b = b, a + b def main():
for n in fib(100):
print n if '__main__' == __name__:
main()
【參考资料】
1. Python Docs: Yield expressions
2. PYTHON - GENERATOR FUNCTIONS AND EXPRESSIONS
3. Improve Your Python: 'yield' and Generators Explained
4. Python PEP 255 -- Simple Generators
5. Python PEP 342 -- Coroutines via Enhanced Generators
========================= EOF =============================
版权声明:本文博主原创文章,博客,未经同意不得转载。
【Python注意事项】如何理解python中间generator functions和yield表情的更多相关文章
- 深入理解Python生成器(Generator)
我们可以通过列表生成式简单直接地创建一个列表,但是受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,而且如果我们仅仅需要访问前面几个元素,那后面绝大多 ...
- 从底层理解Python的执行
摘要:是否想在Python解释器的内部晃悠一圈?是不是想实现一个Python代码执行的追踪器?没有基础?不要怕,这篇文章让你初窥Python底层的奥妙. [编者按]下面博文将带你创建一个字节码级别的追 ...
- 深入理解Python中的生成器
生成器(generator)概念 生成器不会把结果保存在一个系列中,而是保存生成器的状态,在每次进行迭代时返回一个值,直到遇到StopIteration异常结束. 生成器语法 生成器表达式: 通列表解 ...
- 深入理解 Python 异步编程(上)
http://python.jobbole.com/88291/ 前言 很多朋友对异步编程都处于"听说很强大"的认知状态.鲜有在生产项目中使用它.而使用它的同学,则大多数都停留在知 ...
- 完全理解 Python 迭代对象、迭代器、生成器(转)
完全理解 Python 迭代对象.迭代器.生成器 本文源自RQ作者的一篇博文,原文是Iterables vs. Iterators vs. Generators » nvie.com,俺写的这篇文章是 ...
- 理解Python协程:从yield/send到yield from再到async/await
Python中的协程大概经历了如下三个阶段:1. 最初的生成器变形yield/send2. 引入@asyncio.coroutine和yield from3. 在最近的Python3.5版本中引入as ...
- 完全理解 Python 迭代对象、迭代器、生成器
完全理解 Python 迭代对象.迭代器.生成器 2017/05/29 · 基础知识 · 9 评论 · 可迭代对象, 生成器, 迭代器 分享到: 原文出处: liuzhijun 本文源自RQ作者 ...
- 彻底理解Python中的yield
阅读别人的python源码时碰到了这个yield这个关键字,各种搜索终于搞懂了,在此做一下总结: 通常的for…in…循环中,in后面是一个数组,这个数组就是一个可迭代对象,类似的还有链表,字符串,文 ...
- 全面理解Python中的类型提示(Type Hints)
众所周知,Python 是动态类型语言,运行时不需要指定变量类型.这一点是不会改变的,但是2015年9月创始人 Guido van Rossum 在 Python 3.5 引入了一个类型系统,允许开发 ...
随机推荐
- Android滑动到顶部悬停
无图说卵,先上图 jianshu-top.gif 查阅资料后,发现网上大部分都是用这种方法实现的: 多写一个和需要悬浮的部分一模一样的layout,先把浮动区域的可见性设置为gone.当浮动区域滑动到 ...
- Nginx系列(二)--模块化
高度模块化的设计设Nginx架构的基础. 在Nginx中,除了少量的核心代码,其它一切皆为模块.模块化设计具有下面特点: 1.高度抽象的模块接口 2.灵活性 3.配置模块的设计使Nginx提供了高可配 ...
- 数据库中暂时表,表变量和CTE使用优势极其差别
1 在写SQL时常常会用到暂时表,表变量和CTE,这三者在使用时各有优势: 1. 暂时表:分为局部暂时表和全局暂时表. 1.1局部暂时表,创建时以#开头,在系统数据库tempdb中存储. 在当前的链接 ...
- OpenGL_ES-纹理
OpenGL_ES2.0 -纹理 一:纹理基础: 1: 纹素的概念: 一个二维纹理在OpenGLES2.0中是非经常见的,二维纹理就是一个二维数组,每一个数据元素称为纹素,详细格式例如以下: GL_R ...
- Yarn架构基本概况(一)
1)引言 针对MRv1在扩展性.可靠性,资源利用率和多框架的支持上存在着明显的不足.进而诞生了下一代的MapReduce的计算框架MapReduce Version2,MRV1中有一个非常大的问题就是 ...
- 体验ArcGIS9.2的历史库功能
转自原文 体验ArcGIS9.2的历史库功能 ESRI公司于2006年11月9日全球同步发布了历史上重要的软件版本ArcGIS9.2,在该版本中,主要新增了以下四大功能(ESRI田昌莲): 第一大新功 ...
- 【cocos2dx 3.2】瓦片地图制作
使用Tiled编辑地图 每个图层仅仅能放一种瓦片 瓦片的大小最好是32*32的倍数 对象层里面设置路径的坐标 主程序中获取对象层中的坐标,做对应的操作 设置口袋精灵类: Monster.h #incl ...
- Shuttle ESB(三)——架构模型介绍(2)
上一篇文章中,介绍了Shuttle ESB架构模型中的三个重要部分. 今天,我们继续介绍剩余的三个内容:模式和消息路由. 四.模式 Request/Response(请求/响应模式) 对基于Reque ...
- PDF编译出现错误解决的方法————————【Badboy】
额 今天 在编译PDF时发现使用了一下STL中的z数值极限居然编译只是. return GetRangeConstraint(value <= std::numeric_limits::max ...
- [Postgres] Filter Data in a Postgres Table with Query Statements
We have all this data, but how do we answer questions about it? In this lesson we’ll learn how to fi ...