码迷,mamicode.com
首页 > 其他好文 > 详细

迭代器VS生成器

时间:2018-07-18 00:34:07      阅读:165      评论:0      收藏:0      [点我收藏+]

标签:返回   方式   输出   send   one   保存到文件   流程   file   set   

迭代器,python里提供类似装饰器一样的一种语法。

# 依赖下标循环的方法

l = [‘a‘,‘b‘,‘c‘,‘d‘,‘e‘]
i = 0
while i < len(l):
    print(l[i])
    i+=1

# for循环形式迭代
for i in range(len(l)):
    print(l[i])

迭代器

只要对象本身有__iter__方法,那它就是可迭代的,只要执行这个方法,它的返回值就是迭代器,这个返回值就有个__next__方法

dic = {‘a‘:1,‘b‘:2,‘c‘:3}
print(dir(dic))
i = dic.__iter__()
print(dir(i))
print(i.__next__())
print(i.__next__())
print(i.__next__())
print(i.__next__())
# 当取的值多于元素本身会抛出StopIteration错误,也可以理解为终止信号

# 为避免爆出异常可以这么做
i = iter(d)
while True:
    try:
        print(print(next(i))
    excet StopIteration:
        break
        
# python里面的for循环的方式不是按照下标而是将你传入的对象变成迭代器,去__next__

在文件中,文件句柄即时迭代器,也是可迭代对象

为什么要用迭代器

  1. 如果像字典,集合,这种无序的又或者文件这种没有索引的,你没有办法像下标那样的取值
  2. 迭代器的取值方式是统一的,大家都是按照next的方式取值,迭代器的方式取值占内存比列表这种索引取值更节省内存,他next()一下才会生成一个值属于惰性计算。

迭代器缺点

  1. 迭代器无法统计有多长,只有到最后一步才能知道多长。指定取值的话,必须一步步的取值下去才能取到。所以使用不灵活
  2. 迭代器是一次性取值,不能回头。

了解就好了哈

查看可迭代对象

from collections import Iterable,Iterator
s = "hello"
l = [1,2,3]
t = (1,2,3)
d = {‘a‘:1}
set1 = {1,2,3,4}
f = open(‘a.txt‘)

s.__iter__()
l.__iter__()
t.__iter__()
d.__iter__()
set1.__iter__()
f.__iter__()
f.__iter__()
print(isinstance(s,Iterable))

生成器

  • 生成器就是一个函数,这个函数内包含有yield这个关键字,生成器是用来生成值的。 生成器也是一种迭代器所以可以 next(g)。==把函数做成一个迭代器,这种函数就叫做生成器。==
  • 生成器与return的区别,return只能执行一次而yield可以执行多次,返回多次值
  • yield 是把函数变成了迭代器,它使函数可迭代,函数也能够拥有yield使用
from collections import Iterator
def test():
    print(‘first‘)
    yield 1
    yield 2
    yield 3

g= test()
print(g)    #g是一个函数
print(isinstance(g,Iterator))    #判断类型
print(next(g))      
print(next(g))


for i in g:
    print(i)
得到
<generator object test at 0x02FC84B0>
True
first
1
2
3

通过debug得知

print(next(g))   #打印first和1,first是test()打印的,1是print(next(g)打印的
print(next(g))   #打印2


for i in g:     #打印3,因为此时迭代器已经走到了第3个yield
    print(i)

注意以下例子,函数内带了while循环,上面那个例子是不带while循环的

生成流程案例



def test(n):
    print(‘start‘)
    while n>0:
        yield n
        n -= 1
    print(‘done‘)

g = test(6)

for i in g:
    print(i)

生成器场景案例

如果文件内没有error就一直打印====>

如果有error 就打印error那行

如果文件在末尾添加了error,也会打印出来

这是一个监控程序,可以监控log日志,如果日志新添加了error告警,那么print(i)就可以替换成其他的东西,比如发短信等


import time
#定义阶段:定义俩生成器函数
def tail(file_path):
    with open(file_path,‘r‘) as f:
        f.seek(0,2)  #移动到最后一行,2表示以文件末尾为原点进行计算
        while True:
            line=f.readline()
            if not line:  #如果监控到空行,没有那一行了就执行这里
                time.sleep(0.3)
                print(‘====>‘) #这里打印表示他在一直监控这个文件,程序不是卡死
                continue
            else:# 如果这行是有内容的就执行这里,并返回line
                yield line

def grep(pattern,lines):
    for line in lines:
        if pattern in line:
            yield line

#调用阶段:得到俩生成器对象
g1=tail(‘a.txt‘)  #打开要监控的文件,得到生成器对象
g2=grep(‘error‘,g1) #输入关键字,得到生成器对象

#next触发执行g2生成器函数
for i in g2:
    print(i)

通过debug可知,grep成为了生成器,可以被for循环,i 等于g2里面返回的line

g2里面的line怎么生成,由g1生成,于是运行tail函数

如果把f.seek(0,2)去掉,那么就是从第一行开始,line就是有内容的,(如果保留seek,那就是认为当文件更新了最后一行的内容,添加了新的日志之后,line就有内容了),于是函数家就会到else: yield line

然后这个line会送到grep里面判断if pattern in line,如果匹配,那就输出line

如果不匹配,那就在grep里面的for循环继续执行,继续跳到tail去生成line

于是for i in g2:的i 就是line

然后for循环继续执行,找下一个i是啥

继续执行g2 的for循环 跳到 tail生成line,但是此时最后一行为空,那么就不停的print(‘====>‘),continue 到 line=f.readline() ,直到又有新的日志添加保存到文件之后,才执行yield line

我一开始对with open的理解有问题

我以为是with open 一次,就打开一次,然后数据就存内存里面不再变化

如果文件有过变化,保存后,还得再with open一次

实际上不是这样的,而是如果文件对象f被调用到了,那就会自动再将变化后的东西更新到内存里面,相当于每调用一次文件对象f,就执行了一次with open 操作

这样当日志文件更新被保存后,新的日志内容就能进入 line = f.readline() 里面了

协程

协程是用到.send方法,它会去给yield传个值,达到管道的作用,他的作用和next方法相似,但是多了一个传值的步骤

def eater(name)
    print(‘%s start to eat food‘ %(name)
    while True:
        food = yield
        print(‘%s get %s ,to start eat‘%(name,food)
    print(‘done‘)


name_yield = eater(‘alex‘)
next(e)
name_yield.send(‘包子‘)
name_yield.send(‘烧麦‘)
name_yield.send(‘饺子‘)


def eater(name)
    print(‘%s start to eat food‘ %(name)
    food_list = []
    while True:
        food = yield food_list
        print(‘%s get %s ,to start eat‘%(name,food)
        food_list.append(food)
    print(‘done‘)


name_yield = eater(‘alex‘)
next(e)
name_yield.send(‘包子‘)
name_yield.send(‘烧麦‘)
name_yield.send(‘饺子‘)

迭代器VS生成器

标签:返回   方式   输出   send   one   保存到文件   流程   file   set   

原文地址:https://www.cnblogs.com/welljoy/p/9326835.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!