python 迭代器和生成器
1、迭代器
迭代和可迭代协议
迭代器的定义:
我们可以对一个列表进行for循环,而不能对一个整型数据进行for循环。
for i in [1,2,3,4]:
print(i) #1,2,3,4
for i in 1234 print(i) 结果: Traceback (most recent call last): File "test.py", line 4, in <module> for i in 1234: TypeError: 'int' object is not iterable
报错的结果中出现了一个单词iterable,中文翻译为可迭代的。
字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的。如何证明呢?
from collections import Iterable l = [1,2,3,4] t = (1,2,3,4) d = {1:2,3:4} s = {1,2,3,4} print(isinstance(l,Iterable)) print(isinstance(t,Iterable)) print(isinstance(d,Iterable)) print(isinstance(s,Iterable))
结果都是True
综合以上:可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代。
可迭代协议
可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。
使用dir可以查看不同数据类型有哪些方法:
print(dir([])) #告诉我列表拥有的所有方法
print(dir({})) #告诉我字典拥有的所有方法
print(dir('')) #告诉我字符串拥有的所有方法
ret=set(dir([]))&set(dir({}))&set(dir(''))&set(dir(range(10)))
print(ret)
所有的结果中都有__iter__
得出结论:可以被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法。只要是能被for循环的数据类型,就一定拥有__iter__方法。
print([1,2].__iter__()) 结果 <list_iterator object at 0x1024784a8>
执行了list([1,2])的__iter__方法,我们好像得到了一个list_iterator,现在我们又得到了一个新名词——iterator。中文名称:迭代器。
迭代器
''' dir([1,2].__iter__())是列表迭代器中实现的所有方法,dir([1,2])是列表中实现的所有方法,都是以列表的形式返回给我们的,为了看的更清楚,我们分别把他们转换成集合, 然后取差集。 ''' #print(dir([1,2].__iter__())) #print(dir([1,2])) print(set(dir([1,2].__iter__()))-set(dir([1,2]))) 结果: {'__length_hint__', '__next__', '__setstate__'}
我们看到在列表迭代器中多了三个方法,那么这三个方法都分别做了什么事呢?
iter_l = [1,2,3,4,5,6].__iter__() #获取迭代器中元素的长度 print(iter_l.__length_hint__()) #根据索引值指定从哪里开始迭代 print('*',iter_l.__setstate__(4)) #一个一个的取值 print('**',iter_l.__next__()) print('***',iter_l.__next__())
我们使用__next__方法来获取值。
在for循环中,就是在内部调用了__next__方法才能取到一个一个的值。
那接下来我们就用迭代器的next方法来写一个不依赖for的遍历。
l=[1,2,3] iterator = l.__iter__() print(iterator.__next__()) #1 print(iterator.__next__()) #2 print(iterator.__next__()) #3
那现在我们就使用while循环实现了原本for循环做的事情,我们是从谁那儿获取一个一个的值呀?是不是就是l_iter?好了,这个l_iter就是一个迭代器
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
2、生成器
迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行
Python中提供的生成器:
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器函数
一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
import time def genrator_fun1(): a = 1 print('现在定义了a变量') yield a b = 2 print('现在又定义了b变量') yield b g1 = genrator_fun1() print('g1 : ',g1) #打印g1可以发现g1就是一个生成器 print('-'*20) #我是华丽的分割线 print(next(g1)) time.sleep(1) #sleep一秒看清执行过程 print(next(g1))
生成器2:
def wahaha(): ''' wahaha自增2000000 :return: ''' for i in range(2000000): yield '哇哈哈%s'%i ret = wahaha() print(ret) print(ret.__next__()) #打印哇哈哈0 print(ret.__next__()) #打印哇哈哈1 print(ret.__next__()) #打印哇哈哈2 print(ret.__next__()) #打印哇哈哈3 print(ret.__next__()) #打印哇哈哈4 print(ret.__next__()) #打印哇哈哈5
一次打印完所有哇哈哈(0-1999999)
def wahaha(): ''' wahaha自增20000000 :return: ''' for i in range(20000000): yield '哇哈哈%s'%i ret = wahaha() for i in ret: print(i)
send方法:
def generator(): print(12) content = yield 1 print('+++++',content) print(23) ret = yield 2 print("====",ret) print(45) yield 3 g = generator() res = g.__next__() print(res) res = g.send('amy') #send的效果和next一样 print(res) res = g.send('zzm') #send的效果和next一样
print(res) #运行结果: 12 1 +++++ amy 23 2 ==== zzm 45 3
更多应用:
import time def tail(filename): f = open(filename) f.seek(0, 2) #从文件末尾算起 while True: line = f.readline() # 读取文件中新的文本行 if not line: time.sleep(0.1) continue yield line tail_g = tail('tmp') for line in tail_g: print(line)
def average(): sum = 0 count = 0 avg = 0 while True: num =yield avg sum += num count += 1 avg = sum/count avg_g = average() avg_g.__next__() avg1 = avg_g.send(1) #发送一个数:1 avg1 = avg_g.send(2) #发送一个数:2 avg1 = avg_g.send(3) #发送一个数:3 avg1 = avg_g.send(4) #发送一个数:4 avg1 = avg_g.send(5) #发送一个数:5 print(avg1)
def oter(func): def inner(*args,**kwargs): g = func(*args,**kwargs) g.__next__() return g return inner @oter def generator(): sum = 0 count = 0 avg = 0 while True: num = yield avg sum += num count +=1 avg = sum/count avg_g = generator() ret = avg_g.send(10) print(ret) ret = avg_g.send(20) print(ret)
yield from
yield from可以从一个容器类中依次取值
优化前: def generater(): l=[3,334,54,54,3,5,2,4] s='2345dsdfghbvc' for i in l: yield i for i in s: yield i g = generater() #得到生成器g for i in g: print(i 优化后: def generater(): l=[3,334,54,54,3,5,2,4] s='2345dsdfghbvc' yield from l #从l中循环取值 yield from s #从2中循环取值 g = generater() #生成器g for i in g: print(i)
列表推导式:
g = (i for i in range(10)) for i in g: print(i)
g = (i for i in range(10)) #10以内依次打印 print(i) lis = [i*i for i in range(10)] #10以内的值平方 print(lis) li=[i*i for i in range(30) if i%3==0] #有条件的 能整除3的值得平方 print(li)
# mcase={'a':10,'b':34} # mcase_frequency = {mcase[k]:k for k in mcase} # print(mcase_frequency) 对字典中的键值对进行翻转 # mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3} # mcase_new = {k.lower():mcase.get(k.lower(),0)+mcase.get(k.upper(),0) for k in mcase} # print(mcase_new) 对字典中的所有键值对,合并大小写对应的value值,将k统一成小写
计算列表中每个值的平方,自带去重功能 squared = {x**2 for x in [1, -1, 2]} print(squared) # Output: set([1, 4])

浙公网安备 33010602011771号