关注公众号「Python七号」,及时 get Python 技能。

yield 可以实现生成器,可以实现协程。 什么是生成器,什么是协程,如果还不了解,可以继续往下看,概念可以不懂,只要理解它的作用和效果也可以的。

先翻一下英文单词 yield 的意思:

to produce or provide sth, for example a profit, result or crop

中文意思:出产(作物);产生(收益、效益等);提供。

yield 实现生成器

初学 Python 之时,我遇到 yield 关键字时,就把它理解为一种特殊的 return,能看懂就行,自己也很少用,也就没有深入研究过。直到现在,我需要处理大量数据,数据的大小甚至超过了电脑的可用内存,此时我想起来 yield。比如,我操作 db2 数据库查询数据,当数据的结果很大时,我不想一下子读入内存,我就使用了 yield 关键字返回一行数据,程序处理完后,再取下一行:

def read(self, sql, params=()):

stmt = ibm_db.prepare(self.connection, sql)

for index, param in enumerate(params):

ibm_db.bind_param(stmt, index + 1, param)

ibm_db.execute(stmt)

row = ibm_db.fetch_tuple(stmt)

while row:

yield row

row = ibm_db.fetch_tuple(stmt)

可以这么来理解关键字 yield 的用法:它返回了一个值,但程序并未退出,下一次从 yield 后面的代码继续运行,直到后面没有代码,结束运行。这里我们举一个简单的例子看下效果:

>>> def iter_fun():

... print("a")

... yield 1

... print("b")

... yield 2

... print("c")

... yield 3

...

>>> iter_fun()

>>> for i in iter_fun():

... print(i)

...

a

1

b

2

c

3

>>> x = iter_fun()

>>> x.__next__()

a

1

>>> x.__next__()

b

2

>>> x.__next__()

c

3

>>> x.__next__()

Traceback (most recent call last):

File "", line 1, in

StopIteration

通过上面的例子,我们可以发现,yield 关键字自动为我们生成来私有方法 __next__ ,这样不会将所有数据取出来存入内存中,用多少取多少,可以节省内存空间。

除此之外,yield 再数据量较大时,执行速度也会提升:

In [14]: def normal_fun(num):

...: result = []

...: for i in range(0,num):

...: if i%2 == 0:

...: result.append(i)

...: return result

...:

In [14]: def iter_fun(num):

...: for i in range(0,num):

...: if i %2 == 0:

...: yield i

...:

In [15]: %time for i in iter_fun(1000000): a = i

CPU times: user 97 ms, sys: 2.55 ms, total: 99.6 ms

Wall time: 97.2 ms

In [16]: %time for i in normal_fun(1000000): a = i

CPU times: user 115 ms, sys: 13.6 ms, total: 129 ms

Wall time: 128 ms

In [17]: %time for i in normal_fun(100000000): a = i

CPU times: user 10.8 s, sys: 668 ms, total: 11.4 s

Wall time: 11.4 s

In [18]: %time for i in iter_fun(100000000): a = i

CPU times: user 9.1 s, sys: 6.49 ms, total: 9.11 s

Wall time: 9.12 s

上述代码再 Ipython 终端中执行,可以看出使用 yield 的函数,执行的速度更快。

yield 是自己实现一个生成器最便捷的方式。 而 Python 语言的生成器是最有用的特性之一,也是使用不广泛的特性,我曾问过周围用 java 的朋友又没有类似的特性,答曰没有,网上搜了下,确实主流的编程语言都没有,因此 Python 的生成器特性没有引起其他语言转 Python 的工程师的关注。

为什么说生成器非常有用呢?

当你需要处理的数据大小超过你电脑的可用内存时,生成器的懒加载(用的时才读入内存)就非常有效。

比如读文件时,如果你使用下面的方式,文件特别大的话,可能内存一下子就满了:

with open("file.txt","r") as reader:

for line in reader.readlines():

#do something

pass

推荐的做法是使用文件本身的生成器,这样读多大的文件,也不会撑爆内存:

with open("file.txt","r") as reader:

for line in reader:

#do something

pass

其实我们可以看一下 reader 到底有哪些方法:

>>> with open("/etc/passwd","r") as reader:

... dir(reader)

...

['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']

>>>

我们可以看到,这里面有一个 __next__ 方法和一个 __next__ 方法,这两个方法是生成器的标志。

想深入学习生成器,迭代器,可迭代对象,可以看我以前的推文:

yield 可以实现协程

yield 关键字还可以实现协程,虽然现在有来 asyncio 和 await 关键字来方便的实现协程,在此之前,实现协程就靠的是 yield。

yield 有一个 send 方法,可以改变 yield 的返回值,是这样用的,先看代码:

In [20]: def fun():

...: print("a")

...: a = yield 1

...: print("b")

...: print("a = ",a)

...: b = yield a

...: print("c")

...: print("b = ",b)

...:

In [21]: x = fun()

In [22]: x.__next__()

a

Out[22]: 1

In [23]: x.send(4)

b

a = 4

Out[23]: 4

In [24]: x.send(5)

c

b = 5

---------------------------------------------------------------------------

StopIteration Traceback (most recent call last)

in

----> 1 x.send(5)

第一次执行 x 的 next 方法时,函数执行到第一个 yield 处,打印了 a 返回了值 1,此时变量 a 并未获取到 yield 的返回值,a 为 None ,当执行 x.send(4) 时,a 才获取到值 4,程序运行到第二个 yield 处,后续过程也是一样。

利用这一特性,我们可以和被调用的函数通信,进而可以实现一个生产者消费者模型,代码如下:

import time

def consume():

r = ''

while True:

n = yield r

if not n:

return

print('[consumer] consuming%s...' % n)

time.sleep(1)

r = 'well received'

def produce(c):

next(c)

n = 0

while n < 5:

n = n + 1

print('[producer] producing%s...' % n)

r = c.send(n)

print('[producer] consumer return:%s' % r)

c.close()

if __name__=='__main__':

c = consume()

produce(c)

运行结果如下:

[producer] producing 1...

[consumer] consuming 1...

[producer] consumer return: well received

[producer] producing 2...

[consumer] consuming 2...

[producer] consumer return: well received

[producer] producing 3...

[consumer] consuming 3...

[producer] consumer return: well received

[producer] producing 4...

[consumer] consuming 4...

[producer] consumer return: well received

[producer] producing 5...

[consumer] consuming 5...

[producer] consumer return: well received

produce 和 consume 函数在一个线程内执行,通过调用 send 方法和yield 互相切换,实现协程的功能。

关于协程的学习,可以看我以前的推文:

如果您对文章感兴趣,请微信搜索「Python七号」并关注,第一时间获取一手原创干货。

python中row是什么意思_一文搞懂Python中的yield相关推荐

  1. python语言语句快的标记是什么_一文搞懂Python程序语句

    原标题:一文搞懂Python程序语句 程序流 Python 程序中常用的基本数据类型,包括: 内置的数值数据类型 Tuple 容器类型 String 容器类型 List 容器类型 自然的顺序是从页面或 ...

  2. python输入什么就输出什么_一文读懂Python的输入和输出

    本文介绍了Python的输入和输出,既然是Python代码,那么就一定有输出量,那么,Python是如何输出的呢? 输出 用print()在括号中加上字符串,就可以向屏幕上输出指定的文字.比如输出'h ...

  3. java中date类型如何赋值_一文读懂java中的Reference和引用类型

    简介 java中有值类型也有引用类型,引用类型一般是针对于java中对象来说的,今天介绍一下java中的引用类型.java为引用类型专门定义了一个类叫做Reference.Reference是跟jav ...

  4. python数组类型_一文搞懂Python中的所有数组数据类型

    关于我 编程界的一名小小程序猿,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. 联系:hylinux1024@gmail ...

  5. python中w和wb区别_一篇搞懂python文件讀寫操作(r/r+/rb/w/w+/wb/a/a+/ab)

    關於文件操作的幾種常用方式,網上已有很多解說,內容很豐富,但也因此有些雜亂復雜.今天,我就以我個人的學習經驗寫一篇詳細又易懂的總結文章,希望大家看完之后會有所收獲. 一.各模式逐個分解 'r':只讀. ...

  6. python中w和wb区别_一篇搞懂python文件读写操作(r/r+/rb/w/w+/wb/a/a+/ab)

    关于文件操作的几种常用方式,网上已有很多解说,内容很丰富,但也因此有些杂乱复杂.今天,我就以我个人的学习经验写一篇详细又易懂的总结文章,希望大家看完之后会有所收获. 一.各模式逐个分解 'r':只读. ...

  7. python输入时间_一文搞懂python日期时间处理

    前言 datetime是python的内置模块,用来处理日期和时间. 该模块常用的类有: 本文旨在讲解datetime模块中datetime类的使用方法. datetime对象是 date 与 tim ...

  8. 服务器千兆网卡接百兆交换机不通_一文搞懂监控工程中百兆交换机和千兆交换机的区别在哪?...

    安防监控系统工程现在都是用的网络摄像机,那么就肯定会经常和网络设备--交换机打交道,很多人在做监控方案的时候犯难,多少台摄像机该选用百兆交换机还是千兆交换机呢?关于这个问题除了需要掌握理论知识还是结合 ...

  9. python中gbk字符原因报错_不想再被鄙视?那就看进来! 一文搞懂 Python 2 字符编码...

    原标题:不想再被鄙视?那就看进来! 一文搞懂 Python 2 字符编码 程序员都自视清高,觉得自己是创造者,经常鄙视不太懂技术的产品或者QA.可悲的是,程序员之间也相互鄙视,程序员的鄙视链流传甚广, ...

最新文章

  1. 怎样设计一个商城项目?
  2. java生成扑克牌----java基础学习总结
  3. AdaBoost基本原理
  4. 单击CheckBox,全选GridView中所有CheckBox
  5. 2019年第十届蓝桥杯 - 省赛 - C/C++大学B组 - H. 等差数列
  6. IDEA下搜狗输入法输入中文时卡着不动的参考解决方法
  7. 第6步 项目包结构
  8. Problem D: 编程题B-向量的数量积
  9. Hyperledger fabric学习笔记(一)
  10. java动画迷宫寻路_[人工智能] 迷宫生成、寻路及可视化动画
  11. Cosmos互联链通信技术规范(上)
  12. Python-运算符和其优先级
  13. html金额自动换算成大写,JavaScript实现将人民币小写金额自动转换成大写的方法...
  14. php ppt read_PHP如何读取PPT?
  15. 什么是广域网(WAN)?
  16. 如何在旋转屏幕后不再重建Activity
  17. 旋转屏幕时数据的保存与恢复
  18. Backtrader(十六)- Order订单 - order_target_xxx
  19. 华为云fusionsphere 6.1组件功能
  20. Hibernate SQL方言集合

热门文章

  1. Gartner 2021年全球IaaS报告:AWS蛋糕被蚕食,中国云厂商稳步进击
  2. oracle中有关listagg函数的使用
  3. 分享图表制作技巧_15个最有用的Windows 10提示和技巧信息图表
  4. centos7下mysql卸载的方法分享
  5. Xyplayer X3 正式版
  6. 社交网站用户,垃圾病毒邮件多
  7. win7计算机安全配置文件,详述Win7安全模式下的多种功能 -电脑资料
  8. 断言ASSERT使用详解
  9. 2021-2027全球与中国光交叉连接器市场现状及未来发展趋势
  10. OOM问题小例子及思考