Python中的yield关键字

这是stackoverflow上一个关于yield关键字的问题以及它被推荐次数最高的一个答案

问题:

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

例如,下面的这份代码:

def _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 = [], [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对象被返回了?还是一个单独的元素?或者是它再次被调用了?这个调用什么时候会结束呢?

答案

在理解yield关键字之前,需要先理什么是迭代器(iterables)

迭代器(Iterables)

当创建一个列表后,你就可以一个接着一个地读取它的元素, 这种操作就叫做迭代(iteration):

>>> 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 ...语法的对象,都是一个可迭代对象: 例如 lists、string、file等等

由于可以随意地读取这些对象中的元素,这些可迭代对象使用起来非常方便,但是这样做会把可迭代对象所有的值都存在内存中,当有很多个元素的时候,这样做可能并不合适。

生成器(Generators)

生成器是可以迭代的,但它是一个只可以读取一次的迭代器。生成器并不会把所有的值都存在内存中,而是实时地生成数据

>>> mygenerator = (x*x for x in range(3))

>>> for i in mygenerator:

... print(i)

0

1

4

除了把[]换成()之外,生成器看起来和列表推导式没有什么不同。但是,调用之后,你不能再次使用for i in mygenerator,因为生成器只可以被迭代一次:它计算出0,然后计算出1,同时丢弃掉0,然后最终计算出4,丢弃掉1,一个接着一个。

Yield 关键字

yield是一个类似与return的关键字,只是这个函数会返回的是一个生成器(Generator)

>>> def createGenerator():

... mylist = range(3)

... for i in mylist:

... yield i*i

...

>>> mygenerator = createGenerator() # create a generator

>>> print(mygenerator) # mygenerator is an object!

>>> for i in mygenerator:

... print(i)

0

1

4

这个例子并没有什么用处,但是,我们可以看出,createGenerator函数会返回一大批你只需要读取一次的值。

为了理解yield,你必须理解: 当你调用这个函数时,你写在函数体内部的代码并没有运行。这个函数只是返回了一个生成器对象(这可能有一点tricky)。

然后,每次当你使用for进行迭代的时候,你的代码才会执行。

接下来是关键的部分:

使用for第一次调用从你的函数中创建出的生成器对象时,将会执行从函数起始位置到yield所在位置的代码并返回这个循环(函数内部定义)的第一个值。随后的每一次调用(for)都会继续执行你的函数内部的下一轮循环,并返回下一个值,直到没有值可以返回为止。

原文:

The first time the for calls the generator object created from your function, it will run the code in your function from the beginning until it hits yield, then it'll return the first value of the loop. Then, each other call will run the loop you have written in the function one more time, and return the next value, until there is no value to return.

如果生成器内部没有yield关键字,那么这个生成器将会被认为是空的。这可能是因为循环结束了,或者是没有满足if/else判断条件

代码解析

生成器

# Here you create the method of the node object that will return the generator

# 在node对象中创建一个返回生成器的方法

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 child

# 如果节点有下一个左孩子,并且距离是符合要求的,返回下一个左孩子

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

# 如果节点有下一个右孩子,并且距离是符合要求的,返回下一个右孩子

if 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

# 当函数走到这里时,生成器将被认为是空的

# 这个节点最多只有一个子节点

调用者

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

# 在candidates中循环(在初始时值含有一个元素。根节点)

while candidates:

# Get the last candidate and remove it from the list

# 获取最近的一个candidate并将它从列表中移除

node = candidates.pop()

# Get the distance between obj and the candidate

# 获取obj和candidate的距离

distance = node._get_dist(obj)

# If distance is ok, then you can fill the result

# 如果距离符合要求,填入result中

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

# 将candidates列表中candidate的子节点添加到candidates列表中

# 以确保遍历了candidate所有的子节点以及子节点的子节点。。。

candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

return result

这份代码包含了几个巧妙的地方:

我们对一个列表进行迭代的同时,列表还在不断地扩展。尽管这样做有可能导致无限迭代,但是它是一个简单的遍历所有数据的方式。在这个case中,candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))穷尽了生成器的所有值,但是while循环一直在创建新的生成器对象,由于传入的节点不同,这些生成器对象会产生不同的值。

extend() 是一个迭代器方法,作用于迭代器本身并把值添加到列表中

通常,我们会传一个list参数

>>> a = [1, 2]

>>> b = [3, 4]

>>> a.extend(b)

>>> print(a)

[1, 2, 3, 4]

但是,上面的代码是一个生成器,这样做很巧妙,因为:

你不需要读取每个值两次

你可以有很多子对象,但是不必将他们都存储在内存里面

这份代码是可以运行的,因为Python并不关心方法的参数是否是一个list对象,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())

>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs

>>> print(wall_street_atm.next())

>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty

>>> print(corner_street_atm.next())

>>> 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模块包含了很多特殊的迭代方法,你是否曾经想过复制一个迭代器?链接两个迭代器?将嵌套的列表分组?执行不创建新列表的zip/map操作?

所有的这些,只需要import itertools

来一个例子?让我们看看比赛中4匹马到达终点的先后顺序的所有可能情况:

>>> horses = [1, 2, 3, 4]

>>> races = itertools.permutations(horses)

>>> print(races)

>>> 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__() 方法)的过程。可迭代对象是通过它可以获取到一个迭代器的任一对象。迭代器是那些允许你迭代可迭代对象的对象。

更多细节可以阅读 http://effbot.org/zone/python-for-statement.htm

python function if yield_Python中的yield关键字相关推荐

  1. python里的关键字有哪些_Python中的yield关键字做了什么?

    Python中的yield关键字做了什么 要理解yield做了什么,就必须明白生成器(generators)为何物,而在明白生成器之前还要知道迭代器(iterables). 1.迭代器 当我们创建一个 ...

  2. Python中的yield关键字及表达式、生成器、生成器迭代器、生成器表达式详解

    文章目录 1. yield关键字及表达式.生成器.生成器迭代器.生成器表达式 1.1 yield关键字及表达式(yield expression) 1.1.1 yield关键字 1.1.2 yield ...

  3. python3 yield和iter的区别_Python中的yield关键字

    Yield? 总的来说,yield关键字和return关键字有相似之处,但其工作机制却大相径庭.想要完整地理解yield的工作机制,我们首先需要明白什么是generator.而为了明白什么是gener ...

  4. Java yield详解_Java 中的 yield 关键字

    从 Java 14 开始,yield 关键字已添加到 Java 语言中,用于实现 switch 表达式. 它用于从 switch 表达式中的 case 返回值. 例如: int x = switch ...

  5. Python检查Word文件中包含特定关键字的所有页码

    推荐教材:<Python程序设计基础与应用>(ISBN:9787111606178),董付国,机械工业出版社 图书详情: 配套资源: 用书教师可以联系董老师获取教学大纲.课件.源码.教案. ...

  6. Python查找文件夹中含有指定关键字的文件

    查找文件夹中含有指定关键字的文件,并将该文件复制到另一个文件夹中 ''' 查找文件夹中含有指定关键字的文件,并将该文件复制到另一个文件夹中 ''' import os import shutildef ...

  7. python 斐波那契数列 yield_Python中的yield到底是个什么鬼?

    Python大数据分析 记录 分享 成长 相信你已经不止一次在函数中看到关键词,它起着什么作用?返回什么?和又有着什么区别呢?这篇文章将会揭开的神秘面纱,并给出最浅显易懂的例子. 关键字做了什么? 如 ...

  8. python function at 0x00000_Python 中的函数装饰器和闭包

    本文是<流畅的Python>第7章的学习笔记. 函数装饰器可以被用于增强方法的某些行为,如果想自己实现装饰器,则必须了解闭包的概念. 装饰器的基本概念 装饰器是一个可调用对象,它的参数是另 ...

  9. python中关键字参数含义_python中接受任意关键字的参数

    1.*args args是非关键字参数,可以理解为形参,为了方便记忆我理解它是arguments的缩写. 2.*kwargs kwargs是键值对参数,为了方便记忆我理解它是key word argu ...

最新文章

  1. 12 生成器和生成器函数以及各种推导式
  2. python可变参数的特点_可变参数**kwargs传入函数时的存储方式为( )_学小易找答案...
  3. Nacos OPEN API配置管理测试
  4. 【图像处理】——Python图像分割边缘检测算法之一阶梯度算子(Roberts、Prewitt、Sobel、 Kirsch、Canny算子)
  5. python3 上传文件到目标机器_通过python模块实现服务器和本地机器之间快速拷贝文件...
  6. Facebook 正在大规模重构 React Native
  7. Team Foundation Server 2010 安装、部署与配置(三):安装 .
  8. 维护人员工具_软件项目管理:软件工具与开发环境相关知识介绍
  9. 最小生成树:Kruskal算法 和 Prim算法(第23章)
  10. ICANN总裁Fadi Chehadé:IPv6峰会全面推动了IPv6在中国的发展与产业落地
  11. Eclipse配置Hadoop开发环境
  12. 教你如何进行苹果屏幕解锁
  13. java 冒泡排序详解_「图文+动画」超级详细详解冒泡排序
  14. 开启迅盘:ReadyBoost和ReadyDrive的开启方法
  15. POJ 1862 Stripies 贪心
  16. 第二章使用VLAN隔离广播域
  17. java编程将HTML文件转换成PDF文件
  18. Linux CentOS 7 搭建DNS域名服务器
  19. 网络编程、正则表达式
  20. 瞧一瞧,看一看,走过路过不要错过。 关于5G

热门文章

  1. SQL中变量的赋值-select
  2. hdu 4888 最大流慢板
  3. sql 存储过程 分页
  4. ASP.NET程序中常用代码汇总(一)
  5. VS2010 自定义用户控件未出现在工具箱的解决方案
  6. mono-3.4.0 源码安装时出现的问题 [do-install] Error 2 [install-pcl-targets] Error 1 解决方法
  7. 给js加版本号解决浏览器缓存问题
  8. 服务器异常代码413问题
  9. docker启动elasticsearch失败--jvm内存不足解决方案
  10. Go的异常处理 defer, panic, recover