一、什么是迭代?

  迭代通俗的讲就是一个遍历重复的过程。

  维基百科中 迭代(Iteration) 的一个通用概念是:重复某个过程的行为,这个过程中的每次重复称为一次迭代。具体对应到Python编程中就是,对于一个可迭代对象,比如Python中的list、tuple、string、dictionary,set等,使用某种循环结构来遍历其中的元素,这种遍历就是迭代。

 #对列表进行遍历!

 l=['a','b','c','d','e']

 #while循环的方式
i=0
while i < len(l):
print(l[i])
i+=1 #for 循环的方式
for i in range(len(l)):
print(l[i])

二、迭代器

1、迭代器定义

  首先先明确 可迭代对象

  在现所学的数据类型中,只有 文件 是迭代器,其他的数据类型:元组,字符串,字典,集合,列表都是 可迭代对象。

判断是否可迭代:只要判断对象本身是否内置了_iter_方法,那它就是可迭代的。

 #可迭代的:只要对象本身有__iter__方法,那它就是可迭代的
d={'a':1,'b':2,'c':3}
d.__iter__ #iter(d) 

可迭代对象实现了 __iter__ 和 __next__ 方法,这两个方法对应内置函数 iter() 和 next() 。__iter__ 方法返回可迭代对象本身,这使得他既是一个可迭代对象同时也是一个迭代器。

 #执行对象下的__iter__方法,就会得到一个返回值,得到的返回值就是迭代器。
# 使用next()就能依次取出迭代器中的值(在迭代器元素个数之内,超出会报错,也是一次性的元素,不可重复取值),这样就不用再依赖下标的方式。
d={'a':1,'b':2,'c':3}
d.__iter__ #iter(d) i=d.__iter__() #返回值,迭代器 print(i.__next__()) #print(next(i))
print(i.__next__())
print(i.__next__()) #3个元素,依次取三次,由于迭代器中的元素是一次性的,超出个数会报错!
print(i.__next__())

执行结果如下:

a
b
c
Traceback (most recent call last):
File "H:/迭代器.py", line 29, in <module>
print(i.__next__())
StopIteration

  迭代器

  迭代器是访问集合中元素的一种方式,从集合中的第一个元素开始访问,直到所有的元素都被访问一遍后结束。迭代器不能回退,只能往前进行迭代。

  迭代器提供了一个统一的访问的接口,只要是定义了iter()方法的对象,就可以使用迭代器进行访问。只要可以进行访问,就能被next()方法调用并不断返回下一个值的对象称为迭代器。换句话说,迭代器对象具有next()方法。

  对于迭代器的理解,可以把迭代器看成是一个数据流,迭代器对象被next()函数调用不断返回下一个数据,直到没有数据时抛出StopIteration错误。

  把这个数据流看做是一个有序序列,但却不能提前知道这个数据流到底有多长,而对于list、tuple等可迭代对象来说,对象的长度是可知的。这也是可迭代对象和迭代器的区别所在。

  StopIteration异常:是迭代终止的信号,迭代器内的内容是有限的,迭代器内元素已全部取完,及取值结束再继续取的话,会报错。

  异常捕捉:迭代器正常执行代码的过程中,print(next(*))  *代表迭代器 会抛出异常。为保证代码的正常运行,此时就用到了 try 和 except。

1)while:循环

 l=['a','b','c','d','e']
i=l.__iter__()
while True:
try: #监听代码是否会报异常 StopIteration
print(next(i))
except StopIteration: #判断异常是否为 StopIteration,是break
break

执行结果如下:

 a
b
c
d
e

2)for 循环 :for 循环本质就是内部封装了迭代器,对 可迭代的对象 进行遍历取值,同时会在遇到异常捕获的时候会自行处理,所以for 循环作用在迭代器上不报错。

 d={'a':1,'b':2,'c':3}
print(d.__iter__())
for k in d: #d.__iter__()
print(k)

执行结果如下:

1 <dict_keyiterator object at 0x00000000021A7728>
2 a
3 b
4 c

执行结果

 #集合及文件 for 循环 迭代形式
s={1,2,3,4}
for i in s:
print(i)
#a.txt ="aaaaaa bbb cccc eee ffffff"
with open('a.txt','r') as f:
for line in f:
print(line.strip())

执行结果如下:

 1 1
2 2
3 3
4 4
5 aaaaaa
6 bbb
7 cccc
8 eee
9 ffffff

执行结果

3)此处注意一个小点:关于文件的认知!   由于文件本身就是一个迭代器,当给文件添加_iter_函数的时候,对其迭代器本身是不冲突的!

 f=open('a.txt','r')
print(f)
print(f.__iter__())
#迭代器执行iter,得到的还是迭代器本身!结果不冲突。

执行结果:

 <_io.TextIOWrapper name='a.txt' mode='r' encoding='cp936'>
<_io.TextIOWrapper name='a.txt' mode='r' encoding='cp936'>

2、为什么要用迭代器
优点:
  (1)迭代器提供了一种不依赖于索引的取值方式,这样就可以遍历那些没有索引的可迭代对象(字典,集合,文件)。
  (2)迭代器与列表比较,迭代器是惰性计算的,不需要事先准备好集合中的所有元素,仅仅在迭代至某个元素时才计算该元素。适合用于遍历一些大的文件或集合,这样更节省内存。
缺点:
  (1)永远无法获取迭代器的长度,使用不如列表利用索引取值灵活。
  (2)迭代器中的内容是一次性的,并且用next()取值,只能往前走,不能向后退。

3、检查

1)借助用模块查看查看可迭代对象与迭代器对象
    from collections import Iterable,Iterator

isinstance(数据类型,Iterable) 查看是否是可迭代对象

 from collections import Iterable,Iterator

 s='hello'
l=[1,2,3]
t=(1,2,3)
d={'a':1}
set1={1,2,3,4}
f=open('a.txt') # 都是可迭代的,看能不能加上_iter_()
s.__iter__()
l.__iter__()
t.__iter__()
d.__iter__()
set1.__iter__()
f.__iter__()
print(isinstance(s,Iterable))
print(isinstance(l,Iterable))
print(isinstance(t,Iterable))
print(isinstance(d,Iterable))
print(isinstance(set1,Iterable))
print(isinstance(f,Iterable))

执行结果:

True
True
True
True
True
True

isinstance(数据类型,Iterator) 查看是否是迭代器

 #查看是否是迭代器 简单些就是看能不能调用_next_(),除文件,其他的都不能调用。
from collections import Iterable,Iterator s='hello'
l=[1,2,3]
t=(1,2,3)
d={'a':1}
set1={1,2,3,4}
f=open('a.txt') print(isinstance(s,Iterator))
print(isinstance(l,Iterator))
print(isinstance(t,Iterator))
print(isinstance(d,Iterator))
print(isinstance(set1,Iterator))
print(isinstance(f,Iterator))

执行结果:

 False
False
False
False
False
True

4、小结

  膜拜如此强大的  for 循环!!!看能否for循环遍历,就能知道是否是可迭代对象……吊炸天!

  可以使用for循环进行迭代的对象都是可迭代(Iterable)类型!可以调用next()方法的对象都是迭代器(Iterator)类型!

  next(迭代器),就能取迭代器中的值,每次只执行一次,从头开始向前取一个值。取多个就要用到多个next()(超出迭代器中值的个数会报错)或是for循环。

  python 给字典内置,就将证明可迭代。只要有_iter_函数,加()就能运行,将字典的keys重新赋值,生成一个返回值,将返回值重新定义就生成了一个迭代器。

三、生成器

1、定义:

  关于生成器,可以解释为带有yield的函数就被称为生成器。带有yield的函数不再是一个普通函数,不同于while 死循环、for 循环这种一次性创建完整的庞大的序列打印输出,他在循环的过程中值是不断推算不断生成的,一边循环一边计算的机制。可以理解成:加入了yield函数的循环,所有的执行过程都会在yield函数这里停顿进行判定或是开始,当执行一周再次走到yield函数这里时,会再次停顿进行判定或是开始。

  值得注意的是,生成器是可迭代对象,也是迭代器对象。生成器的本质,就是将函数做成了一个迭代器,取名为生成器

2、生成器与return有何区别?

1)return只能执行一次函数就彻底结束了,而yield能返回多次值。

2)生成器就是一个函数,这个函数内包含有yield这个关键字

3)由于生成器是函数类型的迭代器,可以用next() 分步触发函数,也可以for循环。

①next()触发的情况!

 from collections import Iterator
#生成器就是一个函数,这个函数内包含有yield这个关键字
def test():
print('one')
yield 1 #return 1
print('two')
yield 2 #return 2
print('three')
yield 3 #return 3
# print('four')
# yield 4 #return 4
# print('five')
# yield 5 #return 5 g=test() #函数运行返回一个值
print(g) #打印这个值,显示是生成器类型
print(isinstance(g,Iterator)) #查看类型 是否是迭代器
# g.__iter__() #可以使用iter()函数
# g.__next__() #可以使用next()函数打印值
#next()函数会触发生函数的运行,next()一次就触发一次打印一个值。
print(next(g))# next()函数触发生函数运行,打印一个值。
print(next(g))# next()函数触发生成器运行,打印下个值。
print(next(g))# next()函数触发生成器运行,打印下个值。
# print(next(g))# next()函数触发生成器运行,打印下个值。
# print(next(g))# next()函数触发生成器运行,打印下个值。

执行结果如下:

 <generator object test at 0x00000000025CB6D0>
True
one
1
two
2
three
3

②for循环

 def test():
print('one')
yield 1 #return 1
print('two')
yield 2 #return 2
print('three')
yield 3 #return 3
g=test()
for i in g:
print(i)

执行结果与上边一致。

3、yield 函数到底干了什么事情:
  1)yield 把函数变成生成器 ---> 迭代器;
  2)用return 返回只能返回一次,而yield返回多次;
  3)函数在暂停以及继续下一次运行时的状态,是由yield保存。

 def countdown(n):
print('start coutdown')
while n > 0:
yield n #
n-=1
print('done')
g=countdown(5)
print(g)
#for 循环方式
for i in g: #iter(g)
print(i)
# while 循环方式
# while True:
# try:
# print(next(g))
# except StopIteration:
# break
#由于迭代器内的参数是一次性的,输出二选一,while注意要判定异常。
#

执行结果:

 <generator object countdown at 0x000000000260B258>
start coutdown
5
4
3
2
1
done

4、yield函数应用

  1)实现 linux 中 tail -f /tmp/a.txt 的功能(及监听文件,实时刷新文件的动态,插入内容即刻打印显示,没有就暂停在当前位置)

 import time
def tail(file_path):
with open(file_path,'r') as f: #以读的方式打开文件
f.seek(0,2) #读取最后一行的文本
while True: #循环,一直判断
line=f.readline() #一次读一行
if not line: #判断,没有值的话,停顿0.3秒,再回去
time.sleep(0.3)
continue
else:
# print(line,end='') #打印一行,此行结尾的换行不打印。不用yield函数的话。
yield line # 有文本的话,打印文本,然后停在当前位置
g=tail('/tmp/a.txt') #文件路径
print(next(g)) # 打印 实时监听
# for line in g:
# print(line)

2)实现 linux 中 tail -f /tmp/a.txt |grep 'error'的功能(及监听文件,实时刷新文件的动态,实现过滤的功能,及添加进去的内容有'error'打印显示,没有不显示)

形象的比喻下:数据流就像水流一样,在一个管道上源源不断的从左往右一直传值。有带着标签的数据流就显示出来,没有就不显示。

 #/usr/bin/env python
import time
#定义阶段:定义俩生成器函数
def tail(file_path): #管道左边的传值
with open(file_path,'r') as f:
f.seek(0,2) #一直读取最后一行
while True:
line=f.readline()
if not line:
time.sleep(0.3)
# print('====>')
continue
else:
#print(line,end='')
yield line
#管道右边的过滤
def grep(pattern,lines): # 定义函数(参数)分别为:(过滤的内容,左边写入的一行数据流)
for line in lines: #对写入的每行进行遍历循环
if pattern in line: #判断是否有匹配上的行
yield line # return line #调用阶段:得到俩生成器对象
g1=tail('/tmp/a.txt') #为管道左边的传值(要操作的文件)一直监听传入的最后一行的数据
g2=grep('error',g1) #为管道右边的传值(要过滤的数据) #next触发执行g2生成器函数 有符合条件的就打印
for i in g2:
print(i)

5、协程函数

  如果在一个函数内部yield的使用方式是表达式形式的话,如x=yield,那么该函数成为协程函数。

  生成器代码执行过程中,碰到yield 程序就会暂停,既然yield 以表达式的形式出现在函数中,就表明将 yield 暂停时从外部所携带来的值 传给等号左边的变量。

  整个代码再通过next()函数触发,从yield处开始往下走,代码循环一圈之后,又回到yield这儿停止,以此循环。整个代码通过外部的send()函数给yield传值。

 #吃包子代码!
def eater(name): #定义一个名称函数 name 为人名
print('%s start to eat food' %name)
food_list=[] #清单
while True:
food=yield food_list # 将yeild从外部接收的值传给food
print('%s get %s ,to start eat' %(name,food))
food_list.append(food) #将food添加到清单中
print('done') #结束 e=eater('钢蛋') #赋值人名
#print(e)
print(next(e)) # 开始 触发函数,到yield暂停,将当前值存到food_list中,有一个返回值,打印。
print(e.send('包子')) #为yield传值 拿一个值,将值传给yield当前所对应的变量 food
print(e.send('韭菜馅包子'))#为yield传值
print(e.send('大蒜包子'))#为yield传值

执行结果:

 钢蛋 start to eat food
[]
钢蛋 get 包子 ,to start eat
['包子']
钢蛋 get 韭菜馅包子 ,to start eat
['包子', '韭菜馅包子']
钢蛋 get 大蒜包子 ,to start eat
['包子', '韭菜馅包子', '大蒜包子']

Py修行路 python基础 (十一)迭代器 与 生成器的更多相关文章

  1. Py修行路 python基础 (二十一)logging日志模块 json序列化 正则表达式(re)

    一.日志模块 两种配置方式:1.config函数 2.logger #1.config函数 不能输出到屏幕 #2.logger对象 (获取别人的信息,需要两个数据流:文件流和屏幕流需要将数据从两个数据 ...

  2. Py修行路 python基础 (二十五)线程与进程

    操作系统是用户和硬件沟通的桥梁 操作系统,位于底层硬件与应用软件之间的一层 工作方式:向下管理硬件,向上提供接口 操作系统进行切换操作: 把CPU的使用权切换给不同的进程. 1.出现IO操作 2.固定 ...

  3. Py修行路 python基础 (十二) 协程函数应用 列表生成式 生成器表达式

    一.知识点整理: 1.可迭代的:对象下有_iter_方法的都是可迭代的对象 迭代器:对象._iter_()得到的结果就是迭代器 迭代器的特性: 迭代器._next_() 取下一个值 优点: 1.提供了 ...

  4. Py修行路 python基础 (十三)匿名函数 与 内置函数

    一.匿名函数  1.定义: 匿名函数顾名思义就是指:是指一类无需定义标识符(函数名)的函数或子程序. 2.语法格式:lambda 参数:表达式 lambda语句中,开头先写关键字lambda,冒号前是 ...

  5. Py修行路 python基础 (十九)面向对象进阶(下)

    item系列 __slots__方法 __next__ 和 __iter__实现迭代器  析构函数 上下文管理协议 元类一.item系列 把对象操作属性模拟成字典的格式.  例如:对象名['key'] ...

  6. Py修行路 python基础(二)变量 字符 列表

    变量 容器 变量名 标记 数据的作用 字符编码 二进制位 = bit1个二进制位是计算机里的最小表示单元 1个字节是计算机里最小的存储单位 8bits = 1Byte =1字节1024Bytes = ...

  7. Py修行路 python基础 (十四)递归 及 面向对象初识及编程思想

    一.递归 1.定义: 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. (1)递归就是在过程或函数里调用自身: (2)在使用递归策略时,必须有一个明确的递归结束条件 ...

  8. Py修行路 python基础 (十五)面向对象编程 继承 组合 接口和抽象类

    一.前提回忆: 1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均 ...

  9. Py修行路 python基础 (十六)面向对象编程的 继承 多态与多态性 封装

    一.继承顺序: 多继承情况下,有两种方式:深度优先和广度优先 1.py3/py2 新式类的继承:在查找属性时遵循:广度优先 继承顺序是多条分支,按照从左往右的顺序,进行一步一步查找,一个分支走完会走另 ...

随机推荐

  1. poj3311 状压dp+floyd

    先floyd预处理一遍dis,枚举所有状态,dp[ i ] [ j ]表示 以  j  为终点的状态 i 使用最小的时间 #include<map> #include<set> ...

  2. java JVM 随笔

    先说重点: 对象在堆区 方法在栈区 变量在方法区,常量池在方法区 为什么要了解Java 虚拟机 ? 这个问题一直困惑了我很长一段时间,其实在我们开发的过程中,即使我们不了解JVM也能正常的开发,但是当 ...

  3. SVN 的搭建及使用(二)VisualSVN Server建立版本库,以及VisualSVN和TortoiseSVN的使用

    上一篇介绍了VisualSVN Server和TortoiseSVN的下载,安装,汉化.这篇介绍一下如何使用VisualSVN Server建立版本库,以及VisualSVN和TortoiseSVN的 ...

  4. 记录下jplayer的简单demo

    jplay一个播放器的工具包,依赖于jquery或者zepto,有zepto所以相当于是PC和移动都支持. 它的官方文档为:http://www.jplayer.cn/ 同时也推出的react的支持包 ...

  5. Ubuntu和Windows文件Samba共享

    1.在Ubuntu下配置Samba共享文件夹/work和/work1 1.1.安装samba sudo apt-get install samba

  6. macOS 下 Visual Studio Code(VSCODE)安装配置及应用

    Visual Studio Code 重新定义了 Code 编辑. 在任何操作系统上编辑和调试应用程序内置 Git 支持1000 种以上的扩展免费和开源 为什么使用VSCODE? 我们来看看以下功能: ...

  7. ng 双向数据绑定

    1.方向1:model->View模型数据绑定到视图 绑定的方式:①双花括号 ②常见的ng指令(ngRepeat ngIf ngShow....) 效果:数据一旦绑定到视图上,随着数据的修改,视 ...

  8. 打印机无法使用且无法重新安装,提示spooler service is not running

    使用场景:之前安装好的打印服务今天突然无法使用,列表里面找不到打印机,于是重新安装,得到以下错误: The local print spooler service is not running. Pl ...

  9. LINUX命令—让人喜爱的find

    FIND命令的让人喜爱的地方在于其后面跟着的 –exec  可以执行其他linux命令 这点太让人高兴了,不过他的结尾要带一个特殊的结构 {} \: 说说实例:

  10. [QT] Tab键切换焦点顺序

    在Qt Designer 中点击“编辑Tab顺序“按钮进入编辑Tab顺序模式,如下图: 之后界面会变成这样: 随处点击右键即可弹出菜单,选择  "制表符顺序列表..." 出现 剩下 ...