迭代器
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
list1 = [1,2,3,4,5]
it = list1.__iter__()
it 就是个迭代器。
it.__next()__
输出 1
it.__next()__
输出 2
.。。。。。等等, 每次输出1个值。
生成器
生成器函数:
一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
示例1: 计算移动平均值
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__() print(avg_g.send(10)) print(avg_g.send(20)) print(avg_g.send(30)) print(avg_g.send(20)) print(avg_g.send(10))
输出如下:
10.0
15.0
20.0
20.0
18.0
练习2 , 用装饰器把上例改写。 在装饰器中激活生成器, 这样在函数的调用中, 不需要执行__next_() 方法。
def init(func): def inner(*args, **kwargs): g = func(*args, **kwargs) #被装饰的函数执行后返回生成器 g.__next__() #激活生成器 return g #返回的是激活的生成器 (在生成器里已经停在在yield) return inner @init 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__() print(avg_g.send(10)) print(avg_g.send(20)) print(avg_g.send(30)) print(avg_g.send(20)) print(avg_g.send(10))
练习3 编写寻找文件中含有关键字符串的文件。
例如, 找到所有文件中含有python 的文件的文件名。 (在Linux下这是一个有用的工具)
备注: 尚未看懂, 待研究。
mport os def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwargs) next(g) return g return wrapper @init def list_files(target): while 1: dir_to_search=yield for top_dir,dir,files in os.walk(dir_to_search): for file in files: target.send(os.path.join(top_dir,file)) @init def opener(target): while 1: file=yield fn=open(file) target.send((file,fn)) @init def cat(target): while 1: file,fn=yield for line in fn: target.send((file,line)) @init def grep(pattern,target): while 1: file,line=yield if pattern in line: target.send(file) @init def printer(): while 1: file=yield if file: print(file) g=list_files(opener(cat(grep('python',printer())))) g.send('/test1') 协程应用:grep -rl /dir
yield from 语法示例
逐个返回
def generator(): a = 'abcdefghi' b = '1234567' yield from a yield from b g = generator() for i in g: print(i, end=' ') for i in g: print(i, end=' ')
输出如下:
a b c d e f g h i 1 2 3 4 5 6 7
send 方法注意事项:
第一次不能用send, 最后一次不能接收数据。
练习4 , 下面输出什么?
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)) print(list(g2))
输出如下:
[0, 1, 2, 3]
[]
注意: 生成器的值只能取一次, 取出后就没有值了。所以g2是空列表。
练习5. 生成器表达式嵌套的情况。
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]
此题提示: 生成器表达式嵌套时, 把循环拆开逐一推导。要不然脑子很乱, 推导不好的。Eva-J 老师说的。
上面的程序可以拆解为:
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)
n = 1
g=(add(n,i) for i in test())
n = 10
g=(add(n,i) for i in (add(n,i) for i in test())) # 这时, n=10 ###Line A 行
print(list(g)) # 生成器此时才开始取值, 从 “Line A” 行运行