关注上方深度学习技术前沿”,选择“星标公众号”

资源干货,第一时间送达!

作者:grisse    链接:

https://segmentfault.com/a/1190000017405045

这是stackoverflow上一个关于python中yield用法的帖子,这里翻译自投票最高的一个回答,原文链接:

https://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do

问题

Python中yield关键字的用途是什么?它有什么作用?
例如,我试图理解以下代码:

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

这是调用者(caller):

result, candidates = [], [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时会发生什么?返回了一个列表(list)?还是返回了一个元素?然后被重复调用了吗?调用何时结束?

代码来自 Jochen Schulz (jrschulz), who made a great Python library for metric spaces.

回答

要想理解yield的作用,你必须了解什么是生成器(generators),在这之前,我们先来看可迭代对象(iterables)。

可迭代对象 (iterables)

当你创建了一个列表,你可以遍历这个列表读取它的每一个元素,逐个读取列表元素称为迭代(iteration)。

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

mylist就是一个可迭代对象(iterable)。当你使用列表生成式(list comprehension)创建一个列表(list),即创建了一个可迭代对象。

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

可以使用for... in...的所有对象都是可迭代对象:列表(lists)、字符串、文件...
这些可迭代对象使用很方便,因为你可以根据需要如你所愿的读取其中的元素。但是,当你有大量数据时把所有值都存储在内存中,这样往往不是你想要的( but you store all the values in memory and this is not always what you want when you have a lot of values.)。

生成器 (Generators)

生成器是迭代器(iterators),但是只能迭代一次,生成器不会将所有值存储在内存中,而是实时的生成这些值:

>>> 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 的关键字,不同的是这个函数将返回一个生成器。

>>> 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

这个例子没有什么实际作用。但是当你知道你的函数将返回大量你只需要读取一次的值时,使用生成器是一个有效的做法。

要掌握 yeild,你必须要知道当你调用这个函数时,你在函数体中编写的代码并没有立马执行
该函数仅仅返回一个生成器对象,这有点棘手 :-)

然后,你的代码将从for循环每次使用生成器停止的位置继续执行。

现在到了关键部分:

for第一次调用从函数创建的生成器对象,函数将从头开始执行直到遇到yeild,然后返回yield后的值作为第一次迭代的返回值。接下来每次调用都会再次执行你在函数中定义的循环,并返回(return)下一个值,直到没有值可以返回(return)。

当循环结束,或者不满足if/else条件,导致函数运行但不会执行(not hit)yeild,此时生成器被认为是空的。

问题代码的解释 (Your code explained)

生成器 (Generator):

# Here you create the method of the node object that will return the generator
def _get_child_candidates(self, distance, min_dist, max_dist):# Here is the code that will be called each time you use the generator object:# If there is still a child of the node object on its left# AND if distance is ok, return the next childif self._leftchild and distance - max_dist < self._median:yield self._leftchild# If there is still a child of the node object on its right# AND if distance is ok, return the next childif self._rightchild and distance + max_dist >= self._median:yield self._rightchild# If the function arrives here, the generator will be considered empty# there is no more than two values: the left and the right children

调用者 (Caller):

# Create an empty list and a list with the current object reference
result, candidates = list(), [self]# Loop on candidates (they contain only one element at the beginning)
while candidates:# Get the last candidate and remove it from the listnode = candidates.pop()# Get the distance between obj and the candidatedistance = node._get_dist(obj)# If distance is ok, then you can fill the resultif distance <= max_dist and distance >= min_dist:result.extend(node._values)# Add the children of the candidate in the candidates list# so the loop will keep running until it will have looked# at all the children of the children of the children, etc. of the candidatecandidates.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()是一个列表(list)对象的方法,作用于可迭代对象(iterable),并将其值添加到列表里。

通常,通常我们将列表作为参数传递给它:

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

但是在你的代码里它接收到的是一个生成器(generator),这很好,因为:

  1. 你不必重复读取这些值

  2. 你可以有很多子对象,但不需要将它们都存储在内存里。

它很有效,因为Python不关心一个方法的参数是否是列表,Python只希望他是一个可迭代对象,所以这个参数可以是列表,元组,字符串和生成器!这就是所谓的duck typing ,这也是Python为何如此酷的原因之一,但这已经是另外一个问题了......

你可以在这里停下,来看一些生成器的高级用法:

控制生成器的穷尽 (Controlling a generator exhaustion)

>>> 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 # The 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
...

注意,对于Python 3,请使用 print(corner_street_atm.__next__()) 或者 print(next(corner_street_atm))

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

Itertools,你最好的朋友 (Itertools, your best friend)

itertools模块包含很多处理可迭代对象的特殊方法。曾经想要复制一个生成器吗?连接两个生成器?用一行代码将嵌套列表中的值进行分组?不创建另一个列表进行Map/Zip

只需要import 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)]

了解迭代的内部机制 (Understanding the inner mechanisms of iteration)

迭代是一个实现可迭代对象(实现的是 __iter__() 方法)和迭代器(实现的是 __next__() 方法)的过程。你可以获取一个迭代器的任何对象都是可迭代对象,迭代器可以让你迭代遍历一个可迭代对象(Iterators are objects that let you iterate on iterables.) .

在这篇文章中有关于for循环如何工作的更多信息:

http://effbot.org/zone/python-for-statement.htm

Python必须要掌握的高端语法相关推荐

  1. 成为Python大牛必须要掌握的高端语法(附链接代码)

    来源:机器学习算法与Python实战 本文约3000字,建议阅读5分钟 必要掌握的高端语法助你成为Python大牛. 这是stackoverflow上一个关于python中yield用法的帖子,这里翻 ...

  2. node函数 python_成为Python大牛必须要掌握的高端语法(附链接代码)

    来源:机器学习算法与Python实战 本文约3000字,建议阅读5分钟 必要掌握的高端语法助你成为Python大牛. 这是stackoverflow上一个关于python中yield用法的帖子,这里翻 ...

  3. python大牛_成为Python大牛必须要掌握的高端语法——yield

    成为Python大牛必须要掌握的高端语法--yield 1. 什么是yield 在介绍yield语法之前,首先要向大家说明Python中的迭代(iteration).可迭代(iterable).迭代器 ...

  4. python function if yield_成为Python大牛必须要掌握的高端语法——yield!

    这是stackoverflow上一个关于python中yield用法的帖子,这里翻译自投票最高的一个回答,原文链接: 问题 Python中yield关键字的用途是什么?它有什么作用? 例如,我试图理解 ...

  5. linux+python高端运维班2017年1月课程及服务全新升级!

    老男孩IT教育是唯一一个和51CTO学院战略合作达到在线开班15期以上(近千名学员)以上的培训机构. 老男孩教育始终坚持以"不能让学员高薪就业的培训机构都是耍流氓"为标准开展教学任 ...

  6. 十行Python以内代码能有什么高端操作?

    作者 | ZackSock 来源 | CSDN博客 Python凭借其简洁的代码,赢得了许多开发者的喜爱.因此也就促使了更多开发者用Python开发新的模块,从而形成良性循环,Python可以凭借更加 ...

  7. Python 10 行以内代码能有什么高端操作?| 原力计划

    作者 | ZackSock 来源 | CSDN博客 Python凭借其简洁的代码,赢得了许多开发者的喜爱.因此也就促使了更多开发者用Python开发新的模块,从而形成良性循环,Python可以凭借更加 ...

  8. python高端实现各国GDP动态轮换图

    python高端实现各国GDP动态轮换图 文章目录 python高端实现各国GDP动态轮换图 前言效果 一.准备数据等文件 二.完整代码 1.准备showGDP.py 2.准备PlotUtil.py ...

  9. 10 行以下 Python 代码,能有什么高端操作?

    点击上方蓝色小字,关注"涛哥聊Python" 重磅干货,第一时间送达 来源:ZackSock Python凭借其简洁的代码,赢得了许多开发者的喜爱.因此也就促使了更多开发者用Pyt ...

最新文章

  1. 如何衡量RFID技术在仓库中的价值?
  2. python保存图片到指定路径_python将处理好的图像保存到指定目录下的方法
  3. mini2440 裸机编程 -led
  4. 常见的复杂网络模型都有哪些?
  5. hdu 4090(搜索+可行性剪枝)
  6. Myeclipse2013下载,安装,破解,介绍(CSDN首发)
  7. 如何将BeanDefinition注册到IoC容器?
  8. tar ------ linux解压 tar命令
  9. dataframe类型数据的遍历_Python零基础入门到爬虫再到数据分析,这些你都是要学会的...
  10. (免费领取名企Java面试题)volatile作用,指令重排相关
  11. Android----获取包名和sh1
  12. js闭包,这个算是比较通俗的了(转)
  13. 转:用Winform实现屏幕小键盘
  14. linux有名管道 复用,Linux进程间通信(九)---综合实验之有名管道通信实验
  15. 计算机最大化快捷键,最大化窗口快捷键,mac窗口最大化快捷键
  16. 怎么用计算机直接截图,电脑怎样截图又快又方便 1分钟教你如何快速截图
  17. 上海大学计算机学院领军人物,上海大学计算机工程与科学学院研究生导师简介-谢 江高级工程师...
  18. Ubuntu搭建BT服务器FTP服务器发布种子
  19. 全球与中国具有集成保护功能的共模滤波器(CMF)市场深度研究分析报告
  20. [HNOI2007]紧急疏散evacuate

热门文章

  1. 查看数据库中有哪些活动的事务,对应的会话id,执行的语句
  2. 信息上传服务器加速cpu处理,英特尔发布全新第二代至强可扩展处理器携手浪潮加速新型应用发展...
  3. java里面怎么添加表约束_mysql给表增加约束条件
  4. 带有BERT模型代码的BILSTM+BERT+CRF
  5. Jenkins部署SpringBoot应用到远程服务器
  6. 上交张伟楠副教授:基于模型的强化学习算法,基本原理以及前沿进展(附视频)
  7. 干货!神经网络原来是这样和数学挂钩的
  8. stylegan2 示例命令fused_bias_act.cu环境配置异常(无法打开包括文件: “tensorflow/core/framework/op.h”
  9. Python2.x与3​​.x版本区别
  10. Michael Brostein 最新几何深度学习综述:超越 WL 和原始消息传递的 GNN