python迭代器与生成器(二)
一、什么是迭代?
迭代通俗的讲就是一个遍历重复的过程。
维基百科中 迭代(Iteration) 的一个通用概念是:重复某个过程的行为,这个过程中的每次重复称为一次迭代。具体对应到Python编程中就是,对于一个可迭代对象,比如Python中的list、tuple、string、dictionary,set等,使用某种循环结构来遍历其中的元素,这种遍历就是迭代。
1 #对列表进行遍历!
2
3 l=['a','b','c','d','e']
4
5 #while循环的方式
6 i=0
7 while i < len(l):
8 print(l[i])
9 i+=1
10
11 #for 循环的方式
12 for i in range(len(l)):
13 print(l[i])
二、迭代器
1、迭代器定义
首先先明确 可迭代对象
在现所学的数据类型中,只有 文件 是迭代器,其他的数据类型:元组,字符串,字典,集合,列表都是 可迭代对象。
判断是否可迭代:只要判断对象本身是否内置了_iter_方法,那它就是可迭代的。
1 #可迭代的:只要对象本身有__iter__方法,那它就是可迭代的
2 d={'a':1,'b':2,'c':3}
3 d.__iter__ #iter(d)
可迭代对象实现了 __iter__ 和 __next__ 方法,这两个方法对应内置函数 iter() 和 next() 。__iter__ 方法返回可迭代对象本身,这使得他既是一个可迭代对象同时也是一个迭代器。
1 #执行对象下的__iter__方法,就会得到一个返回值,得到的返回值就是迭代器。
2 # 使用next()就能依次取出迭代器中的值(在迭代器元素个数之内,超出会报错,也是一次性的元素,不可重复取值),这样就不用再依赖下标的方式。
3 d={'a':1,'b':2,'c':3}
4 d.__iter__ #iter(d)
5
6 i=d.__iter__() #返回值,迭代器
7
8 print(i.__next__()) #print(next(i))
9 print(i.__next__())
10 print(i.__next__()) #3个元素,依次取三次,由于迭代器中的元素是一次性的,超出个数会报错!
11 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:循环
1 l=['a','b','c','d','e']
2 i=l.__iter__()
3 while True:
4 try: #监听代码是否会报异常 StopIteration
5 print(next(i))
6 except StopIteration: #判断异常是否为 StopIteration,是break
7 break
执行结果如下:
1 a
2 b
3 c
4 d
5 e
2)for 循环 :for 循环本质就是内部封装了迭代器,对 可迭代的对象 进行遍历取值,同时会在遇到异常捕获的时候会自行处理,所以for 循环作用在迭代器上不报错。
1 d={'a':1,'b':2,'c':3}
2 print(d.__iter__())
3 for k in d: #d.__iter__()
4 print(k)
执行结果如下:
1 #集合及文件 for 循环 迭代形式
2 s={1,2,3,4}
3 for i in s:
4 print(i)
5 #a.txt ="aaaaaa bbb cccc eee ffffff"
6 with open('a.txt','r') as f:
7 for line in f:
8 print(line.strip())
执行结果如下:
3)此处注意一个小点:关于文件的认知! 由于文件本身就是一个迭代器,当给文件添加_iter_函数的时候,对其迭代器本身是不冲突的!
1 f=open('a.txt','r')
2 print(f)
3 print(f.__iter__())
4 #迭代器执行iter,得到的还是迭代器本身!结果不冲突。
执行结果:
1 <_io.TextIOWrapper name='a.txt' mode='r' encoding='cp936'>
2 <_io.TextIOWrapper name='a.txt' mode='r' encoding='cp936'>
2、为什么要用迭代器
优点:
(1)迭代器提供了一种不依赖于索引的取值方式,这样就可以遍历那些没有索引的可迭代对象(字典,集合,文件)。
(2)迭代器与列表比较,迭代器是惰性计算的,不需要事先准备好集合中的所有元素,仅仅在迭代至某个元素时才计算该元素。适合用于遍历一些大的文件或集合,这样更节省内存。
缺点:
(1)永远无法获取迭代器的长度,使用不如列表利用索引取值灵活。
(2)迭代器中的内容是一次性的,并且用next()取值,只能往前走,不能向后退。
3、检查
1)借助用模块查看查看可迭代对象与迭代器对象
from collections import Iterable,Iterator
isinstance(数据类型,Iterable) 查看是否是可迭代对象
1 from collections import Iterable,Iterator
2
3 s='hello'
4 l=[1,2,3]
5 t=(1,2,3)
6 d={'a':1}
7 set1={1,2,3,4}
8 f=open('a.txt')
9
10 # 都是可迭代的,看能不能加上_iter_()
11 s.__iter__()
12 l.__iter__()
13 t.__iter__()
14 d.__iter__()
15 set1.__iter__()
16 f.__iter__()
17 print(isinstance(s,Iterable))
18 print(isinstance(l,Iterable))
19 print(isinstance(t,Iterable))
20 print(isinstance(d,Iterable))
21 print(isinstance(set1,Iterable))
22 print(isinstance(f,Iterable))
执行结果:
True
True
True
True
True
True
isinstance(数据类型,Iterator) 查看是否是迭代器
1 #查看是否是迭代器 简单些就是看能不能调用_next_(),除文件,其他的都不能调用。
2 from collections import Iterable,Iterator
3
4 s='hello'
5 l=[1,2,3]
6 t=(1,2,3)
7 d={'a':1}
8 set1={1,2,3,4}
9 f=open('a.txt')
10
11 print(isinstance(s,Iterator))
12 print(isinstance(l,Iterator))
13 print(isinstance(t,Iterator))
14 print(isinstance(d,Iterator))
15 print(isinstance(set1,Iterator))
16 print(isinstance(f,Iterator))
执行结果:
1 False
2 False
3 False
4 False
5 False
6 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()触发的情况!
1 from collections import Iterator
2 #生成器就是一个函数,这个函数内包含有yield这个关键字
3 def test():
4 print('one')
5 yield 1 #return 1
6 print('two')
7 yield 2 #return 2
8 print('three')
9 yield 3 #return 3
10 # print('four')
11 # yield 4 #return 4
12 # print('five')
13 # yield 5 #return 5
14
15 g=test() #函数运行返回一个值
16 print(g) #打印这个值,显示是生成器类型
17 print(isinstance(g,Iterator)) #查看类型 是否是迭代器
18 # g.__iter__() #可以使用iter()函数
19 # g.__next__() #可以使用next()函数打印值
20 #next()函数会触发生函数的运行,next()一次就触发一次打印一个值。
21 print(next(g))# next()函数触发生函数运行,打印一个值。
22 print(next(g))# next()函数触发生成器运行,打印下个值。
23 print(next(g))# next()函数触发生成器运行,打印下个值。
24 # print(next(g))# next()函数触发生成器运行,打印下个值。
25 # print(next(g))# next()函数触发生成器运行,打印下个值。
执行结果如下:
1 <generator object test at 0x00000000025CB6D0>
2 True
3 one
4 1
5 two
6 2
7 three
8 3
②for循环
1 def test():
2 print('one')
3 yield 1 #return 1
4 print('two')
5 yield 2 #return 2
6 print('three')
7 yield 3 #return 3
8 g=test()
9 for i in g:
10 print(i)
执行结果与上边一致。
3、yield 函数到底干了什么事情:
1)yield 把函数变成生成器 ---> 迭代器;
2)用return 返回只能返回一次,而yield返回多次;
3)函数在暂停以及继续下一次运行时的状态,是由yield保存。
1 def countdown(n):
2 print('start coutdown')
3 while n > 0:
4 yield n #1
5 n-=1
6 print('done')
7 g=countdown(5)
8 print(g)
9 #for 循环方式
10 for i in g: #iter(g)
11 print(i)
12 # while 循环方式
13 # while True:
14 # try:
15 # print(next(g))
16 # except StopIteration:
17 # break
18 #由于迭代器内的参数是一次性的,输出二选一,while注意要判定异常。
19 #
执行结果:
1 <generator object countdown at 0x000000000260B258>
2 start coutdown
3 5
4 4
5 3
6 2
7 1
8 done
4、yield函数应用
1)实现 linux 中 tail -f /tmp/a.txt 的功能(及监听文件,实时刷新文件的动态,插入内容即刻打印显示,没有就暂停在当前位置)
1 import time
2 def tail(file_path):
3 with open(file_path,'r') as f: #以读的方式打开文件
4 f.seek(0,2) #读取最后一行的文本
5 while True: #循环,一直判断
6 line=f.readline() #一次读一行
7 if not line: #判断,没有值的话,停顿0.3秒,再回去
8 time.sleep(0.3)
9 continue
10 else:
11 # print(line,end='') #打印一行,此行结尾的换行不打印。不用yield函数的话。
12 yield line # 有文本的话,打印文本,然后停在当前位置
13 g=tail('/tmp/a.txt') #文件路径
14 print(next(g)) # 打印 实时监听
15 # for line in g:
16 # print(line)
2)实现 linux 中 tail -f /tmp/a.txt |grep 'error'的功能(及监听文件,实时刷新文件的动态,实现过滤的功能,及添加进去的内容有'error'打印显示,没有不显示)
形象的比喻下:数据流就像水流一样,在一个管道上源源不断的从左往右一直传值。有带着标签的数据流就显示出来,没有就不显示。
1 #/usr/bin/env python
2 import time
3 #定义阶段:定义俩生成器函数
4 def tail(file_path): #管道左边的传值
5 with open(file_path,'r') as f:
6 f.seek(0,2) #一直读取最后一行
7 while True:
8 line=f.readline()
9 if not line:
10 time.sleep(0.3)
11 # print('====>')
12 continue
13 else:
14 #print(line,end='')
15 yield line
16 #管道右边的过滤
17 def grep(pattern,lines): # 定义函数(参数)分别为:(过滤的内容,左边写入的一行数据流)
18 for line in lines: #对写入的每行进行遍历循环
19 if pattern in line: #判断是否有匹配上的行
20 yield line # return line
21
22 #调用阶段:得到俩生成器对象
23 g1=tail('/tmp/a.txt') #为管道左边的传值(要操作的文件)一直监听传入的最后一行的数据
24 g2=grep('error',g1) #为管道右边的传值(要过滤的数据)
25
26 #next触发执行g2生成器函数 有符合条件的就打印
27 for i in g2:
28 print(i)
5、协程函数
如果在一个函数内部yield的使用方式是表达式形式的话,如x=yield,那么该函数成为协程函数。
生成器代码执行过程中,碰到yield 程序就会暂停,既然yield 以表达式的形式出现在函数中,就表明将 yield 暂停时从外部所携带来的值 传给等号左边的变量。
整个代码再通过next()函数触发,从yield处开始往下走,代码循环一圈之后,又回到yield这儿停止,以此循环。整个代码通过外部的send()函数给yield传值。
1 #吃包子代码!
2 def eater(name): #定义一个名称函数 name 为人名
3 print('%s start to eat food' %name)
4 food_list=[] #清单
5 while True:
6 food=yield food_list # 将yeild从外部接收的值传给food
7 print('%s get %s ,to start eat' %(name,food))
8 food_list.append(food) #将food添加到清单中
9 print('done') #结束
10
11 e=eater('钢蛋') #赋值人名
12 #print(e)
13 print(next(e)) # 开始 触发函数,到yield暂停,将当前值存到food_list中,有一个返回值,打印。
14 print(e.send('包子')) #为yield传值 拿一个值,将值传给yield当前所对应的变量 food
15 print(e.send('韭菜馅包子'))#为yield传值
16 print(e.send('大蒜包子'))#为yield传值
执行结果:
1 钢蛋 start to eat food
2 []
3 钢蛋 get 包子 ,to start eat
4 ['包子']
5 钢蛋 get 韭菜馅包子 ,to start eat
6 ['包子', '韭菜馅包子']
7 钢蛋 get 大蒜包子 ,to start eat
8 ['包子', '韭菜馅包子', '大蒜包子']
python迭代器与生成器(二)的更多相关文章
- Python 迭代器和生成器(转)
Python 迭代器和生成器 在Python中,很多对象都是可以通过for语句来直接遍历的,例如list.string.dict等等,这些对象都可以被称为可迭代对象.至于说哪些对象是可以被迭代访问的, ...
- 一文搞懂Python迭代器和生成器
很多童鞋搞不懂python迭代器和生成器到底是什么?它们之间又有什么样的关系? 这篇文章就是要用最简单的方式让你理解Python迭代器和生成器! 1.迭代器和迭代过程 维基百科解释道: 在Python ...
- Python - 迭代器与生成器 - 第十三天
Python 迭代器与生成器 迭代器 迭代是Python最强大的功能之一,是访问集合元素的一种方式. 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问 ...
- 怎么理解Python迭代器与生成器?
怎么理解Python迭代器与生成器?在Python中,使用for ... in ... 可以对list.tuple.set和dict数据类型进行迭代,可以把所有数据都过滤出来.如下: ...
- python迭代器与生成器详解
迭代器与生成器 迭代器(iterator)与生成器(generator)是 Python 中比较常用又很容易混淆的两个概念,今天就把它们梳理一遍,并举一些常用的例子. for 语句与可迭代对象(ite ...
- python -迭代器与生成器 以及 iterable(可迭代对象)、yield语句
我刚开始学习编程没多久,对于很多知识还完全不知道,而有些知道的也是一知半解,我想把学习到的知识记录下来,一是弥补记忆力差的毛病,二也是为了待以后知识能进一步理解透彻时再回来做一个补充. 参考链接: 完 ...
- Python迭代器,生成器--精华中的精华
1. 迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大 ...
- Python—迭代器与生成器
迭代器与生成器 生成器(generator) 先来了解一下列表生成器: list = [i*2 for i in range(10)] print(list)>>>>[0, 2 ...
- python迭代器,生成器
1. 迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大 ...
- Python迭代器和生成器你学会了吗?
在了解什么是迭代器和生成器之前,我们先来了解一下容器的概念.对于一切皆对象来说,容器就是对象的集合.例如列表.元祖.字典等等都是容器.对于容器,你可以很直观地想象成多个元素在一起的单元:而不同容器的区 ...
随机推荐
- 什么是EJB
学习EJB可以加深对J2EE平台的认识. 百科定义EJB: 被称为java企业bean,服务器端组件,核心应用是部署分布式应用程序.用它部署的系统不限定平台.实际上ejb是一种产品,描述了应用组件要解 ...
- Go 结构体和map等数据结构转json字符串
Go语言中使用json包中的 Marshal() 函数将数据结构转成json字符串,源代码: func Marshal(v interface{}) ([]byte, error) { e := ne ...
- linux ioctl
Linux内核的ioctl函数学习 我这里说的ioctl函数是在驱动程序里的,因为我不知道还有没有别的场合用到了ioctl, 所以就规定了我们讨论的范围.为什么要写篇文章呢,是因为我前一阵子被ioct ...
- for迭代序列的三种方式
while循环是条件性的,for循环是迭代性的. for循环会访问所有迭代对象中的所有元素,并在所有条目都结束后结束循环. for循环迭代序列有三种基本的方式,分别是通过序列项迭代.通过索引迭代.通过 ...
- SHELL —— grep命令+正则表达式
一 什么是正则 正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法.或者说:正则就是用来描述一类事物的规则. 生活中处处都是正则: 比如我们描述:4条腿 你可能会想 ...
- change the color of a disabled TEdit?
change the color of a disabled TEdit? Author: P. Below Category: VCL {Question:How can I change the ...
- Unity,如何阻塞当前函数一段时间
public class Example : MonoBehaviour { IEnumerator Example() { print(Time.time); ); print(Time.time) ...
- SVN插件下载地址及更新地址
SVN插件下载地址及更新地址,你根据需要选择你需要的版本.现在最新是1.8.xLinks for 1.8.x Release:Eclipse update site URL: http://subcl ...
- hadoop10---消息队列
java消息队列 BlockingQueue也是java.util.concurrent下的主要用来控制线程同步的工具.锁也是用来控制线程同步的,释放锁的时候谁拿到了锁是没有预测的,所以谁拿到了锁是不 ...
- FutureTask、Fork/Join、 BlockingQueue
我们之前学习创建线程有Thread和Runnable两种方式,但是两种方式都无法获得执行的结果. 而Callable和Future在任务完成后得到结果. Future是一个接口,表示一个任务的周期 ...