zoukankan      html  css  js  c++  java
  • 函数之迭代器和生成器

    面向过程编程

     面向过程编程是一种以过程为中心的编程思想,分析出解决问题的步骤,然后用函数把这些步骤一步一步实现。面向过程编程,数据和对数据的操作是分离的。

    写程序时:要先想功能,分步实现

    os模块中walk输出目录中文件路径,os.walk() 方法用于通过在目录树中游走输出在目录中的文件名,向上或者向下。

    Send可以传多个值,但是必须是元组类型。面向过程的编程思想像流水线,代码简洁,体系结构。

    #应用:grep -rl 'root' /etc
    import os
    def init(func):
        def wrapper(*args,**kwargs):
            g=func(*args,**kwargs)
            next(g)
            return g
        return wrapper
    #阶段一:递归地找文件的绝对路径,把路径发给阶段二
    @init
    def search(target):
        'search file abspath'
        while True:
            start_path=yield
            g = os.walk(start_path)
            for par_dir, _, files in g:
                # print(par_dir,files)
                for file in files:
                    file_path = r'%s\%s' % (par_dir, file)
                    target.send(file_path)
    #阶段二:收到文件路径,打开文件获取获取对象,把文件对象发给阶段三
    @init
    def opener(target):
        'get file obj: f=open(filepath)'
        while True:
            file_path=yield
            with open(file_path,encoding='utf-8') as f:
                target.send((file_path,f))
    
    #阶段三:收到文件对象,for循环读取文件的每一行内容,把每一行内容发给阶段四
    @init
    def cat(target):
        'read file'
        while True:
            filepath,f=yield
            for line in f:
                res=target.send((filepath,line))
                if res:
                    break
    
    #阶段四:收到一行内容,判断root是否在这一行中,如果在,则把文件名发给阶段五
    @init
    def grep(target,pattern):
        'grep function'
        tag=False
        while True:
            filepath,line=yield tag #target.send((filepath,line))
            tag=False
            if pattern in line:
                target.send(filepath)
                tag=True
    #阶段五:收到文件名,打印结果
    @init
    def printer():
        'print function'
        while True:
            filename=yield
            print(filename)
    
    start_path1=r'C:UsersAdministratorPycharmProjectspython5期a'
    start_path2=r'C:UsersAdministratorPycharmProjectspython5期a'
    g=search(opener(cat(grep(printer(),'root'))))
    
    print(g)
    # g.send(start_path1)
    g.send(start_path2)
    
    #应用:grep -rl 'root' /etc
    View Code

    一  迭代器

        迭代的意思:类似于循环每一次重复的过程就被称为迭代的过程,提供迭代方法的容器称为迭代器。

        1.迭代器协议是指:迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。

        2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法),对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代

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

     集合的目的:去重,关系运算。

    # a={1,2,3,4,5,6,7}
    # b={41,52,5,26,7,4,2,9,}
    # print(a-b)
    

      isinstance:判断数据的类型,还可以判断是否可迭代。

      iterable:形容词  可迭代的:from collections import Iterable:用来检测一个对象是否可以迭代。

    # from collections import Iterable
    # print(isinstance('fadda',Iterable))
    

      dir:打印一种数据类型的方法 

    print(dir('asdasf'))
    

     什么叫做可迭代对象:操作的对象下面有__iter__()方法的就是可迭代对象。

     什么叫做迭代器:操作的对象下面不光有__iter__()方法的,还有__next__()方法的就是迭代器,迭代器是不用关心值得索引状态的

      iterator:迭代器:实现了能从其中一个一个的 取值出来。

    lst_iterator=[1,2,3,4,5].__iter__()
    print(lst_iterator.__next__())
    

     一切可以用for循环的基本数据类型都是可迭代对象,而不是迭代器。for循环的可以是一个可迭代对象,也可以是一个迭代器。而for循环自动为可迭代对象调用__iter__()方法。

    for i in [1,2,3,4,5,6]:
        print(i)
    

      文件本质就是一个迭代器。range()就是一个可迭代对象,可迭代对象有:字符串,列表,元组等等。

    __iter__():将一个可迭代对象转化成一个迭代器

    __next__():读取迭代器的内容,一次只能读取一行或者一个内容

    # with open('a.txt',encoding='utf-8')as f:
    #     print(f.__next__())
    #     print(f.__next__())
    #     print(f.__next__())
    #     print(f.__next__())
    #     print(f.__next__())
    # 
    # b=[41,52,5,26,7,4,2,9,]
    # bb=b.__iter__()
    # print(bb.__next__())
    # print(bb.__next__())
    # print(bb.__next__())
    # print(bb.__next__())
    # print(bb.__next__())
    

     迭代器取到没有值的时候就会报错。报的是StopIteration。

    迭代的概念:重复的过程称为迭代,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值

     迭代器的好处:1 能够对python中的基本数据类型进行统一的遍历,不需要关系每一个值是什么。

            2 惰性预算:可以节省内存。

    # for i in range(1,1000):
    #     print(i)
    #     if i == 100:
    #         break
    

    迭代器的坏处:

    无法获取长度
    一次性的,只能往后走,不能往前退

    迭代器的创建:1 天生就是一个迭代器,比如说:文件句柄

           2 后天转换成迭代器:迭代器=可迭代对象.__iter__()

    二 生成器

     什么是生成器(Gerator):生成器本质就是一个迭代器,生成器就是自己写出来的迭代器。

     什么是生成器函数:函数里有yield的关键字就是一个生成器函数。

    # def iter_1():
    #     print(111)
    #     yield 222
    #     print(333)
    #     yield 444
    # 
    # iter_2=iter_1()
    # print(iter_2.__next__())
    # print(iter_2.__next__())
    

      yield:意思和return差不多,但是yiled可以定位当前取值的位置,就是在next取值过后,就定位到取到的那个值的位置,等再次使用next取值的时候,就从第一次取值过后定位的那个位置开始取值,一个next对应一个yield。

       from:yield下的一个方法,可以简化生成器函数。用了from,就可以不用for循环。

    # def foo():
    #     for i in [1,2,3,4,5,6,7]:
    #         yield i
    # for i in foo():
    #     print(i)
    
    # def foo():
    #     yield from [1,2,3,4,5,6,7]
    # for i in foo():
    #     print(i)
    

     生成器函数在调用的时候返回来的就是一个生成器,不会执行生成器函数里面的内容。

    # def iter_1():
    #     print(111)
    #     yield 222
    #     print(333)
    #     yield 444
    # 
    # iter_2=iter_1()
    # print(iter_2)
    

     而生成器和迭代器运行的顺序是只能往前取值,而不能够回头。

      生成器的用法:1 有几个next,就取几个yield的返回值,不能超过yiled的范围。

             2 直接for循环取值:for 变量名 in 生成器名。

             3 其他数据类型进行强转,list(数据类型对象)返回一个列表,里面装着生成器的所有内容。

    for i in range(1,10):
        print(i)
    print(list(range(10,20)))
    

      注意:1 生成器函数取值必须先转换成生成器,在进行next取值。

         2 生成器的内容只能取一次,不能重复,只能向前取值;不能后退,直到取完为止。

    惰性运算:生成器里面的内容不获取,不生成。

       为函数封装好了__iter__和__next__方法,把函数的执行结果做成了迭代器

     遵循迭代器的取值方式obj.__next__(),触发的函数的执行,函数暂停与再继续的状态都是由yield保存的    

    协同程序,就是可以运行的独立函数调用,保留函数的局部变量等数据,让函数可以暂停或者挂起。并且在需要的时候会从上次执行的后面第一个地方继续执行。

    yield表达式

    yield关键字用来定义生成器(Generator),其具体功能是可以当return使用,从函数里返回一个值,不同之处是用yield返回之后,可以让函数从上回yield返回的

    地点继续执行。也就是说,yield返回函数,交给调用者一个返回值,然后再“瞬移”回去,让函数继续运行, 直到吓一跳yield语句再返回一个新的值,能多次重复使用。

    使用yield返回后,调用者实际得到的是一个迭代器对象,迭代器的值就是返回值,而调用该迭代器的next()方法会导致该函数恢复yield语句的执行环境继续往下跑,

    直到遇到下一个yield为止,如果遇不到yield,就会抛出异常表示迭代结束。

     

    1. 包含yield的函数

    假如你看到某个函数包含了yield,这意味着这个函数已经是一个Generator,它的执行会和其他普通的函数有很多不同。

    调用函数之后,print 语句并没有执行!这就是yield,那么,如何让print 语句执行呢?这就是后面要讨论的问题,通过后面的讨论和学习,就会明白yield的工作原理了。

    2. yield是一个表达式

    Python2.5以前,Python yield是一个语句,但现在2.5中,yield是一个表达式(Expression)

    假如,m=yield 5。表达式(yield 5)的返回值将赋值给m,所以,认为 m = 5 是错误的。那么如何获取(yield 5)的返回值呢?需要用到后面要介绍的send(msg)方法。

    3. 透过next()语句看原理

    现在,我们来揭晓yield的工作原理。我们知道,函数被调用后并没有执行,因为它有yield表达式,因此,我们通过next()语句让它执行。next()语句将恢复Generator执行,

    并直到下一个yield表达式处。

    当我们再次调用print(next(*))时,会继续执行,直到找到下一个yield表达式。由于后面没有yield了,因此会拋出异常:

    4. send(msg) 与 next(),

    了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)。其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的

    值进去,而next(*)不能传递特定的值,只能传递None进去。因此,我们可以看做next(*) 和 *.send(None) 作用是一样的。

         

    需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有Python yield语句来接收这个值。

    5. send(msg) 与 next()的返回值

    send(msg) 和 next()是有返回值的,它们的返回值很特殊,返回的是下一个yield表达式的参数。

    其实是每次都调用了alist.Next(),而每次alist.Next()的返回值正是yield的参数,即我们开始认为被压进去的东东。

    6 可以为wield表达式创建一个装饰器,就是在多个表达式开始执行前不用每个都输入next(*) 和 *.send(None),为next(*) 和 *.send(None)创建一个装饰器,在每个wield表达式

    函数名称前加上@装饰器的名称。装饰器为此提供了方便,可以多个一起使用。

    三 文件补充小知识

    文件下seek()传的值:0:光标的相对位置;1:当前光标的位置;2:将光标一道最后一个一个位置。

    # def aa(bb,dd):
    #     with open(bb,'w',encoding='utf-8')as f :
    #         f.write(dd)
    # aa('a.txt','afdfd
    fadsa')
    # def tail(cc):
    #     f=open(cc,encoding='utf-8')
    #     f.seek(0,2)
    #     while True:
    #         line=f.readline()
    #         if not line:
    #             continue
    #         yield line
    # tail_g=tail('a.txt')
    # for line in tail_g:
    #     print(line,end='')
    #
    
  • 相关阅读:
    10/28总结
    10/27总结
    10/26总结
    10/25总结
    10/24总结
    毕业设计第二周整理规划
    毕业设计第一周第五天完成情况汇总
    毕业设计第一周第四天完成情况汇总
    毕业设计第一周第三天完成情况汇总
    毕业设计第一周第二天完成情况汇总
  • 原文地址:https://www.cnblogs.com/fangjie0410/p/7474409.html
Copyright © 2011-2022 走看看