前言

Python生成器(generator)并不是一个晦涩难懂的概念。相比于MetaClass和Closure等概念,其较为容易理解和掌握。但相对于程序结构:顺序、循环和分支而言其又不是特别的直观。无论学习任何的东西,概念都是非常重要的。正确树立并掌握一些基础的概念是灵活和合理运用的前提,本文将以一种通俗易懂的方式介绍一下generator和yield表达式。

1. Iterator与Iterable

首先明白两点:

Iterator(迭代器)是可迭代对象;

可迭代对象并不一定是Iterator;

比较常见的数据类型list、tuple、dict等都是可迭代的,属于collections.Iterable类型;

迭代器不仅可迭代还可以被内置函数next调用,属于collections.Iterator类型;

迭代器是特殊的可迭代对象,是可迭代对象的一个子集。

将要介绍的gererator(生成器)是types.GeneratorType类型,也是collections.Iterator类型。

也就是说生成器是迭代器,可被next调用,也可迭代。

三者的包含关系:(可迭代(迭代器(生成器)))

迭代器:可用next()函数访问的对象;

生成器:生成器表达式和生成器函数;

2. Python生成器

python有两种类型的生成器:生成器表达式和生成器函数。

由于生成器可迭代并且是iterator,因此可以通过for和next进行遍历。

2.1 生成器表达式

把列表生成式的[]改成()便得到生成器表达式。

>>> gen = (i + i for i in xrange(10))

>>> gen

at 0x0000000003A2DAB0>

>>> type(gen)

>>> isinstance(gen, types.GeneratorType) and isinstance(gen, collections.Iterator) and isinstance(gen, collections.Iterable)

True

>>>

2.2 生成器函数

python函数定义中有关键字yield,该函数便是一个生成器函数,函数调用返回的是一个generator.

def yield_func():

for i in xrange(3):

yield i

gen_func = yield_func()

for yield_val in gen_func:

print yield_val

生成器函数每次执行到yield便会返回,但与普通函数不同的是yield返回时会保留当前函数的执行状态,再次被调用时可以从中断的地方继续执行。

2.3 next与send

通过for和next可以遍历生成器,而send则可以用于向生成器函数发送消息。

def yield_func():

for i in xrange(1, 3):

x = yield i

print 'yield_func',x

gen_func = yield_func()

print 'iter result: %d' % next(gen_func)

print 'iter result: %d' % gen_func.send(100)

结果:

iter result: 1

yield_func 100

iter result: 2

简单分析一下执行过程:

line_no 5 调用生成器函数yield_func得到函数生成器gen_func;

line_no 6 使用next调用gen_func,此时才真正的开始执行yield_func定义的代码;

line_no 3 执行到yield i,函数yield_func暂停执行并返回当前i的值1.

line_no 6 next(gen_func)得到函数yield_func执行到yield i返回的值1,输出结果iter result: 1;

line_no 7 执行gen_func.send(100);

line_no 3 函数yield_func继续执行,并将调用者send的值100赋值给x;

line_no 4 输出调用者send接收到的值;

line_no 3 执行到yield i,函数yield_func暂停执行并返回当前i的值2.

line_no 7 执行gen_func.send(100)得到函数yield_func运行到yield i返回的值2,输出结果iter result: 2;

如果在上面代码后面再加一行:

print 'iter result: %d' % next(gen_func)

结果:

iter result: 1

yield_func 100

iter result: 2

yield_func None

File "G:\Cnblogs\Alpha Panda\Main.py", line 22, in

print 'iter result: %d' % next(gen_func)

StopIteration

yield_func只会产生2个yield,但是我们迭代调用了3次,会抛出异常StopIteration。

next和send均会触发生成器函数的执行,使用for遍历生成器函数时不要用send。原因后面解释。

2.4 生成器返回值

使用了yield的函数严格来讲已经不是一个函数,而是一个生成器。因此函数中yield和return是不能同时出现的。

SyntaxError: 'return' with argument inside generator

生成器只能通过yield将每次调用的结果返回给调用者。

2.5 可迭代对象转成迭代器

list、tuple、dict等可迭代但不是迭代器的对象可通过内置函数iter转化为iterator,便可以通过next进行遍历;

这样的好处是可以统一使用next遍历所有的可迭代对象;

tup = (1,2,3)

for ele in tup:

print ele + ele

上面的代码等价于:

tup_iterator = iter(tup)while True:

try:

ele = next(tup_iterator)

except StopIteration:

break

print ele + ele

for循环使用next遍历一个迭代器,混合使用send可能会导致混乱的遍历流程。

其实到这里生成器相关的概念基本已经介绍完成了,自己动手过一遍应该能弄明白了。为了更加深刻的体会生成器,下面我们在往前走一步。

3. range与xrange

在Python 2中这两个比较常用,看一下两者的区别:

range为一个内置函数,xrange是一个类;

前者返回一个list,后者返回一个可迭代对象;

后者遍历操作快于前者,且占用更少内存;

这里xrange有点类似于上面介绍的生成器表达式,虽然xrange返回的并不是生成器,但两者均返回并不包含全部结果可迭代对象。

3.1 自定义xrange的Iterator版本

作为一个iterator:

The iterator objects themselves are required to support the following two methods, which together form the iterator protocol:

iterator.__iter__()

Return the iterator object itself. This is required to allow both containers and iterators to be used with the for and in statements. This method corresponds to the tp_iter slot of the type structure for Python objects in the Python/C API.

iterator.next()

Return the next item from the container. If there are no further items, raise the StopIteration exception. This method corresponds to the tp_iternext slot of the type structure for Python objects in the Python/C API.

下面我们自定义class my_xrange:

class my_xrange(object):

def __init__(self, start, stop = None, step = 1):

""" 仅仅为了演示,假设start, stop 和 step 均为正整数 """

self._start = 0 if stop is None else start

self._stop = start if stop is None else stop

self._step = step

self._cur_val = self._start

def __iter__(self):

return self

def next(self):

if self._start <= self._cur_val < self._stop:

cur_val = self._cur_val

self._cur_val += self._step

return cur_val

raise StopIteration

测试结果:

import collections

myxrange = my_xrange(0, 10, 3)

res = []

for val in myxrange:

res.append(val)

print res == range(0, 10, 3)   # True

print isinstance(myxrange, collections.Iterator)  # Trueprint isinstance(myxrange, types.GeneratorType)  # False

3.2 使用函数生成器

下面使用函数生成器定义一个generator版的xrange。

def xrange_func(start, stop, step = 1):

""" 仅仅为了演示,假设start, stop 和 step 均为正整数 """

cur_val = start

while start <= cur_val and cur_val < stop:

yield cur_val

cur_val += step

isinstance(myxrange, collections.Iterator) and isinstance(myxrange, types.GeneratorType) is True

上面两个自定义xrange版本的例子,均说明生成器以及迭代器保留数列生成过程的状态,每次只计算一个值并返回。这样只要占用很少的内存即可表示一个很大的序列。

4. 应用

不管是迭代器还是生成器,对于有大量有规律的数据产生并需要遍历访问的情景均适用,占用内存少而且遍历的速度快。其中一个较为经典的应用为斐波那契数列(Fibonacci sequence)。

这里以os.walk遍历目录为例来说明yield的应用。如果我们需要遍历一个根目录下的所有文件并根据需要进行增删改查。可能会遇到下列的问题:

预先遍历且缓存结果,但是目录下文件可能很多,而且会动态改变;如果不缓存,多个地方可能会频繁的需要访问这一结果导致效率低下。

这时候可以使用yield定义一个生成器函数。

def get_all_dir_files(target_dir):

for root, dirs, files in os.walk(target_dir):

for file in files:

file_path = os.path.join(root, file)

yield os.path.realpath(file_path)

def file_factory(file):

""" do something """

target_dir = './'

all_files = get_all_dir_files(target_dir)

for file in all_files:

file_factory(file)

限于篇幅,就先介绍到这里,希望本文能让你对生成器有一个新的认识。

,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值

python生成器yield原理_Python generator生成器和yield表达式详解相关推荐

  1. python itertools模块位置_Python高效编程之itertools模块详解

    0 前言 说到处理循环,我们习惯使用for, while等,比如依次打印每个列表中的字符:lis = [ I , love , python ] for i in lis: print(i) I lo ...

  2. python用蒙特卡洛法区间_python编程通过蒙特卡洛法计算定积分详解

    想当初,考研的时候要是知道有这么个好东西,计算定积分...开玩笑,那时候计算定积分根本没有这么简单的.但这确实给我打开了一种思路,用编程语言去解决更多更复杂的数学问题.下面进入正题. 如上图所示,计算 ...

  3. python 自动化办公 案例_python自动化工具之pywinauto实例详解

    python自动化工具之pywinauto实例详解 来源:中文源码网 浏览: 次 日期:2019年11月5日 [下载文档: python自动化工具之pywinauto实例详解.txt ] (友情提示: ...

  4. python属性使用教程_Python对象的属性访问过程详解

    只想回答一个问题: 当编译器要读取obj.field时, 发生了什么? 看似简单的属性访问, 其过程还蛮曲折的. 总共有以下几个step: 1. 如果obj 本身(一个instance )有这个属性, ...

  5. python dataframe loc函数_python pandas.DataFrame.loc函数使用详解

    官方函数 DataFrame.loc Access a group of rows and columns by label(s) or a boolean array. .loc[] is prim ...

  6. python布尔类型运算_Python对象类型及其运算方法(详解)

    基本要点: 程序中储存的所有数据都是对象(可变对象:值可以修改 不可变对象:值不可修改) 每个对象都有一个身份.一个类型.一个值 例: >>> a1 = 'abc' >> ...

  7. python实现文本编辑器_Python实现文本编辑器功能实例详解

    这篇文章主要介绍了Python实现的文本编辑器功能,结合实例形式详细分析了基于wxpython实现文本编辑器所需的功能及相关实现技巧,需要的朋友可以参考下 本文实例讲述了Python实现的文本编辑器功 ...

  8. [转载] python 遍历字符串 字符_python 遍历字符串(含汉字)实例详解

    参考链接: Python字符串| rfind python 遍历字符串(含汉字)实例详解 python 遍历字符串(含汉字)实例详解 s = "中国china" for j in ...

  9. python怎么取共轭_python print出共轭复数的方法详解

    python print出共轭复数的方法详解 发布时间:2020-09-21 01:42:19 来源:脚本之家 阅读:92 作者:爱喝马黛茶的安东尼 复数是由一个实数和一个虚数组合构成,表示为:x+y ...

最新文章

  1. Fluke OTDR新增SmartLoop双向测试功能
  2. alibab仓库 idea_01.微服务架构编码、构建
  3. GXU - 7D - 区间求和 - 前缀和
  4. 2018 支付宝Java开发四面:Ngnix+MQ队列+集群+并发抢购
  5. django-验证码
  6. R语言可视化学习笔记之ggpubr包
  7. 读书-算法《程序设计导引及在线实践》-简单计算题4:填词
  8. python爬取酷狗音乐top500_Python爬取酷狗音乐TOP500榜单
  9. WPF随笔(十)--使用AvalonDock实现可停靠式布局
  10. DVWA网盘下载和安装教程 详解
  11. 中国银行为房地产买家加入新区块链平台
  12. Linux 系统常见命令功能大全_【all】
  13. Python:计算任意n阶矩阵行列式
  14. VMware虚拟机 与 windows宿主机做目录映射
  15. 应该来说没有什么太大的关系。因为计算精度既取决于你的级数的项
  16. 爬取楼盘网并将数据保存在excel表中
  17. Davinci的异构多核间通信基础组件SysLink
  18. 机器人操作系统 ROS 相关书籍整理合集 [古月居推荐]
  19. phpcms 点赞_php+js实现点赞功能的示例详解
  20. LAC+CELLID定位手机位置

热门文章

  1. 从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用Application Part动态加载控制器和视图...
  2. C#并行编程(5):需要知道的异步
  3. .NET Core 3.0 linux 部署小贴士
  4. 大规模开发团队如何实现DevOps转型? 来自微软全球开发平台工程团队的实践经验
  5. 如果不懂Service mesh,就不要谈微服务了
  6. .NET Core 2.0体验
  7. 升讯威微信营销系统开发实践:(3)中控服务器的设计 .Net 还是 Java?
  8. C语言试题六十九之请编写函数判断一个数是不是素数
  9. C语言试题三十四之求除1到m之内(含m)能北7或11整除的所有整数放在数组a中,通过n返回这些数的个数。
  10. Android之jni编译出现multiple definition of ‘××××ב