python中yield的用法详解——最简单,最清晰的解释

首先,如果你还没有对yield有个初步分认识,那么你先把yield看做“return”,这个是直观的,它首先是个return,普通的return是什么意思,就是在程序中返回某个值,返回之后程序就不再往下运行了。看做return之后再把它看做一个是生成器(generator)的一部分(带yield的函数才是真正的迭代器),好了,如果你对这些不明白的话,那先把yield看做return,然后直接看下面的程序,你就会明白yield的全部意思了:

def foo():print("starting...")while True:res = yield 4print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(next(g))

就这么简单的几行代码就让你明白什么是yield,代码的输出这个:

starting...
4
********************
res: None
4

 

我直接解释代码运行顺序,相当于代码单步调试:

1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)

2.直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环

3.程序遇到yield关键字,然后把yield想想成return,return了一个4之后,程序停止,并没有执行赋值给res操作,此时next(g)语句执行完成,所以输出的前两行(第一个是while上面的print的结果,第二个是return出的结果)是执行print(next(g))的结果,

4.程序执行print("*"*20),输出20个*

5.又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候res赋值是None,所以接着下面的输出就是res:None,

6.程序会继续在while里执行,又一次碰到yield,这个时候同样return 出4,然后程序停止,print函数输出的4就是这次return出的4.

 

到这里你可能就明白yield和return的关系和区别了,带yield的函数是一个生成器,而不是一个函数了,这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步停止的地方开始,然后遇到yield后,return出要生成的数,此步就结束。

def foo():print("starting...")while True:res = yield 4print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(g.send(7))

再看一个这个生成器的send函数的例子,这个例子就把上面那个例子的最后一行换掉了,输出结果:

starting...
4
********************
res: 7
4

先大致说一下send函数的概念:此时你应该注意到上面那个的紫色的字,还有上面那个res的值为什么是None,这个变成了7,到底为什么,这是因为,send是发送一个参数给res的,因为上面讲到,return的时候,并没有把4赋值给res,下次执行的时候只好继续执行赋值操作,只好赋值为None了,而如果用send的话,开始执行的时候,先接着上一次(return 4之后)执行,先把7赋值给了res,然后执行next的作用,遇见下一回的yield,return出结果后结束。

 

5.程序执行g.send(7),程序会从yield关键字那一行继续向下运行,send会把7这个值赋值给res变量

6.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环

7.程序执行再次遇到yield关键字,yield会返回后面的值后,程序再次暂停,直到再次调用next方法或send方法。

这就结束了,说一下,为什么用这个生成器,是因为如果用List的话,会占用更大的空间,比如说取0,1,2,3,4,5,6............1000

你可能会这样:

for n in range(1000):a=n

这个时候range(1000)就默认生成一个含有1000个数的list了,所以很占内存。

这个时候你可以用刚才的yield组合成生成器进行实现,也可以用xrange(1000)这个生成器实现

yield组合:

def foo(num):print("starting...")while num<10:num=num+1yield num
for n in foo(0):print(n)

输出:

starting...
1
2
3
4
5
6
7
8
9
10

xrange(1000):

for n in xrange(1000):a=n

 其中要注意的是python3时已经没有xrange()了,在python3中,range()就是xrange()了,你可以在python3中查看range()的类型,它已经是个<class 'range'>了,而不是一个list了,毕竟这个是需要优化的。

转载自https://blog.csdn.net/mieleizhi0522/article/details/82142856 

3. (译)Python关键字yield的解释(stackoverflow)¶

译者:hit9
原文:http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained
译者注:这是stackoverflow上一个很热的帖子,这里是投票最高的一个答案

Contents

  • (译)Python关键字yield的解释(stackoverflow)
    • 提问者的问题
    • 回答部分
    • 可迭代对象
    • 生成器
    • yield关键字
    • 回到你的代码
    • 控制生成器的穷尽
    • Itertools,你最好的朋友
    • 了解迭代器的内部机理

3.1. 提问者的问题¶

Python关键字yield的作用是什么?用来干什么的?

比如,我正在试图理解下面的代码:

def node._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

下面的是调用:

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 的时候发生了什么?返回了一个列表?返回了一个元素?被重复调用了么? 什么时候这个调用结束呢?

3.2. 回答部分¶

为了理解什么是 yield,你必须理解什么是生成器。在理解生成器之前,让我们先走近迭代。

3.3. 可迭代对象¶

当你建立了一个列表,你可以逐项地读取这个列表,这叫做一个可迭代对象:

>>> 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 .. 语法的叫做一个迭代器:列表,字符串,文件……你经常使用它们是因为你可以如你所愿的读取其中的元素,但是你把所有的值都存储到了内存中,如果你有大量数据的话这个方式并不是你想要的。

3.4. 生成器¶

生成器是可以迭代的,但是你 只可以读取它一次因为它并不把所有的值放在内存中它是实时地生成数据:

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

看起来除了[] 换成 () 外没什么不同。但是,你不可以再次使用 for i in mygenerator , 因为生成器只能被迭代一次:先计算出0,然后继续计算1,然后计算4,一个跟一个的…

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

这个例子没什么用途,但是它让你知道,这个函数会返回一大批你只需要读一次的值.

为了精通 yield ,你必须要理解:当你调用这个函数的时候,函数内部的代码并不立马执行 ,这个函数只是返回一个生成器对象,这有点蹊跷不是吗。

那么,函数内的代码什么时候执行呢?当你使用for进行迭代的时候.

现在到了关键点了!

第一次迭代中你的函数会执行,从开始到达 yield 关键字,然后返回 yield 后的值作为第一次迭代的返回值. 然后,每次执行这个函数都会继续执行你在函数内部定义的那个循环的下一次,再返回那个值,直到没有可以返回的。

如果生成器内部没有定义 yield 关键字,那么这个生成器被认为成空的。这种情况可能因为是循环进行没了,或者是没有满足 if/else 条件。

3.6. 回到你的代码¶

(译者注:这是回答者对问题的具体解释)

生成器:

# Here you create the method of the node object that will return the generator
def node._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

调用者:

# 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() 是一个迭代器方法,作用于迭代器,并把参数追加到迭代器的后面。

通常我们传给它一个列表参数:

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

但是在你的代码中的是一个生成器,这是不错的,因为:

  • 你不必读两次所有的值
  • 你可以有很多子对象,但不必叫他们都存储在内存里面。

并且这很奏效,因为Python不关心一个方法的参数是不是个列表。Python只希望它是个可以迭代的,所以这个参数可以是列表,元组,字符串,生成器... 这叫做 duck typing,这也是为何Python如此棒的原因之一,但这已经是另外一个问题了...

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

3.7. 控制生成器的穷尽¶

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

对于控制一些资源的访问来说这很有用。

3.8. Itertools,你最好的朋友¶

itertools包含了很多特殊的迭代方法。是不是曾想过复制一个迭代器?串联两个迭代器?把嵌套的列表分组?不用创造一个新的列表的 zip/map?

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

3.9. 了解迭代器的内部机理¶

迭代是一个实现可迭代对象(实现的是 __iter__() 方法)和迭代器(实现的是 __next__() 方法)的过程。可迭代对象是你可以从其获取到一个迭代器的任一对象。迭代器是那些允许你迭代可迭代对象的对象。

更多见这个文章 http://effbot.org/zone/python-for-statement.htm

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/567985.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python matplotlib简单使用

一、简单介绍 Matplotlib是Python的一个绘图库&#xff0c;是Python中最常用的可视化工具之一。 二、安装方法 安装方法&#xff1a;pip install matplotlib 注意&#xff1a;安装matplotlib前需要先安装numpy才可以 三、基本绘图命令 1、plt.fig([num]) 在绘图过程中&a…

python窗体设计插件_Python 界面生成器 wxFormBuilder 的入门使用(wxPython的界面设计工具的初学笔记)...

环境&#xff0c;Win10&#xff0c;python3.7.3&#xff0c;wxPython 4.0.4&#xff0c;wxFormBuilder 3.91、准备一个窗体。点击wxformbuilder上方的标签“forms”&#xff0c;并点击标签下方的第一个类似窗体的图标“Frame”然后&#xff0c;下面就会出现一个窗体。但是现在还…

mysql配置性能_MySQL配置性能优化

下面配置的优化&#xff0c;可能影响比较大&#xff0c;可能可以显著提高读写性能。1、mysql一些主要配置项介绍&#xff1a;innodb_buffer_pool_size这是你安装完InnoDB后第一个应该设置的选项。缓冲池是数据和索引缓存的地方&#xff1a;这个值越大越好&#xff0c;这能保证你…

用cmd编译c++程序

1、设置好环境变量&#xff08;已安装vs&#xff09; ①在计算机的系统环境变量--》用户变量--》path中添加 D:\VS15\VC\bin ②新建变量INCLUDE&#xff1a;D:\VS15\VC\include ③新建变量LIB&#xff1a;D:\VS15\VC\lib 2、利用cl编译c文件 打开cmd 输入c…

C++编译运行过程分析

为了减轻使用机器语言编程的痛苦&#xff0c;人们进行了一种有益的改进&#xff1a;用一些简洁的英文字母、符号串来替代一个特定的指令的二进制串&#xff0c;比如&#xff0c;用“A D D”代表加法&#xff0c;“M O V”代表数据传递等等&#xff0c;这样一来&#xff0c;人们…

mysql给用户授权最大_mysql 给用户授权

MySQL用户授权 GRANT 语句的语法如下&#xff1a; GRANT privileges (columns) ON what TO user IDENTIFIEDBY "password" WITH GRANT OPTION 对用户授权 mysql>grant rights on database.* to userhost identified by &qu…

python插入排序_直接插入排序(python实现)

这篇博文用来介绍直接插入排序直接插入排序基本思想&#xff1a;每次将一个待排序的记录插入到已经排好序的数据区中&#xff0c;直到全部插入完为止直接插入排序算法思路&#xff1a;在直接插入排序中&#xff0c;数据元素分为了有序区和无序区两个部分&#xff0c;在这里我们…

STL1-函数模板

1、函数模板和普通函数区别 //普通函数可以进行自动类型转换&#xff0c; //函数模板必须精确类型匹配; //函数模板可以被重载;c优先考虑普通函数;#include<iostream> using namespace std; //函数模板-->产生模板函数-->调用函数 template<class T> T MyAd…

ruby 安装mysql_ruby安装mysql

ruby安装mysqlruby on rails安装mysql数据库1. 下载mysql软件http://www.mysql.cn/mysql中文官方网站下载安装软件&#xff0c;选择5.02. 安装mysql&#xff0c;设置默认字符集为utf-83. 下载mysql for ruby的驱动并安装http://rubyforge.org/搜索mysql&#xff0c;找到mysql -…

jupyter安装与迁移文件

1、安装 pip install jupyter notebook -i https://pypi.tuna.tsinghua.edu.cn/simple 2、测试安装成功 安装完后输入 jupyter notebook 出现一个jupyter网址&#xff0c;即证明安装成功 3、数据迁移 将之前的jupyter notebook产生的文件复制在python所安装的盘目录下。然…

修正的判定条件覆盖例题_语句覆盖、判断覆盖、条件覆盖、条件判定组合覆盖、多条件覆盖、修正条件覆盖...

int function(bool a,bool b,boolc){intx;x0;if(a&&(b||c)){x1;returnx;}}1、语句覆盖(SC)选择足够多的测试数据&#xff0c;使得被测程序中的每条语句至少执行一次。测试用例&#xff1a;aT,bT,cT2、判断覆盖(DC)设计足够的测试用例&#xff0c;使得程序中的每个判定至…

STL2-类模板

1、类模板实现 函数模板在调用时可以自动类型推导 类模板必须显式指定类型 #include<iostream> using namespace std;template<class T> class Person { public:T mId;T mAge; public:Person(T id,T age){this->mAge age;this->mId id;}void Show(){cout…

STL3-MyArray动态数组类模板实现

注意 1、右值的拷贝使用 2、拷贝构造函数的使用 #include<iostream> using namespace std;template<class T> class MyArray{ public:MyArray(int capacity){this->mCapacity capacity;this->mSize 0;//申请内存this->pAddr new T[this->mCapac…

mysql udf提权hex_Mysql_UDF提权

Mysql_UDF提权作者&#xff1a;admin 发布于&#xff1a;2013-5-25 18:55 Saturday分类&#xff1a;MYSQLRoot权限一、上传udf.dll小于mysql5.1版本C:\\WINDOWS\\udf.dll 或C:\\WINDOWS\\system32\\udf.dll等于mysql5.1版本%mysql%\\plugin\\udf.dll 用 selectplugin_dir 查询…

STL4-类型转换

#include<iostream> using namespace std;class Building{}; class Animal{}; class Cat :public Animal {}; //Cat是Animal的子类//static_cast //用于内置的数据类型及具有继承关系的指针或者引用 void test01() {int a 97;//static_cast<要转换的类型>(转换的…

线程池写入mysql_用多线程写入数据库的问题(150分)

把 一少的程序稍加修改就可以多个线程处理一个文件了unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 class(TForm)Button1: TButton;procedure FormCreate(Sender: TObject);procedure Bu…

java对象排序_Java™ 教程(对象排序)

对象排序List l可以如下排序。Collections.sort(l);如果List包含String元素&#xff0c;它将按字母顺序排序&#xff0c;如果它由Date元素组成&#xff0c;它将按时间顺序排序&#xff0c;这是怎么发生的&#xff1f;String和Date都实现了Comparable接口&#xff0c;Comparable…

python argparse模块

argparse模块 argparse是python用于解析命令行参数和选项的标准模块&#xff0c;用于代替已经过时的optparse模块 使用步骤 import argparse # 1 导入模块&#xff0c;这个没什么说的 parser argparse.ArgumentParser() # 2 实例化一个对象&#xff0c;默认参数一堆&#…

java解析yml文件_如何基于JAVA读取yml配置文件指定key内容

这篇文章主要介绍了如何基于JAVA读取yml配置文件指定key内容,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下先引入需要的依赖org.yamlsnakeyaml1.23读取YML文件工具类的代码import org.apache.commons.lang3.St…

STL5-异常

异常可以跨函数 异常必须处理 1、 #include<iostream> using namespace std; //c异常机制 跨函数 //c异常必须处理 不能留&#xff0c;否则报错 int divided(int x, int y) {if (y 0)throw y; //抛异常return (x / y); } void test01() {int x 10, y 0;//试着去捕获…