Python 学习笔记: 迭代器和生成器

迭代器

 迭代器遵循迭代器协议:必须拥有__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]
[]
View Code

注意: 生成器的值只能取一次, 取出后就没有值了。所以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” 行运行

 

posted @ 2018-11-15 17:07  程序猿🌽  阅读(126)  评论(0编辑  收藏  举报