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

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

  • 相关阅读:
    grunt in webstorm
    10+ Best Responsive HTML5 AngularJS Templates
    响应式布局
    responsive grid
    responsive layout
    js event bubble and capturing
    Understanding Service Types
    To add private variable to this Javascript literal object
    Centering HTML elements larger than their parents
    java5 新特性
  • 原文地址:https://www.cnblogs.com/caoj/p/7875949.html
Copyright © 2011-2022 走看看