zoukankan      html  css  js  c++  java
  • python基础(8)--迭代器、生成器、装饰器

    1.迭代器

      迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件

    特点:

      1).访问者不需要关心迭代器内部的结果,仅需要通过next()方法不断去取下一个内容

      2).不能随机访问集合中的某个值,只能从头到尾依次访问

      3).访问到一半时不能往回退

      4).便于循环比较大的数据集合,节省内存

      Python内置函数iter就是一个简单的帮助我们创造一个迭代器的内置函数。

     1 names = iter(['a' , 'b', 'c'])
     2 print(names)
     3 print(names.__next__())
     4 print(names.__next__())
     5 print(names.__next__())
     6 print(names.__next__())
     7 
     8 #第一个输出打印迭代器对象
     9 #第二三四次next方法每次都去获取迭代对象的值,每次往下取一个值,直到取完
    10 #第五次输出    print(names.__next__())
    11 #StopIteration,因为迭代器里面的对象已经取完了,所以出现这个异常
    View Code

      其实事实上,我们很少会使用__next__()方法一个一个的去取值,多数情况下都是使用for in循环进行遍历

    2.生成器

      一个函数调用时返回一个迭代器,这个函数就叫做生成器(generator)。如果函数中包含yield语法,那这个函数就会变成生成器

    def fei(num):
        x, y, z = 0, 0, 1
        while x < num:
            if x == 0:  #当第一次循环时,返回数列0
                yield y #生成器返回一个迭代对象
            else:   #否则返回z
                yield z
                y, z = z, y + z
            x += 1
    
    print(fei(9).__next__())    #只从迭代器里取一次值
    
    
    for i in fei(9):    #通过遍历获取迭代器内容
        print(i)

      生成器异步应用

    import time
    def consumer(name): #定义消费者函数
        print("%s 准备吃包子啦!" % name)  #打印消费者名字
        while True:
            baozi = yield   #当代码运行到这里时,返回一个迭代器对象,当对象第二次调用时,会接收值并赋值给baozi
            print("包子[%s]来了,被[%s]吃了!" % (baozi, name))  #打印当前使用的迭代器对象,及接收得值
    
    
    def producer(*name):    #定义一个生产者函数,并使用动态参数
        if len(name) == 3:  #当传入三个参数时,调用三次消费者函数
            c = consumer(name[0])
            c2 = consumer(name[1])
            c3 = consumer(name[2])
        c.__next__()    #通过函数内置方法获取迭代器内部元素
        c2.__next__()
        c3.__next__()
        print("老子开始准备做包子啦!")
        for i in range(1, 11):
            time.sleep(1)
            print("做了%s个包子!" % len(name))
            c.send(i)   #通过迭代器方法的send属性给生成器传送值
            c2.send(i)
            c3.send(i)
    
    
    
    producer('alex', 'tom', 'eric') #创建一个生产者对象,并传入三个消费者名字
    
    
    
    '''
    执行结果
    alex 准备吃包子啦!
    tom 准备吃包子啦!
    eric 准备吃包子啦!
    老子开始准备做包子啦!
    做了3个包子!
    包子[1]来了,被[alex]吃了!
    包子[1]来了,被[tom]吃了!
    包子[1]来了,被[eric]吃了!
    做了3个包子!
    包子[2]来了,被[alex]吃了!
    包子[2]来了,被[tom]吃了!
    包子[2]来了,被[eric]吃了!
    做了3个包子!
    包子[3]来了,被[alex]吃了!
    包子[3]来了,被[tom]吃了!
    包子[3]来了,被[eric]吃了!
    做了3个包子!
    包子[4]来了,被[alex]吃了!
    包子[4]来了,被[tom]吃了!
    包子[4]来了,被[eric]吃了!
    ...
    
    '''

    3.装饰器

      先来了解一下函数

    def func():
        print('hello world')
    
    print(func) #第一次输出的是一个函数对象
    print(type(func))   #第二次输出的是一个类型为函数
    func()  #第三次才是执行函数
    print(func())   #第四次是执行函数后,并打印函数的返回结果,函数没有指定返回内容,所以是默认返回值:None
    print(type(func())) #第五次也是先执行函数,再打印返回值对象的类型,可以看出,返回值对象类型是NoneType
    
    
    '''
    第一次
    <function func at 0x0000000002D771E0>
    第二次
    <class 'function'>
    第三次
    hello world
    第四次
    hello world
    None
    第五次
    hello world
    <class 'NoneType'>
    
    '''

      以上总结出,函数不加括号是没有执行的,这时这个函数名只是一个函数对象,加括号后,函数将会执行函数体代码,最终这个函数则是函数执行完成后返回的对象

      有了上面这个基础,我们再来看看装饰器

    def wrapper(func):
        print(func) #打印参数
        return func  #返回参数
    
    
    
    @wrapper
    def index():
        print('hello')
    
    
    index()
    
    #执行顺序是,解释器从上往下读取代码
    #遇到函数时,只加载定义的函数对象,并不执行函数体代码
    #然后遇到装饰器@wrapper时
    #解释器就会跳到这个wrapper函数
    #然后执行这个wrapper函数内部代码
    #通过观察可发现,这个wrapper函数一定会传入一个参数,因为测试发现,不传入一个参数,程序执行会抛出需要一个参数的异常错误
    #通过分析这个参数,发现这个参数打印结果是一个函数对象
    #然后wrapper函数体代码执行完毕后,继续往下执行,遇到函数index
    #也是加载这个函数对象,并不执行内部函数体代码
    #当遇到代码index()时,结合之前积累的函数基础知识
    #这个写法实际是开始执行一个函数,所以解释器会跳到指定的index函数对象
    #然后开始执行这个函数代码块
    #整个执行过程结束
    
    
    
    #执行结果
    #<function index at 0x0000000002D272F0>
    #hello

      下面的列子具体说明装饰器的工作原理

    def wrapper(func):
        print(func) #打印参数
        #return func`#返回参数,现在注释掉这个返回值
    
    @wrapper
    def index():
        print('hello')
    
    
    print(type(index))  #加上一句输出类型代码语句
    index()
    
    
    #执行结果
    #<function index at 0x0000000002D972F0>
    #<class 'NoneType'>
    #TypeError: 'NoneType' object is not callable
    
    #首先分析下这个结果
    #会不会很惊讶,只是针对上面的例子仅仅注释掉一个返回值而已,代码就不能工作了
    #首先解释器还是从上往下读取代码
    #遇到函数时,只加载定义函数对象,并不执行函数替代吗
    #然后遇到装饰器@wrapper时
    #解释器会跳到这个wrapper函数
    #然后执行这个wrapper函数内部代码
    #通过分析这个参数,发现这个参数打印结果是一个函数对象
    #然后wrapper函数体代码执行完毕后,继续往下执行,遇到函数index
    #也是只加载函数对象,并不执行内部函数体代码
    #关键点来了
    #代码执行到打印对象类型语句时,结果却是一个NoneType类型,根据之前对函数的基本介绍,这里的类型应该是一个函数类型才对呀
    #为什么呢?命名定义了index函数,打印index类型却是NoneType类型?
    #我们之前也看到只有函数没有返回值时,函数默认会返回一个None对象,故而这个对象的类型也就是NoneType类型了
    #仅仅是加了一个装饰器代码@wrapper,就出现这个情况
    #我们上一个例子已经说明,这个装饰器会携带一个参数,这个参数为一个函数对象
    #实际上,这时候这个装饰器会对引用装饰器的函数,也就是我们这里的index函数进行重构
    #所以如果我们不反悔一个函数对象时,name这个时候的index实质是一个普通的对象,不是函数类型了
    #它已经被赋予None这个值了,而None不是一个函数对象,所以就没有调用方法,就不能以括号方式执行
    #这是解释器督导index()这句代码,依据之前的知识,都能看出这个是取执行index这个函数内部代码快的语句
    #但是执行时解释却抛了异常
    #返回错误类型,TypeError: 'NoneType' object is not callable
    #这个错误说明我们index执行后,是不能被调用的,只有对象类型为函数才有内置调用方法
    #因为这个index已经被重构,返回值已经变成了None,也就是说index对象目前仅仅是一个普通标识符,不是函数

      装饰器的高级应用

      通过上面的示例可以知道,只要代码执行到装饰器标识符,都会去执行装饰器函数体,但是这个不是我们想要的,我们希望是只有我们调用引用装饰器函数时,才去执行这个装饰器函数体,那怎么办呢?我们知道,只有类型是函数对象时,代码是不会被执行,只是加载到内存而已,那装饰器函数体就可以直接返回一个函数对象

      示例

    def wrapper(func):
        def inner():
            print(func) #输出是一个函数对象
            func()  #这里实际是执行例子中原先定义的index函数对象的函数体
        return inner
    
    @wrapper
    def index():
        print('hello')
    
    
    print(type(index))
    index()
    
    
    #这个例子满足了需求,当我们不调用index函数时,得到的仅仅是一个函数对象,并不会执行函数代码
    #当执行index函数,实际执行装饰器函数体里传入的index函数,就是执行的它本身

      有参数的函数,或装饰器实现传参实例

    #无参装饰器,有参函数
    def wrapper(func):
        def inner(name):    #这个参数最终会传给这个函数体内部需要调用参数的对象
            func(name)  #这个参数个数是由原来函数,也就是index函数决定的
        return inner
    
    
    @wrapper
    def index(name):    #传入一个参数
        print('hello %s' % name)
    
    
    index('alex')
    #无参装饰器,多参函数
    def wrapper(func):
        def inner(*args):   #使用动态参数
            func(*args)
        return inner
    
    @wrapper
    def index(*args):   #传入一个参数
        print('hello %s' % ''.join(args))
    
    
    index('alex', 'eric')
    #无参装饰器,多参函数2
    def wrapper(func):
        def inner(*args, **kwargs): #使用动态参数
            func(*args, **kwargs)
        return inner
    
    @wrapper
    def index(*args, **kwargs):
        print('hello %s'% ' '.join(args))
    
    index('alex', 'eric')
    #有参装饰器,多参函数
    def one():
        print('one')
    
    def two():
        print('two')
    
    def func(arg1, arg2):
        def wrapper(oldfunc):
            def inner(*args, **kwargs):
                arg1()
                arg2()
                oldfunc(*args, **kwargs)
            return inner
        return wrapper
    
    @func(one, two)
    def index(*args, **kwargs):
        print('hello %s' % ''.join(args))
    
    index('alex', 'eric')
    
    #解释器遇到装饰器,由于这个装饰器是一个可执行函数
    #故而先执行韩式,再次就成了我们所认知的普通装饰器了
    #执行结果
    #one
    #two
    #hello alexeric

      注:本篇博客转载至博客园-曾春云的笔记,再敲一遍是为了加深印象

  • 相关阅读:
    项目结队开发---NABC分析(成员)
    梦断代码读后感(二)
    梦断代码读后感(一)
    首尾相连的循环数组求其子数组最大值
    子数组求和之大数溢出
    梦断代码读后感——终结
    软件工程结对开发——一维最大子数组求和溢出问题
    结对开发——求环形一维数组最大子数组的和
    结对开发——电梯调度问题需求分析
    软件工程——求二维数组所有子矩阵的和的最大值
  • 原文地址:https://www.cnblogs.com/caoj/p/7875949.html
Copyright © 2011-2022 走看看