Python迭代器和生成器

一、什么是迭代器协议?

对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个stopiteration异常,以终止迭代(只能往后走不能往前进)

可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个iter()方法)

协议是一种约定,可迭代对象实现了迭代器协议,Python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。

Python中强大的for循环机制

例如字符串、列表、元组、字典、集合、文件对象等都不是可迭代对象,只不过在for循环时,调用了他们内部的_iter_方法,把他们变成了可迭代对象

然后for循环调用可迭代对象的_next_方法去取值,而且for循环会捕捉Stopiteration异常,以终止迭代。

含有__iter__方法的就是可迭代对象

#__iter__()生成可迭代对象,返回一个对象地址。之后利用__next__()进行遍历
x='hello'
iter_test=x.__iter__()
print(iter_test)      ----->  <str_iterator object at 0x000001AEE54225C0>
print(iter_test.__next__())     h
print(iter_test.__next__())     e
print(iter_test.__next__())     l
print(iter_test.__next__())     l
print(iter_test.__next__())     0


l=[1,2,3]
for i in l:    # i_l=l.__iter__()    i_l.__next__()
    print(i)
====>>    1
          2
          3

#__iter__()遵循迭代器协议,生成可迭代对象
iter_l=l.__iter__()  
print(iter_l.__next__())
print(iter_l.__next__())
=====>> 1
        2
#不使用for循环,完成元组、列表等等的遍历,while不能进行字典集合的遍历
l=[1,2,3,4]
index=0
while index<len(l):
    print(l[index])
    index+=1
#迭代器即迭代的工具,那什么是迭代呢?
#迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
while True: #只是单纯地重复,因而不是迭代
    print('===>') 
    
l=[1,2,3]
count=0
while count < len(l): #迭代
    print(l[count])
    count+=1
#1、为何要有迭代器?
对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器

#2、什么是可迭代对象?
可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
'hello'.__iter__
(1,2,3).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
{'a','b'}.__iter__
open('a.txt').__iter__

#3、什么是迭代器对象?
可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象

文件类型是迭代器对象
open('a.txt').__iter__()
open('a.txt').__next__()


#4、注意:
迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

三 、迭代器对象的使用

三 迭代器对象的使用

dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
iter_dic.__iter__() is iter_dic #True

print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
# print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志

#有了迭代器,我们就可以不依赖索引迭代取值了
iter_dic=dic.__iter__()
while 1:
    try:
        k=next(iter_dic)
        print(dic[k])
    except StopIteration:
        break
        
#这么写太丑陋了,需要我们自己捕捉异常,控制next,python这么牛逼,能不能帮我解决呢?能,请看for循环

四 、for循环

#基于for循环,我们可以完全不再依赖索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:
    print(dic[k])

#for循环的工作原理
#1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
#2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
#3: 重复过程2,直到捕捉到异常StopIteration,结束循环

五 、迭代器的优缺点

#优点:
  - 提供一种统一的、不依赖于索引的迭代方式
  - 惰性计算,节省内存
#缺点:
  - 无法获取长度(只有在next完毕才知道到底有几个值)
  - 一次性的,只能往后走,不能往前退

 六  生成器初探

一 什么是生成器?

可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用自己内置的__iter__方法),所以生成器就是可迭代对象

插入知识点:

(三元表达式:'SB' if name == 'hello' else 'handsome')

 

 

生成器在Python中的表现形式:(Python有两种不同的方式提供生成器)

        1.生成器函数:常规函数定义,但是使用yield语句而不是return语句返回结果    yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行

        2.生成器表达式:类似于列表缩写,但是生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

  yield:
  1).把函数做成迭代器  2).对比return,可以返回多次值,可以挂起/保存函数的运行状态

为何使用生成器之生成器优点?

 

#只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码

def func():
    print('====>first')
    yield 1
    print('====>second')
    yield 2
    print('====>third')
    yield 3
    print('====>end')

g=func()
print(g) #<generator object func at 0x0000000002184360> 

生成器和列表解析的区别

   生成器格式最外层是一个(),列表解析格式最外层是一个[]

   生成器是将数据不用一次读取,而列表解析是一次读取所有数据(耗内存)

   生成器返回一个生成器对象(不能直接循环),列表解析返回的是一个新列表

 

  生成器好处:可以存储一次用一次,而不是全部产生后在用,可以在__next__()中间加一些代码
  列表解析缺点:
      缺点一:占空间大

      缺点二:效率低

 

列表解析(一次生成所有,然后使用)

#列表解析

egg_list=[]
for i in range(10):
    egg_list.append('鸡蛋%s'  %i)
print(egg_list)

l = ["鸡蛋%s" %i for i in range(10)] 
print(l)     =======>>以上两者功能是相同的,但第二种代码少,一次性生成10个蛋在内存中

li = ['鸡蛋%s' %i for i in range(10) if i>5]
print(li)     =====>> ['鸡蛋6','鸡蛋7','鸡蛋8','鸡蛋9']

生成器函数(生成器好处是可以存储一次用一次,而不是全部产生后在用)

#生成器函数用法   
def product_baozi():
    for i in range(100):
        print("正在生产包子")
        yield '一提包子%s' %i
        print('开始卖包子')
pro_g=product_baozi()
print(pro_g.__next__())
print(pro_g.__next__())
print(pro_g.__next__())
print(pro_g.__next__())
#本题实现人口普查功能,找总人口
def get_population():
    with open('xxx_new','r',encoding='utf-8') as f:
        for i in f:
            yield i
g = get_population()

#实现输出总人口功能(多行实现)
res=0
for p in g:
    p_dic=eval(p)
   # print(p_dic['population'])
    res+=p_dic['population']
print(res)

#两行实现输出总人口功能
all_pop=sum(eval(i)['population'] for i in g)
print(all_pop)

 

三 练习

1、自定义函数模拟range(1,7,2)

2、模拟管道,实现功能:tail -f access.log | grep '404'

#题目一:
def my_range(start,stop,step=1):
    while start < stop:
        yield start
        start+=step

#执行函数得到生成器,本质就是迭代器
obj=my_range(1,7,2) #1  3  5
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj)) #StopIteration

#应用于for循环
for i in my_range(1,7,2):
    print(i)

#题目二
import time
def tail(filepath):
    with open(filepath,'rb') as f:
        f.seek(0,2)
        while True:
            line=f.readline()
            if line:
                yield line
            else:
                time.sleep(0.2)

def grep(pattern,lines):
    for line in lines:
        line=line.decode('utf-8')
        if pattern in line:
            yield line

for line in grep('404',tail('access.log')):
    print(line,end='')

#测试
with open('access.log','a',encoding='utf-8') as f:
    f.write('出错啦404\n')

 

四 协程函数

#yield关键字的另外一种使用形式:表达式形式的yield
def eater(name):
    print('%s 准备开始吃饭啦' %name) food_list=[] while True: food=yield food_list print('%s 吃了 %s' % (name,food)) food_list.append(food) g=eater('egon') g.send(None) #对于表达式形式的yield,在使用时,第一次必须传None,g.send(None)等同于next(g) g.send('蒸羊羔') g.send('蒸鹿茸') g.send('蒸熊掌') g.send('烧素鸭') g.close() g.send('烧素鹅') g.send('烧鹿尾')

 

五 练习
1、编写装饰器,实现初始化协程函数的功能

2、实现功能:grep  -rl  'python'  /etc

#题目一:
def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper
@init
def eater(name):
    print('%s 准备开始吃饭啦' %name)
    food_list=[]
    while True:
        food=yield food_list
        print('%s 吃了 %s' % (name,food))
        food_list.append(food)

g=eater('egon')
g.send('蒸羊羔')

#题目二:
#注意:target.send(...)在拿到target的返回值后才算执行结束
import os
def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper

@init
def search(target):
    while True:
        filepath=yield
        g=os.walk(filepath)
        for dirname,_,files in g:
            for file in files:
                abs_path=r'%s\%s' %(dirname,file)
                target.send(abs_path)
@init
def opener(target):
    while True:
        abs_path=yield
        with open(abs_path,'rb') as f:
            target.send((f,abs_path))
@init
def cat(target):
    while True:
        f,abs_path=yield
        for line in f:
            res=target.send((line,abs_path))
            if res:
                break
@init
def grep(pattern,target):
    tag=False
    while True:
        line,abs_path=yield tag
        tag=False
        if pattern.encode('utf-8') in line:
            target.send(abs_path)
            tag=True
@init
def printer():
    while True:
        abs_path=yield
        print(abs_path)


g=search(opener(cat(grep('你好',printer()))))
# g.send(r'E:\CMS\aaa\db')
g=search(opener(cat(grep('python',printer()))))
g.send(r'E:\CMS\aaa\db')

 

转载于:https://www.cnblogs.com/xtznb/p/10794828.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值