问题:

Python中yield关键字的作用是什么?它做了什么?

例如,我想理解以下代码

def node._get_child_candidates(self, distance, min_dist, max_dist):
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild

下面是调用者

result, candidates = list(), [self]
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

在_get_child_candidates这个函数被调用时发生了什么?返回了一个列表?还是只返回了一个元素?然后又再次被调用?什么时候调用结束?

这段代码的来源 Jochen Schulz (jrschulz), who made a great Python library for metric spaces. 完整源码链接: here


要了解yield的作用,你必须先明白什么是生成器,在此之前,你需要了解什么是可迭代对象(可迭代序列)

迭代

你可以创建一个列表,然后逐一遍历,这就是迭代

>>> mylist = [1, 2, 3]
>>> for i in mylist:
... print(i)
1
2
3

mylist是可迭代的对象,当你使用列表解析时,你创建一个列表,即一个可迭代对象

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4

任何你可用 “for… in…” 处理的都是可迭代对象:列表,字符串,文件…. 这些迭代对象非常便捷,因为你可以尽可能多地获取你想要的东西

但,当你有大量数据并把所有值放到内存时,这种处理方式可能不总是你想要的 (but you store all the values in memory and it’s not always what you want when you have a lot of values.)

生成器

生成器是迭代器,但你只能遍历它一次(iterate over them once) 因为生成器并没有将所有值放入内存中,而是实时地生成这些值

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4

这和使用列表解析地唯一区别在于使用()替代了原来的[]

注意,你不能执行for i in mygenerator第二次,因为每个生成器只能被使用一次: 计算0,并不保留结果和状态,接着计算1,然后计算4,逐一生成

yield

yield是一个关键词,类似return, 不同之处在于,yield返回的是一个生成器

>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4

这个例子并没有什么实际作用,仅说明当你知道你的函数将产生大量仅被读取一次的数据时,使用生成器将是十分有效的做法

要掌握yield,你必须明白 - 当你调用这个函数,函数中你书写的代码并没有执行。这个函数仅仅返回一个生成器对象

这有些狡猾 :-)

然后,在每次for循环使用生成器时,都会执行你的代码

然后,是比较困难的部分:

第一次函数将会从头运行,直到遇到yield,然后将返回循环的首个值. 然后,每次调用,都会执行函数中的循环一次,返回下一个值,直到没有值可以返回

当循环结束,或者不满足”if/else”条件,导致函数运行但不命中yield关键字,此时生成器被认为是空的

问题代码的解释

生成器:

# 这你你创建了node的能返回生成器的函数
def node._get_child_candidates(self, distance, min_dist, max_dist): # 这里的代码你每次使用生成器对象都会调用 # 如果node节点存在左子节点,且距离没问题,返回该节点
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild # 同理,返回右子节点
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild # 如果函数运行到这里,生成器空,该节点不存在左右节点

调用者:

# 创建一个空列表,一个包含当前候选对象引用的列表
result, candidates = list(), [self] # 当前候选非空,循环(开始时仅有一个元素)
while candidates: # 从候选列表取出最后一个元素作为当前节点
node = candidates.pop() # 获取obj和当前节点距离
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))
# 注意这里extend会反复调用获取到所有生成器返回值 return result

这段代码包含几个灵活的部分:

1.这个循环遍读取历候选列表,但过程中,候选列表不断扩展:-)

这是一种遍历嵌套数据的简明方法,虽然有些危险,你或许会陷入死循环中

在这个例子中, candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) 读取了生成器产生的所有值, 同时while循环产生新的生成器对象加入到列表,因为每个对象作用在不同节点上,所以每个生成器都将生成不同的值

2.列表方法extend() 接收一个生成器,生成器的所有值被添加到列表中

通常,我们传一个列表作为参数:

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

但是,在代码中,这个函数接受到一个生成器

这样的做法好处是:

1.你不需要重复读这些值

2.你可能有海量的子节点,但是不希望将所有节点放入内存

并且,可以这么传递生成器作为参数的原因是,Python不关心参数是一个方法还是一个列表

Python接收可迭代对象,对于字符串,列表,元组还有生成器,都适用!

这就是所谓的“鸭子类型”(duck typing), 这也是Python如此酷的原因之一, 但这是另一个问题了,对于这个问题……

你可以在这里完成阅读,或者读一点点生成器的进阶用法:

####控制一个生成器的消耗

>>> class Bank(): # let's create a bank, building ATMs
... 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 # crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # it's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # build a new one to get back in business
>>> for cash in brand_new_atm:
... print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

这在很多场景都非常有用,例如控制资源的获取

Itertools

一个很好的工具

itertools模块包含很多处理可迭代对象的具体方法. 例如

复制一个生成器?连接两个生成器?一行将嵌套列表中值分组?不使用另一个列表进行Map/Zip? (Ever wish to duplicate a generator? Chain two generators? Group values in a nested list with a one liner? Map / Zip without creating another list?)

只需要使用itertools模块

一个例子,4匹马赛跑的可能抵达顺序

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> 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()方法)

你可以获取一个迭代器的任何对象都是可迭代对象,迭代器可以让你迭代遍历一个可迭代对象(Iterators are objects that let you iterate on iterables.) [好拗口:]

更多关于这个问题的 how does the for loop work

如果你喜欢这个回答,你也许会喜欢我关于 decorators 和 metaclasses 的解释

[翻译]Python中yield的解释的更多相关文章

  1. [转]Python中yield的解释

    转自: http://python.jobbole.com/83610/ 本文作者: 伯乐在线 - wklken .未经作者许可,禁止转载!欢迎加入伯乐在线 专栏作者. 翻译 来源于stackover ...

  2. [翻译]PYTHON中如何使用*ARGS和**KWARGS

    [翻译]Python中如何使用*args和**kwargs 函数定义 函数调用 不知道有没有人翻译了,看到了,很短,顺手一翻 原文地址 入口 或者可以叫做,在Python中如何使用可变长参数列表 函数 ...

  3. Python中yield和yield from的用法

    yield python中yield的用法很像return,都是提供一个返回值,但是yield和return的最大区别在于,return一旦返回,则代码段执行结束,但是yield在返回值以后,会交出C ...

  4. Python Deque 模块使用详解,python中yield的用法详解

    Deque模块是Python标准库collections中的一项. 它提供了两端都可以操作的序列, 这意味着, 你可以在序列前后都执行添加或删除. https://blog.csdn.net/qq_3 ...

  5. Python关键字yield的解释(stackoverflow)

    3.1. 提问者的问题 Python关键字yield的作用是什么?用来干什么的? 比如,我正在试图理解下面的代码: def node._get_child_candidates(self, dista ...

  6. python中yield的用法

    ---"在python中,当你定义一个函数,使用了yield关键字时,这个函数就是一个生成器" (也就是说,只要有yield这个词出现,你在用def定义函数的时候,系统默认这就不是 ...

  7. python中yield用法

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

  8. Python中yield深入理解

    众所周知,python中的yield有这样的用法: def test(alist): for i in alist: yield i 这样,这个test函数就变成了一个生成器,当每次调用的时候,就会自 ...

  9. 关于Python中yield的一些个人见解

    # 样例代码def yield_test(n): for i in range(n): yield call(i) print("i=",i) #做一些其它的事情 print(&q ...

随机推荐

  1. CSS的color属性并非只能用于文本显示

    虽然CSS并不是一种很复杂的技术,但就算你是一个使用CSS多年的高手,仍然会有很多CSS用法/属性/属性值你从来没使用过,甚至从来没听说过. 对于CSS的color属性,相信所有Web开发人员都使用过 ...

  2. C#如何检测网络端口连接的状态

    原文:C#如何检测网络端口连接的状态 C#如何检测/监控远程连接网络端口的情况(例如:3389端口是否处于监听状态,是否建立了连接等). using System; using System.Coll ...

  3. js对象类型判断工具

    对象类型判断工具 /** *类功能:对象类型判断工具 **/ var TypeUtil = { /** *方法说明:是否是数组 **/ isArray: function (obj) {//是否是数组 ...

  4. spring和mybatis整合遇到org.springframework.beans.factory.BeanDefinitionStoreException

    今天对spring和mybatis整合进行练习,通过MapperScannerConfigurer进行mapper扫描 但是在进行单元测试的时候,死活就是报错,具体报错如下: org.springfr ...

  5. html 遮罩层以及弹出框的制作

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. CSS(前)篇

    1.1CSS重点总结 1.1.1 选择器 1.1.2 盒子模型 1.1.3 浮动 1.1.4定位 1.2CSS介绍 概念: 层叠样式表或者级联样式表(Cascading Style Sheets) 层 ...

  7. 如何把pdf文档转化为word

    在工作中常常遇到大量的pdf文档,再加工进行处理文件,特别的不方便,需要转换为WORD. 尝试如下: 使用wps自带的工具转换,提示需要是会员才能进行.否则只能进行5页以下的转换. 再想是不是又有个这 ...

  8. 微信小程序制作下来菜单

    wxml: <view class="phone_one" bindtap="clickPerson"> <view class=" ...

  9. ASP.NET+C#面试题

    1.维护数据库的完整性.一致性.你喜欢用触发器还是自写业务逻辑?为什么? 尽可能用约束(包括CHECK.主键.唯一键.外键.非空字段)实现,这种方式的效率最好:其次用触发器,这种方式可以保证无论何种业 ...

  10. Kryo官方文档-中文翻译

    Kryo作为一个优秀的Java序列化方案,在网上能找到不少测评,但未见系统的中文入门或说明文档.官方文档是最好的学习文档.虽然英文不差,但啃下来毕竟没母语来的舒服.这里抽出时间做些翻译,以方便大家查阅 ...