在理解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的更多相关文章

  1. 关于Python中的yield

    关于Python中的yield   在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,f ...

  2. 深入理解Python中的yield和send

    send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互. 但是需要注意,在一个生成器对象没有执行next方法之前,由 ...

  3. [转]关于Python中的yield

    在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何 ...

  4. 【转载】关于Python中的yield

    在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何 ...

  5. 关于Python中的yield(转载)

    您可能听说过,带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ? 我们先抛开 generator,以一个常见的编程题目来展示 yield ...

  6. Python中的yield生成器的简单介绍

    Python yield 使用浅析(整理自:廖 雪峰, 软件工程师, HP 2012 年 11 月 22 日 ) 初学 Python 的开发者经常会发现很多 Python 函数中用到了 yield 关 ...

  7. Python中的yield和Generators(生成器)

    本文目的 解释yield关键字到底是什么,为什么它是有用的,以及如何来使用它. 协程与子例程 我们调用一个普通的Python函数时,一般是从函数的第一行代码开始执行,结束于return语句.异常或者函 ...

  8. python 中的 yield 究竟为何物?生成器和迭代器的区别?

    当你突然看到别人的代码中出现了一个好像见过但又没用过的关键词 比如 yield ,你是否会觉得这段代码真是高大上呢? 或许只有我这种小白才会这样子觉得,就在刚刚,我就看见了别人的代码中的yield,觉 ...

  9. 解析Python中的yield关键字

    前言 python中有一个非常有用的语法叫做生成器,所利用到的关键字就是yield.有效利用生成器这个工具可以有效地节约系统资源,避免不必要的内存占用. 一段代码 def fun(): for i i ...

随机推荐

  1. number_format函数的使用

    <!-- ###:表示传入的价格,2:保留两位小数,'.':用点区分, --> <!-- 该函数只支持1.2.4个参数.不能只写3个参数 --> <span>< ...

  2. IMP-00009: 导出文件异常结束

    今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到" IMP-00009: 导出文件异常结束" 错误,google一下,发现可能有如下原因导致 imp的数据太大,没有 ...

  3. HOSTS文件详解【win|mac】

    hosts文件是一个用于储存计算机网络中各节点信息的计算机文件.这个文件负责将主机名映射到相应的IP地址. hosts文件通常用于补充或取代网络中DNS的功能.和DNS不同的是,计算机的使用者可以直接 ...

  4. single-chip microcomputer Microcontroller 单片机 单片微型计算机 微控制器

    https://zh.wikipedia.org/wiki/单片机 单片机,全称单片微型计算机(英语:single-chip microcomputer),又称微控制器(microcontroller ...

  5. Python For Data Analysis -- Pandas

    首先pandas的作者就是这本书的作者 对于Numpy,我们处理的对象是矩阵 pandas是基于numpy进行封装的,pandas的处理对象是二维表(tabular, spreadsheet-like ...

  6. MySQL问题汇总(持续更新)

    1.This function has none of DETERMINISTIC, NO SQL 原因: Mysql如果开启了bin-log, 我们就必须指定我们的函数是否是 1 DETERMINI ...

  7. JAVA Callable

    Listing -. Calculating Euler’s Number e import java.math.BigDecimal; import java.math.MathContext; i ...

  8. Eclipse反编译插件JadEclipse 【转】

    JAVA的反编译插件JadEclipse,官网地址为:http://java.decompiler.free.fr/ 这里面有3个,Jad-Gui大家都知道是一个单独的可执行程序,把要反编绎的jar直 ...

  9. window对象;document对象;

    window对象: DOM:文档对象模型 --树模型文档:标签文档,对象:文档中每个元素对象,模型:抽象化的东西 一:window: 属性(值或者子对象):opener:打开当前窗口的源窗口,如果当前 ...

  10. Solr4.3之检索建议suggest

    原文链接:http://www.656463.com/article/Efm26v.htm 很多才学solr的人,都容易把solr spellcheck和solr suggest混淆,误以为他们是一样 ...