python中的yield
在理解yield之前,要首先明白什么是generator,在理解generator之前首先要理解可迭代的概念。
可迭代(iterables)
在你创建一个list的时候,可以逐个读取其中的元素,该逐个读取的过程称作迭代:
>>> myList=[1,2,3]
>>> for item in myList:
print item 1
2
3
>>>
如上所示,myList是可以迭代的。
当使用列表推导式(list comprehension)创建了一个列表时,它就是一个可迭代对象:
>>> myList=[x*x for x in range(3)]
>>> for item in myList:
print item 0
1
4
>>>
任何可以在其上使用for...in...语句的对象都是可迭代的,例如:lists,strings,files等等。
这些可迭代对象使用非常方便因为它能尽你所愿地读取其中的元素,但是是将所有的值存储在内存中,当它包含大量元素的时候这并不一定总是你想要的。
生成器(Generators)
生成器是可以迭代的,但是只能迭代一次,因为他们并不是将所有的内容放在内存中。而是实时地生成(on the fly):
>>> myGenerator=(x*x for x in range(3))
>>> for item in myGenerator:
print item 0
1
4
>>>
这里虽然只是使用了()代替了[],但是你不可以使用for item in myGenerator进行第二次迭代。
因为myGenerator是迭代器,只能使用一次。先算出0,然后忘记0再算出1,然后忘记1再算出4,如此一个接着一个进行计算。
>>> myList=[1,2,3]
>>> for item in myList:
print item 1
2
3
>>> myGenerator=(x*x for x in range(3))
>>> for item in myGenerator:
print item 0
1
4
>>> for item in myList:
print item 1
2
3
>>> for item in myGenerator:
print item >>>
yield
yield是类似return的关键字,只不过函数返回的是generator。
>>> def createGenerator():
myList = range(3)
for item in myList:
yield item*item >>> myGenerator=createGenerator() # create a generator
>>> print myGenerator
<generator object createGenerator at 0x0000000002B20CA8> # mygenerator is an object!
>>> for item in myGenerator:
print item 0
1
4
>>>
这个列子用途并不大,但是当你知道你的函数将返回很大一批数量的值并且只需要读取一次的时候就很方便了。
为了弄懂yield,你要明白:当你调用函数的时候,函数体中的代码不会被运行;函数只是返回一个生成器(generator)对象。
在for语句部分,使用生成器时,函数体中的代码每次都会被运行。
for第一次调用生成器对象时,代码将会从函数的开始处运行直到遇到yield为止,然后返回此次循环的第一个值,接着循环地执行函数体,返回下一个值,直到没有值返回为止。
一旦函数运行再也没有遇到yield时,生成器就被认为是空的。这有可能是因为循环终止,或者因为没有满足任何if/else。
代码示例
生成器(generator):
#创建node对象的方法_get_child_candidates(),该方法会返回generator
def node._get_child_candidates(self, distance, min_dist, max_dist):
#每次使用生成器对象的时候都会调用的代码 #如果node对象还有左孩子(left child)并且distance是符合要求的,返回下一个child
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild #如果node对象还有右孩子(right child)并且distance是符合要求的,返回下一个child
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
#函数运行到这里,生成器就会被认为是空的,已经没有children存在
调用器(caller):
#创建一个空的列表、一个包含当前对象的引用的列表
result, candidates = list(), [self] #在candidates上循环 (一开始只包含一个元素)
while candidates:
#获取最后一个候选者并从列表中移除
node = candidates.pop() #获取对象和候选者之间的距离
distance = node._get_dist(obj) #如果距离合适,加入结果中
if distance <= max_dist and distance >= min_dist:
result.extend(node._values) #将候选者的孩子加入候选者列表
#循环会继续直到所有孩子都检查完毕
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
这个代码包含了几个小部分:
-我们对一个链表进行迭代,但是迭代中链表还在不断的扩展。它是一个迭代这些嵌套的数据的简洁方式,即使这样有点危险,因为可能导致无限迭代。 candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) 穷尽了生成器的所有值,但 while 不断地在产生新的生成器,它们会产生和上一次不一样的值,既然没有作用到同一个节点上.
-extend() 是一个迭代器方法,作用于迭代器,并把参数追加到迭代器的后面。
通常我们传给它一个链表参数:
>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]
但是在代码中的是一个生成器,这是很好的,因为:
-不必读两次所有的值
-可以有很多children,但不必叫他们都存储在内存里面。
并且这很有效,因为Python不关心一个方法的参数是不是个列表。Python只希望它是个可以迭代的,所以这个参数可以是列表,元组,字符串,生成器...
这叫做 duck typing,这也是为何Python如此棒的原因之一,但这已经是另外一个问题了...
你可以在这里停下,来看看生成器的一些高级用法:
控制生成器的耗尽
>>> class Bank(): # 创建银行,构建ATM机,只要没有危机,就可以不断地每次从中取100
... crisis = False
... def create_atm(self):
... while not self.crisis:
... yield "$100"
>>> hsbc = Bank() # when everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # 危机来临,没有更多的钱了
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # 即使创建一个新的ATM,银行还是没钱
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # 危机过后,银行还是空的,因为该函数之前已经不满足while条件
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # 必须构建一个新的atm,恢复取钱业务
>>> for cash in brand_new_atm:
... print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...
对于类似资源的访问控制等场景,生成器显得很实用。
Itertools,很实用的工具
Itertools模块包含特殊的函数,用于操作可迭代对象。
可否想过复制一个生成器?把两个生成器进行连接?在内嵌列表中一行代码处理分组?不会创建另外一个列表的Map/Zip函数?
你要做的就是import itertools 。
举个例子,我们来看看4匹马赛跑到达终点所有可能的顺序:
>>> import itertools
>>> horses=[1,2,3,4]
>>> races=itertools.permutations(horses)
>>> print races
<itertools.permutations object at 0x0000000002F09A40>
>>> print list(itertools.permutations(horses))
[(1, 2, 3, 4), (1, 2, 4, 3), (1, 3, 2, 4), (1, 3, 4, 2), (1, 4, 2, 3), (1, 4, 3, 2), (2, 1, 3, 4), (2, 1, 4, 3), (2, 3, 1, 4), (2, 3, 4, 1), (2, 4, 1, 3), (2, 4, 3, 1), (3, 1, 2, 4), (3, 1, 4, 2), (3, 2, 1, 4), (3, 2, 4, 1), (3, 4, 1, 2), (3, 4, 2, 1), (4, 1, 2, 3), (4, 1, 3, 2), (4, 2, 1, 3), (4, 2, 3, 1), (4, 3, 1, 2), (4, 3, 2, 1)]
>>>
迭代器的内部机理
迭代是一个实现可迭代对象(实现的是 __iter__() 方法)和迭代器(实现的是 __next__() 方法)的过程。
可迭代对象是你可以从其获取到一个迭代器的任一对象。迭代器是那些允许你迭代可迭代对象的对象。
python中的yield的更多相关文章
- 关于Python中的yield
关于Python中的yield 在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,f ...
- 深入理解Python中的yield和send
send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互. 但是需要注意,在一个生成器对象没有执行next方法之前,由 ...
- [转]关于Python中的yield
在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何 ...
- 【转载】关于Python中的yield
在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何 ...
- 关于Python中的yield(转载)
您可能听说过,带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ? 我们先抛开 generator,以一个常见的编程题目来展示 yield ...
- Python中的yield生成器的简单介绍
Python yield 使用浅析(整理自:廖 雪峰, 软件工程师, HP 2012 年 11 月 22 日 ) 初学 Python 的开发者经常会发现很多 Python 函数中用到了 yield 关 ...
- Python中的yield和Generators(生成器)
本文目的 解释yield关键字到底是什么,为什么它是有用的,以及如何来使用它. 协程与子例程 我们调用一个普通的Python函数时,一般是从函数的第一行代码开始执行,结束于return语句.异常或者函 ...
- python 中的 yield 究竟为何物?生成器和迭代器的区别?
当你突然看到别人的代码中出现了一个好像见过但又没用过的关键词 比如 yield ,你是否会觉得这段代码真是高大上呢? 或许只有我这种小白才会这样子觉得,就在刚刚,我就看见了别人的代码中的yield,觉 ...
- 解析Python中的yield关键字
前言 python中有一个非常有用的语法叫做生成器,所利用到的关键字就是yield.有效利用生成器这个工具可以有效地节约系统资源,避免不必要的内存占用. 一段代码 def fun(): for i i ...
随机推荐
- DS实验题 地鼠安家
★实验任务 fd是一个公认的美丽校园.一天,fd来了一群地鼠,编号为1到n,他们希望在这里定居.现在先由第一只地鼠往下打一个单位的距离,并且在那里安家.对于每一个已经安家的地鼠,如果他左下或右下没有邻 ...
- SnapKit代码约束
let label = UILabel() label.frame = CGRectMake(, , , ) label.backgroundColor = UIColor.cyanColor() l ...
- Android 通过网页打开自己的APP(scheme)
Android 通过网页打开自己的APP(scheme) 分类: android2014-07-09 17:35 8565人阅读 评论(2) 收藏 举报 通过用手机的浏览器(内置,第三方都可)访问一个 ...
- PROTEL99SE封装说明
PROTEL99SE封装说明 路板上的焊盘间的距离也就是300mil(因为在电机领域里,是以英制单位为主的.同样的, 对于无极性的电容,RAD0.1-RAD0.4 也是一样:对有极性的电容如电解电容 ...
- 正则基础之 \b 单词边界
本文转载自: http://www.jb51.net/article/19330.htm 1概述 “\b”匹配单词边界,不匹配任何字符. “\b”匹配的只是一个位置,这个位置的一侧是构成单词的字符,另 ...
- [转]C#之反射
前言 之所以要写这篇关于C#反射的随笔,起因有两个: 第一个是自己开发的网站需要用到 其次就是没看到这方面比较好的文章. 所以下定决心自己写一篇,废话不多说开始进入正题. 前期准备 在VS20 ...
- 低功耗蓝牙4.0BLE编程-nrf51822开发(4)
蓝牙是一种短距离的通讯方式,它设计的意图是取代电子便携设备之间的有线电缆连接.蓝牙的主要特性是健壮性.低功耗.成本低,它工作于免费的2.4无线传输频段. 蓝牙有两种技术系统:基本速率Basic Rat ...
- Qt 之 自定义提示信息框—迅雷风格(模拟QDialog类的exec()方法) good
http://blog.csdn.net/goforwardtostep/article/details/53614830
- 【Java 基础篇】【第三课】表达式、控制结构
这两天再看敏捷开发流程,我这个算是敏捷博客吗? 哈哈o(∩_∩)o package a.b; public class Three { static void Expression() { Syste ...
- 关于PushKit的使用总结
1.PushKit的认识 (1)概念 ios8苹果新引入了名为pushkit的框架和一种新的push通知类型,被称作voip push.该push方式旨在提供区别于普通apns push的能力,通过这 ...