zoukankan      html  css  js  c++  java
  • 迭代器与装饰器

      一. iter&yield迭代器

        1.1 iter

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

       1.2 yield

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

       1.3  生成器的异步应用

    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", 'zengchunyun', 'peiqi')  # 创建一个生产者对象,并传入三个消费者名字

      二. 装饰器

        2.1 首先了解一下函数

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

        通过上述总结一句话,函数不加括号是没有执行的,这时的这个函数名只是一个函数对象,而加括号后,函数将会执行函数体代码,

      最终这个函数则是函数执行完后的返回的对象

        2.2 装饰器

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

     1 def wrapper(func):
     2     print(func)  # 打印参数
     3     return func  # 返回参数
     4 
     5 
     6 @wrapper  # 使用装饰器
     7 def index():
     8     print('welcome')
     9 
    10 
    11 index()
    12 
    13 # 执行顺序是,解释器从上往下读取代码
    14 # 遇到函数时,只加载定义的函数对象,并不执行函数体代码
    15 # 然后遇到装饰器@wrapper时
    16 # 解释器会跳到这个wrapper函数
    17 # 然后执行这个wrapper函数内部代码
    18 # 我们通过观察得知,这个wrapper函数一定会传入一个参数,因为测试发现,不传入一个参数,程序执行会抛出需要一个参数的异常错误.
    19 # 通过分析这个参数,发现这个参数打印结果是一个函数对象
    20 # 然后wrapper函数体代码执行完毕后,继续往下执行,遇到函数index,
    21 # 也是只是加载这个函数对象,并不执行内部函数体代码
    22 # 当遇到代码index()时,结合到我们之前积累的函数基础知识,
    23 # 这个写法实际是开始执行一个函数,所以解释器会跳到指定的index函数对象
    24 # 然后开始执行这个函数体代码块,
    25 # 整个执行过程就结束了
    26 
    27 
    28 # 以下为代码执行结果
    29 # <function index at 0x1010ed0d0>
    30 # welcome

        可能你对上面这个过程还是不够了解装饰器,那么我们再通过下面这个例子说明装饰器的工作原理

     1 def wrapper(func):
     2     print(func)  # 打印参数
     3     # return func  # 返回参数 ,现在注释掉这个返回值
     4 
     5 
     6 @wrapper  # 使用装饰器
     7 def index():
     8     print('welcome')
     9 
    10 print(type(index))  # 加上一句输出类型代码的语句
    11 index()
    12 
    13 #我们在上面这个例子中,把函数wrapper中的返回值给注释掉了,代码执行结果
    14 
    15 # function index at 0x1011ed0d0>
    16 # <class 'NoneType'>
    17 # Traceback (most recent call last):
    18 #   File "/Users/zengchunyun/PycharmProjects/s12/day4/noteext.py", line 68, in <module>
    19 #     index()
    20 # TypeError: 'NoneType' object is not callable
    21 
    22 # 首先我们来分析一下这个结果
    23 # 你会不会很惊讶?我们只是针对第一个例子的代码仅仅只是注释掉一个返回值而已,代码就不能工作了
    24 
    25 # 首先解释器还是从上往下读取代码
    26 # 遇到函数时,只加载定义的函数对象,并不执行函数体代码
    27 # 然后遇到装饰器@wrapper时
    28 # 解释器会跳到这个wrapper函数
    29 # 然后执行这个wrapper函数内部代码
    30 # 通过分析这个参数,发现这个参数打印结果是一个函数对象
    31 # 然后wrapper函数体代码执行完毕后,继续往下执行,遇到函数index,
    32 # 也是只是加载这个函数对象,并不执行内部函数体代码
    33 # 关键点来了
    34 # 代码执行到打印对象类型的语句时,结果却是一个NoneType的类型,根据我们之前对函数的基本介绍,这里的类型应该是一个函数类型才对啊
    35 # 怎么回事呢?我们明明定义了index函数,怎么打印的类型却是NoneType类型?
    36 # 我们之前也看到只有函数没有返回值时,函数默认会返回一个None对象,故而这个对象的类型也就是NoneType类型了,
    37 # 我们仅仅只是加了一个装饰器代码@wrapper,其他都没有变,为什么会出现这个情况呢
    38 # 我们上一个例子已经说明,这个装饰器会携带一个参数,这个参数为一个函数对象,
    39 # 实际上,这个时候这个装饰器会对引用装饰器的函数,也就是我们这里的index函数进行重构,
    40 # 所以如果我们如果不返回一个函数对象时,那么这个时候的index实质是一个普通的对象,不是函数类型了
    41 # 它已经被赋予None这个值了,而None不是一个函数对象,所以就没有调用方法,就不能以括号方式执行
    42 # 这时解释器读到index()这句代码,大家依据之前的理念,都能看出这个是去执行index这个函数内部代码块的语句
    43 # 但是这个时候,解释器却在这个时候抛出异常了
    44 # 返回类型错误,TypeError: 'NoneType' object is not callable
    45 # 这个错误说我们的index执行后,是不能被调用的,只有对象类型为函数才有内置调用方法
    46 # 因为这个index已经被重构,返回值已经变成了None,也就是说,index 对象目前仅仅是一个普通标识符,不是函数

        2.3 装饰器的高级应用

          我们通过上面的事咧可知,只要代码执行到装饰器标识符,都会去执行装饰器函数体,但是这个不是我们想要的,我们希望的是

        只有我们调用引用装饰器的函数时,才去执行这个装饰器函数体,那怎么办呢?我们知道,只有类型是函数对象时,代码是不会被执行,

        只是加载到内存而已,那好吧,我们直接返回一个函数对象不就好了?

          我们来看看代码实现

     1 def wrapper(func):
     2     def inner():
     3         print(func)  # 输出是一个函数对象
     4         func()  # 这里实际是执行我们这个例子中原先定义的index函数对象的函数体
     5     return inner
     6     # print(func)  # 打印参数
     7     # return func  # 返回参数 ,现在注释掉这个返回值
     8 
     9 
    10 @wrapper  # 使用装饰器
    11 def index():
    12     print('welcome')
    13 
    14 print(type(index))  # 加上一句输出类型代码的语句
    15 index()
    16 
    17 # 首先看看这种情况
    18 # 代码执行到index()时,啥也没有,我们明明打印了一句welcome,为什么没有输出信息,这也就再次证明了,
    19 # 实质是这个index已经等价于装饰器的inner这个函数了,因为装饰器返回的是inner这个函数对象
    20 # 我们既想用装饰器,又想执行调用装饰器的内部代码时怎么办呢?
    21 # 我们已经知道,装饰器会携带一个参数,这个参数是引用装饰器对象的一个函数对象
    22 # 既然是一个函数对象,那我们是不是可以直接执行这个函数对象呢?
    23 # 答案是肯定的,所以我们直接在这个装饰器里执行这个函数对象,等于就是执行我们原先那个函数的函数体
    24 # 这个例子就满足了我们的需求,当我们不调用index函数时,得到的仅仅是一个函数对象,并不会执行函数体代码

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

     1 # 无参装饰器,有参函数
     2 def wrapper(func):
     3     def inner(name):  # 这个参数最终会传给这个函数体内部需要调用参数的对象
     4         func(name)  # 这个参数个数是由原来的函数,也就是我们这里的index函数决定参数个数的
     5     return inner
     6 
     7 
     8 @wrapper  # 使用装饰器
     9 def index(name):  # 传入一个参数
    10     print('welcome %s' % name)
    11 
    12 index('zengchunyun')
    无参装饰器,有参函数
     1 # 无参装饰器,多参函数
     2 def wrapper(func):
     3     def inner(*args):  # 使用动态参数
     4         func(*args)
     5     return inner
     6 
     7 
     8 @wrapper  # 使用装饰器
     9 def index(*args):  # 传入一个参数
    10     print('welcome %s' % ' '.join(args))
    11 
    12 index('zengchunyun', 'goodbye')
    无参装饰器,多参函数
     1 # 无参装饰器,多参函数2
     2 def wrapper(func):
     3     def inner(*args, **kwargs):  # 使用动态参数
     4         func(*args, **kwargs)
     5     return inner
     6 
     7 
     8 @wrapper  # 使用装饰器
     9 def index(*args, **kwargs):  # 传入一个参数
    10     print('welcome %s' % ' '.join(args))
    11 
    12 index('zengchunyun', 'goodbye')
    无参装饰器,多参函数2
     1 # 有参装饰器,多参函数
     2 def one():
     3     print('one')
     4 
     5 
     6 def two():
     7     print('two')
     8 
     9 
    10 def func(arg1, arg2):
    11     def wrapper(oldfunc):
    12         def inner(*args, **kwargs):  # 使用动态参数
    13             arg1()
    14             arg2()
    15             oldfunc(*args, **kwargs)
    16         return inner
    17     return wrapper
    18 
    19 
    20 @func(one, two)  # 使用装饰器
    21 def index(*args, **kwargs):  # 传入一个参数
    22     print('welcome %s' % ' '.join(args))
    23 
    24 index('zengchunyun', 'goodbye')
    25 
    26 # 解释器遇到装饰器,由于这个装饰器是一个可执行函数
    27 # 故而先执行函数,再次就成了我们所认知的普通装饰器了
    # 有参装饰器,多参函数
  • 相关阅读:
    常用记录
    golang学习笔记之UDP:server端与client端
    golang学习笔记之tcp简单实现:server端与client端
    golang学习笔记之channel
    golang学习笔记之单元测试和压力测试
    Ubuntu 18.04桌面版安装
    Nas服务器使用xampp修改端口号
    Laravel使用Supervisor管理linux自动队列
    php 设计模式学习笔记之单例模式
    终极二分查找--传说十个人写九个有bug
  • 原文地址:https://www.cnblogs.com/zengchunyun/p/5155089.html
Copyright © 2011-2022 走看看