字符串、列表、元祖、集合、字典都是可迭代的,数字是不可迭代的。(可以用 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就是可迭代,False就是不可迭代
可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义就是内部实现了__iter__方法,即可迭代对象中封装有__iter__方法。
迭代器:用变量调__iter__后就可以生成一个迭代器,迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
- l = [1,2,3,4]
- l_iter = l.__iter__()#l_iter只是一个接受的变量
- item = l_iter.__next__()#利用迭代器取值
- print(item)#1
- item = l_iter.__next__()
- print(item)#2
- item = l_iter.__next__()
- print(item)#3
- item = l_iter.__next__()
- print(item)#4
- item = l_iter.__next__()
- print(item)#超出限度,报错
上步在最后出现了报错情况,为了使程序不报错,可以在取完了的最后将其终止掉:
- l = [1,2,3,4]
- l_iter = l.__iter__()
- while True:
- try:
- item = l_iter.__next__()
- print(item)
- except StopIteration:
- break
生成器:(本质就是一个迭代器,不过是由程序员写出来的才叫生成器,内置的就叫迭代器)
1. 生成器函数:常规函数定义,但是,使用 yield 语句而不是 return 语句返回结果。yield 语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行,惰性。
2. 生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
简易生成器:
- import time
- def func():
- a = 1
- print('现在定义了a变量')
- yield a
- b = 2
- print('现在又定义了b变量')
- yield b
- g1 = func()
- print('g1 : ',g1) #打印g1可以发现g1就是一个生成器
- print('-'*20) #我是华丽的分割线
- print(next(g1))
- time.sleep(1) #sleep一秒看清执行过程
- print(next(g1))#每print一次next才会出来一个yield的值,不然就挂在上一个yield上不继续执行
生成器有什么好处呢?就是不会一下子在内存中生成太多数据,只有在你要的时候才会给你你要的数据
生成器应用的几个小栗子:
有关衣服订单:
- def produce():
- """生产衣服"""
- for i in range(2000000):
- yield "生产了第%s件衣服"%i
- product_g = produce()
- print(product_g.__next__()) #要一件衣服
- print(product_g.__next__()) #再要一件衣服
- print(product_g.__next__()) #再要一件衣服
- num = 0
- for i in product_g: #要一批衣服,比如5件
- print(i)
- num +=1
- if num == 5:
- break
- #到这里我们找工厂拿了8件衣服,我一共让我的生产函数(也就是produce生成器函数)生产2000000件衣服。
- #剩下的还有很多衣服,我们可以一直拿,也可以放着等想拿的时候再拿
View Code
生成器监听文件输入的栗子:
- 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)
View Code
计算移动平均值(类似于年化收益):
- def averager():
- total = 0
- day = 0
- average = 0
- while True:
- term = yield average
- total += term
- day += 1
- average = total/day
- g_avg = averager()
- next(g_avg)
- print(g_avg.send(10))
- print(g_avg.send(12))
- print(g_avg.send(13))
View Code
yield from 可以在实行 for 循环的效果的同时将代码变少:
- def gen1():
- for c in 'AB':
- yield c
- for i in range(3):
- yield i
- print(list(gen1()))#['A','B',1,2,3]
- #简化版本
- def gen2():
- yield from 'AB'
- yield from range(3)
- print(list(gen2()))#['A','B',1,2,3]
- #为了彰显高富帅本质,一口气买了十个茶叶蛋,将他们依次排开并编号,拍照发到朋友圈
- egg_list=['茶叶蛋%s' %i for i in range(10)] #列表解析
- #可是这十个茶叶蛋一口气吃不完啊,要吃也就是一个一个吃,那么就吃一个拍一个照吧
- eat=('茶叶蛋%s' %i for i in range(10))#生成器表达式
- print(eat)
- print(next(eat)) #next本质就是调用__next__
- print(eat.__next__())
- print(next(eat))
高富帅与茶叶蛋
总结:
1. 把列表解析的 [] 换成 () 得到的就是生成器表达式
2. 列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.Python 使用迭代器协议,让 for 循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。
- def demo():
- for i in range(4):
- yield i
- g=demo()
- g1=(i for i in g)
- g2=(i for i in g1)
- print(list(g1))#[0,1,2,3]
- print(list(g2))#[]
面试题 1
- def add(n,i):
- return n+i
- def test():
- for i in range(4):
- yield i
- g=test()
- for n in [1,10]:
- g=(add(n,i) for i in g)
- print(list(g))#[20,21,22,23]
面试题 2
来源: http://www.bubuko.com/infodetail-2451213.html