一 概要

在了解Python的数据结构时,容器(container)、可迭代对象(iterable)、迭代器(iterator)、生成器(generator)、列表/集合/字典推导式(list,set,dict comprehension)众多概念参杂在一起,难免让初学者一头雾水,我将用一篇文章试图将这些概念以及它们之间的关系捋清楚

二 容器(container)

容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用 in , not in 关键字判断元素是否包含在容器中。通常这类数据结构把所有的元素存储在内存中(也有一些特列并不是所有的元素都放在内存)在Python中,常见的容器对象有:

list, deque, ....

set, frozensets, ....

dict, defaultdict, OrderedDict, Counter, ....

tuple, namedtuple, …

str

容器比较容易理解,因为你就可以把它看作是一个盒子、一栋房子、一个柜子,里面可以塞任何东西。从技术角度来说,当它可以用来询问某个元素是否包含在其中时,那么这个对象就可以认为是一个容器,比如 list,set,tuples都是容器对象:

尽管绝大多数容器都提供了某种方式来获取其中的每一个元素,但这并不是容器本身提供的能力,而是 可迭代对象 赋予了容器这种能力,当然并不是所有的容器都是可迭代的。

三 可迭代对象(iterable)

如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。

刚才说过,很多容器都是可迭代对象,此外还有更多的对象同样也是可迭代对象,比如处于打开状态的files,sockets等等。但凡是可以返回一个迭代器的对象都可称之为可迭代对象,听起来可能有点困惑,没关系,可迭代对象与迭代器有一个非常重要的区别。先看一个例子:

这里 x 是一个可迭代对象,可迭代对象和容器一样是一种通俗的叫法,并不是指某种具体的数据类型,list是可迭代对象,dict是可迭代对象,set也是可迭代对象。 y 和 z 是两个独立的迭代器,迭代器内部持有一个状态,该状态用于记录当前迭代所在的位置,以方便下次迭代的时候获取正确的元素。迭代器有一种具体的迭代器类型,比如 list_iterator , set_iterator 。可迭代对象实现了 __iter__ 和 __next__ 方法(python2中是 next 方法,python3是 __next__ 方法),这两个方法对应内置函数 iter() 和 next() 。 __iter__ 方法返回可迭代对象本身,这使得他既是一个可迭代对象同时也是一个迭代器。

四 迭代器(iterator)

那么什么迭代器呢?它是一个带状态的对象,他能在你调用 next() 方法的时候返回容器中的下一个值,任何实现了 __next__() (python2中实现 next() )方法的对象都是迭代器,至于它是如何实现的这并不重要。

现在我们就以斐波那契数列()为例,学习为何创建以及如何创建一个迭代器:

著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

code1:

deffab(max):

n, a, b= 0, 0, 1

while n

a, b= b, a +b

n= n + 1

直接在函数fab(max)中用print打印会导致函数的可复用性变差,因为fab返回None。其他函数无法获得fab函数返回的数列。

code2:

deffab(max):

L=[]

n, a, b= 0, 0, 1

while n

L.append(b)

a, b= b, a +b

n= n + 1

return L

代码2满足了可复用性的需求,但是占用了内存空间,最好不要。

对比for i in range(1000): pass和for i in xrange(1000): pass,前一个返回1000个元素的列表,而后一个在每次迭代中返回一个元素,因此可以使用迭代器来解决复用可占空间的问题

code3:

classFab(object):def __init__(self, max):

self.max=max

self.n, self.a, self.b= 0, 0, 1

def __iter__(self):returnselfdefnext(self):if self.n

r=self.b

self.a, self.b= self.b, self.a +self.b

self.n= self.n + 1

returnrraiseStopIteration()'''>>> for key in Fabs(5):

print key

1

1

2

3

5'''

Fabs 类通过 next() 不断返回数列的下一个数,内存占用始终为常数

Fib既是一个可迭代对象(因为它实现了 __iter__ 方法),又是一个迭代器(因为实现了 __next__ 方法)。实例变量 self .a 和 self.b 用户维护迭代器内部的状态。每次调用 next() 方法的时候做两件事:

为下一次调用 next() 方法修改状态

为当前这次调用生成返回结果

迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。

五 for i in (iterable)的内部实现

在大多数情况下,我们不会一次次调用next方法去取值,而是通过 for i in (iterable)的方式

注意:in后面的对象如果是一个迭代器,内部因为有iter方法才可以进行操作,所以,迭代器协议里面有iter和next两个方法,否则for语句无法应用。

注意:定时垃圾回收机制

for i in range(10):print i :定时垃圾回收机制:没有引用指向这个对象,则被回收

六 生成器(generator)

生成器算得上是Python语言中最吸引人的特性之一,生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。代码3远没有代码1简洁,生成器(yield)既可以保持代码1的简洁性,又可以保持代码3的效果。它不需要再像上面的类一样写 __iter__() 和 __next__() 方法了,只需要一个 yiled 关键字。 生成器有如下特征是它一定也是迭代器(反之不成立),因此任何生成器也是以一种懒加载的模式生成值。用生成器来实现斐波那契数列的例子是:

deffab(max):

n, a, b= 0, 0, 1

while n

a, b= b, a +b

n= n + 1

>>> for n in fab(5):printn1

1

2

3

5

fib 就是一个普通的python函数,它特需的地方在于函数体中没有 return 关键字,函数的返回值是一个生成器对象。当执行 f=fib(5) 返回的是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用next的时候才会真正执行里面的代码。

yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:

>>> f = fab(3)>>> f.__next__()1

>>> f.__next__()1

>>> f.__next__()2

>>> f.__next__()

Traceback (most recent call last):

File"", line 1, in f.next()

StopIteration

需要明确的就是生成器也是iterator迭代器,因为它遵循了迭代器协议。

两种创建方式

包含yield的函数

生成器函数跟普通函数只有一点不一样,就是把 return 换成yield,其中yield是一个语法糖,内部实现了迭代器协议,同时保持状态可以挂起。如下:

在一个生成器中,如果没有return,则默认执行到函数完毕;如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。

复制代码deff():yield 5

print("ooo")return

yield 6

print("ppp")#if str(tem)=='None':

#print("ok")

f=f()#print(f.__next__())#print(f.__next__())

for i inf:print(i)'''return即迭代结束

for不报错的原因是内部处理了迭代结束的这种情况'''

注意:

文件读取

defread_file(fpath):

BLOCK_SIZE= 1024with open(fpath,'rb') as f:whileTrue:

block=f.read(BLOCK_SIZE)ifblock:yieldblockelse:return

如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

生成器对象就是一种特殊的迭代器对象,满足迭代器协议,可以调用next;对生成器对象for 循环时,调用iter方法返回了生成器对象,然后再不断next迭代,而iter和next都是在yield内部实现的。

例1:

defadd(s, x):return s +xdefgen():for i in range(4):yieldi

base=gen()for n in [1, 10]:

base= (add(i, n) for i inbase)print list(base)

'''核心语句就是:

for n in [1, 10]:

base = (add(i, n) for i in base)#在这里,base依旧是个生成器

在执行list(base)的时候,开始检索,然后生成器开始运算了。关键是,这个循环次数是2,也就是说,有两次生成器表达

式的过程。必须牢牢把握住这一点。

生成器返回去开始运算,n = 10而不是1没问题吧,这个在上面提到的文章中已经提到了,就是add(i, n)绑定的是n这个

变量,而不是它当时的数值。

然后首先是第一次生成器表达式的执行过程:base = (10 + 0, 10 + 1, 10 + 2, 10 +3),这是第一次循环的结

果(形象表示,其实已经计算出来了(10,11,12,3)),然后第二次,

base = (10 + 10, 11 + 10, 12 + 10, 13 + 10) ,终于得到结果了[20, 21, 22, 23].'''

例2:自定义range

七 生成器的扩展

生成器对象支持几个方法,如gen.next() ,gen.send() ,gen.throw()等。

由于没有额外的yield,所以将直接抛出StopIteration。

send的工作方式:

deff():print("ok")

s=yield 7

print(s)yield 8f=f()print(f.send(None))print(next(f))#print(f.send(None))等同于print(next(f)),执行流程:打印ok,yield7,当再next进来时:将None赋值给s,然后返回8,可以通过断点来观察

协程应用:

所谓协同程序也就是是可以挂起,恢复,有多个进入点。其实说白了,也就是说多个函数可以同时进行,可以相互之间发送消息等。

importqueuedeftt():for x in range(4):print ('tt'+str(x) )yield

defgg():for x in range(4):print ('xx'+str(x) )yield

classTask():def __init__(self):

self._queue=queue.Queue()defadd(self,gen):

self._queue.put(gen)defrun(self):while notself._queue.empty():for i inrange(self._queue.qsize()):try:

gen=self._queue.get()

gen.send(None)exceptStopIteration:pass

else:

self._queue.put(gen)

t=Task()

t.add(tt())

t.add(gg())

t.run()#tt0#xx0#tt1#xx1#tt2#xx2#tt3#xx3

python迭代器与生成器_Python的迭代器和生成器相关推荐

  1. python迭代器和生成器_Python之迭代器和生成器

    一.迭代器 1.迭代器的概念 迭代器即迭代的工具,那么什么是迭代呢?迭代是一个重复的过程,每一次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值.下面的代码就不是迭代. whileTrue:p ...

  2. python迭代器面试题_Python面试题之生成器/迭代器

    原博文 2017-07-02 21:36 − 1.为什么要有生成器? 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占 ...

  3. python迭代器和生成器_python中迭代器和生成器。

    前言:很多python教程中,对python的解释不容易理解,本文记录自己的理解和体会,是对迭代器和生成器的初步理解. 迭代器: 迭代器的实质是实现了next()方法的对象,常见的元组.列表.字典都是 ...

  4. python迭代器创建序列_Python 中迭代器与生成器实例详解

    Python 中迭代器与生成器实例详解 本文通过针对不同应用场景及其解决方案的方式,总结了Python中迭代器与生成器的一些相关知识,具体如下: 1.手动遍历迭代器 应用场景:想遍历一个可迭代对象中的 ...

  5. python迭代器与生成器_python的迭代器与生成器实例详解

    本文以实例详解了python的迭代器与生成器,具体如下所示: 1. 迭代器概述: 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后 ...

  6. python加减法器课程报告_python实现随机加减法生成器

    本文实例为大家分享了python实现随机加减法生成器的具体代码,供大家参考,具体内容如下 为了让外甥女练习算术,用python给她写了个自动出加减法的小程序. 该程序使用了文字转语音的库pyttsx, ...

  7. python中文讨论组_python - 中文名字随机生成器

    伊谢尔伦2017-04-18 09:41:043楼 下班没事干凑凑热闹, 说说我看到这个问题的思路及抄码记录 1. 为了让生成的名字看起来像名字, 就像一楼说的, 要分姓和名 2. 先整姓 找找百家姓 ...

  8. python yield用法_Python中迭代器和生成器的区别?

    公众号新增加了一个栏目,就是每天给大家解答一道Python常见的面试题,反正每天不贪多,一天一题,正好合适,只希望这个面试栏目,给那些正在准备面试的同学,提供一点点帮助! 小猿会从最基础的面试题开始, ...

  9. python生成器和装饰器_python三大法器:生成器、装饰器、迭代器

    迭代器 迭代的概念 使用for循环遍历取值的过程叫做迭代,比如:使用for循环遍历列表获取值的过程 使用for循环遍历取值的对象叫做可迭代对象, 比如:列表.元组.字典.集合.range.字符串 判断 ...

最新文章

  1. Mat矩阵基本操作与示例 OpenCV
  2. [20160311]ora-01732.txt
  3. CSS3知识点整理(三)----变形与动画
  4. 删除U盘作为启动盘的隐藏启动分区
  5. MCSDK HUA Demonstration Guide
  6. 【推荐】英国金融时报推荐的数据可视化图表分类图
  7. python测试驱动开发百度云_【有书共读】《Python测试驱动开发》读书笔记01
  8. 小程序引入的echarts过大如何解决_智慧虎超:为服装行业带来3倍收益?小程序如何解决销售难题?...
  9. Linux多任务编程之七:Linux守护进程及其基础实验(转)
  10. 『资源』机器学习实践中文资源合集
  11. c++ 写x64汇编 5参数_自己动手写操作系统 8086sos
  12. matlab 换热器仿真,基于MATLAB的换热器温度控制仿真研究.doc
  13. 方大东莞新材:实习总结报告PPT讲稿
  14. 小程序服务器mp4文件,如何添加小程序视频链接及获取MP4格式视频
  15. 公司网站被黑 跳转到彩票网站的处理解决办法
  16. java log 断点_断点续传JAVA实现
  17. 服务器winsxs文件夹怎么清理工具,winsxs文件夹怎么清理 winsxs文件夹清理方法教程...
  18. JQueryUI自动补全 搜索提示实践
  19. 计算机专业毕设太难做不出来怎么办?
  20. 60-硅谷课堂6-硅谷课堂-公众号消息和微信授权-- 笔记

热门文章

  1. LeetCode----13. 罗马数字转整数
  2. java包的概念及作用
  3. 应该允许公司报复黑客吗?
  4. HTML5调用手机前置摄像头或后置摄像头拍照,canvas显示,经过Android测试
  5. C++ - 模板函数须要类型转换时使用友元(friend)模板函数
  6. 车联网行业No.1元征科技的云端架构实现
  7. [C++] STACK_Principle
  8. HTML5 audio 标签-在html中定义声音的标签
  9. expandableListView 总结
  10. k8s serviceaccount pod亲和性 污点