美文网首页
三大器(装饰器,迭代器,生成器)

三大器(装饰器,迭代器,生成器)

作者: 鬼泣_89b6 | 来源:发表于2019-03-10 14:26 被阅读0次

    装饰器

    装饰器本质上就是一个闭包函数,他可以在不改变源码的情况下,增加其他的功能

    闭包:内层函数对外层函数的非全局变量的引用叫闭包,他的本质是函数的嵌套

    应用场景:小范围的需要给类增加其他功能就可以使用装饰器

    插入日志,性能测试,缓存,事务处理等场景

    原则:装饰器遵循开放封闭原则

    1、对外扩展是开放的
    任何一个程序,不可能在设计之初就想好了所有的功能,并且未来不做任何更新和修改.所以我们允许代码扩展、添加新功能
    2、对内修改时封闭的
    一个程序在修改的时候,可能已经交付给他人使用了,如果这时候我们对其进行修改,很有可能影响其他已经在使用该程序的用户

    无参数装饰器

    # 性能测试
    import time
    def timer(func):
        def inner(*args,**kwargs):
            start = time.time()
            ret = func(*args,**kwargs)  # func() 的返回值赋值给 ret
            time.sleep(1)
            print('所用时间:',time.time() - start - 1)
            return ret  # inner函数的返回值 返回给调用者 func1
        return inner
    
    @timer # 没有参数 timer = timer(func1)
    def func1(*args,**kwargs):
        print('打印参数:',args)
        print('打印参数:',*args)
        return 'func1 的返回值' # 因为是 func() 调用的所以返回给 func()
    
    print(func1(666,555,444))   # 得到 ret 打印得到 :func1 的返回值
    

    补充:函数的有效信息

    def login(username, password):
        """
        函数的注释信息
        :param (参数) username: 用户名
        :param (参数)password: 密码
        :return: (返回值) True 登录成功  False  登录失败
        """
        print(login.__name__)   # 打印这个函数的函数名
        print(login.__doc__)    # 打印这个函数的注释信息
        return username + password
    
    
    print login('你好', '中国')
    
    #-------------打印结果----------------
    login
    
        函数的注释信息
        :param (参数) username: 用户名
        :param (参数)password: 密码
        :return: (返回值) True 登录成功  False  登录失败
        
    你好中国
    

    装饰器修复技术

    函数的有效信息通常记录着一些有用信息,但是一旦和装饰器联系在一块的话,就会出现打印为空的现象

    def wrapper(func):
        def inner(*args, **kwargs):
            print("在前面执行的代码。。。。")
            func()
            print("在后面执行的代码...")
        return inner
    
    
    @wrapper
    def f():
        """
        这是一个用来测试装饰器修复技术的函数
        """
        print("哈哈哈")
    
    
    if __name__ == '__main__':
        print("执行的函数名:", f.__name__)
        print("函数的注释:", f.__doc__)
    
    # 打印结果
       执行的函数名: inner
       函数的注释: None
    

    这是因为我们所执行的f() 函数实际上执行的是装饰器中的 inner() 函数,所以打印为空

    添加装饰器修复技术@wraps
    from functools import wraps # 导入 weaps模块
    def wrapper(func):
        @wraps(func)  # 装饰 inner函数
        def inner(*args, **kwargs):
            print("在前面执行的代码。。。。")
            func()
            print("在后面执行的代码...")
        return inner
    
    @wrapper
    def f():
        """
        这是一个用来测试装饰器修复技术的函数
        """
        print("哈哈哈")
    
    if __name__ == '__main__':
        print("执行的函数名:", f.__name__)
        print("函数的注释:", f.__doc__)
    
    # 打印结果
        执行的函数名: f
        函数的注释: 这是一个用来测试装饰器修复技术的函数
    

    但在django框架中使用的装饰器修复技术并不是这个

    # CBV装饰器:
        from django.utils.decorateors import method_decorator(是给CBV视图的)
    
    # csrf相关的装饰器:
    
      from django.views.decoraters.csrf import csrf_exempt,csrf_protect
    
        csrf_exempt  # 取消伪造保护
    
        csrf_protect  # 当全局没有保护校验时,可以通过其获取保护
    
       示例:@method_decorator(csrf_exempt)
       注意:不能直接加在  get 或 post 方法上,可以加在 dispatch上或类上(指定装饰的方法)
       @method_decorator(csrf_exempt, name='get')
    

    带参数装饰器

    def outer(flag):
        def timer(func):
            def inner(*args,**kwargs):
                if flag:
                    print('''执行函数之前要做的''')
                re = func(*args,**kwargs)
                if flag:
                    print('''执行函数之后要做的''')
                return re
            return inner
        return timer
    
    @outer(True)  # 这是两步 1. timer = outer(True) 将参数传进去
                    # 2.@timer == timer=timer(func)
    def func():
        print(111)
    
    func()
    

    多个装饰器装饰一个

    在函数中如果有装饰器,计算机会将有语法糖的代码和下面被装饰的代码视为一条,所以他才会执行完语法糖后找到被装饰的函数,如果有多个装饰器,则优先执行离函数近的语法糖,最后依次向上执行其他的语法糖

    def xxx1(f):    # f = inner2
        def inner1():
            print('''装饰前1''')
            rte = f()
            print('''装饰后1''')
        return inner1
    def xxx2(f):  # f = func 函数
        def inner2():
            print('''装饰前2''')
            rte = f()
            print('''装饰后2''')
        return inner2
    @xxx1 # func = xxx1(func) * 里面的func = inner2, 外面的最新变量func = inner1
    @xxx2 # 优先执行 func = xxx2 (func) * 里面的 func 是下面的函数,外面的func = inner2
    def func():
        print('函数')
    func()    由此得 func = inner1
    结果:
      装饰前1
      装饰前2
      函数
      装饰后2
      装饰后1
    

    在哪里用过装饰器:用户登录
    在哪里用过带参数的装饰器:flask的路由系统


    迭代 重复做一些事很多次(就现在循环中做的那样)

    可迭代对象 Iterable 内部实现 __iter__方法 (可以直接作用于for循环的对象统称为可迭代对象,可以使用isinstance() 来判断 Iterable)

    迭代器 Iterator__next__ 方法的就是迭代器,迭代器是从第一个值开始一个一个的取,直到取完才结束,中间只能前进不能后退,也不能生成值可以通过 isinstance()判断对象是否是 Iterator

    生成器 具有 yeild方法,生成器是基于迭代器的,不仅能取值,还能生成值.被执行后返回的是一个生成器 生成器的本质就是一个迭代器

    生成器

    通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量可定是有限的.而且,我们创建一个包含100万个元素的列表,不仅占用很大的储存空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了.
    在Python中像列表生成器这样 一边循环一边计算的机制称为生成器generator

    要创建一个生成器generator 有很多中方法

    • 列表推导式来实现 将 [] 改成 ()
    L = [ x for x in range(2) ]
    G = ( x for x in range(2))
    
    print(L)
    print(G.__next__())
    print(G.__next__())
    结果:
    [0, 1]
    0
    1
    如果再加一个 print G.__next__() 就会报错,因为这里面只有两个,找不到第三个
    报错信息:
    Traceback (most recent call last):
      File "D:/xxx/生成器.py", line 368, in <module>
        print(G.__next__())
    StopIteration
    

    注意:只有调用 __next__一次,就会生成一个值,直到生成最后一个元素,其他时候就是一段普通的代码
    不断的调用 __next__ 很不方便,正确的方法是 for 循环,因为生成器也是可迭代对象,而且通过 for 不用关心 StopIteration 的错误

    手写 for 循环
    G = ( x for x in range(3) )
    while 1:
        try:
            print(G.__next__())
        except StopIteration as f:
            print('没了',f.value)
            break       # 跳出循环
    
    结果
    0
    1
    2
    没了 None
    
    • 函数来实现,在定义中包含 yield 关键字

    generator 非常强大.如果推算的算法比较复杂,用类似列表生成式的for循环无法实现就可以用函数来实现.
    比如,著名的斐波拉契数列(Finonacci) 除去第一个和第二个以外,任意一个数都可有前两个数相加得到
    1,1,2,3,5,8,13,21,34........

    用函数实现 斐波拉契数列
    def fib(max):
        n,a,b = 0,0,1   # 循环次数,ab 前两位数
        li = []
        while n < max:
            a,b = b,a + b
            n = n + 1
            li.append(a)
        return li
    

    通过观察,fib定义了斐波拉契数列的推算规则,可以从第一个元素开始推算出后续任意的元素,他和 generator 的距离 仅有一个yield

    def fib(max):
        n,a,b = 0,0,1   # 循环次数,ab 前两位数
        while n < max:
            a,b = b,a + b
            n = n + 1
            yield b    # 不在需要列表来保存,调用一次,生成一个
            yield  '不返回 return'
            return '返回 return'
    
    x = fib(6)
    print('return',x)
    print( x.__next__() )
    print( x.__next__() )
    结果:
    return <generator object fib at 0x0000019C749541A8>
    1
    不返回 return
    

    由此可知 函数时顺序执行,遇到 return或最后一行就返回,而生成器的函数,是每次调用 __next__ 的执行,遇到 yield 返回,再次执行,从上一次返回的yield继续执行

    迭代器

    Python中迭代器的本质上每次调用next()方法都返回下一个元素或抛出StopIteration的容器对象
    由于Python中没有“迭代器”这个类,因此具有以下两个特性的类都可以称为“迭代器”类
      1.有__next__()方法,返回容器的下一个元素或抛出StopIteration异常
      2.有__iter__()方法,返回迭代器本身

    迭代器和可迭代对象:都可以使用for循环,当然,迭代器内部多了一个 __next__ 方法
    判断迭代器和可迭代的方法
    1. 判断内部是否含有 __next__ 方法
    2. lterable 判断是不是可迭代对象
    3. Iterator 判断是不是迭代器

    判断是否为可迭代对象
    from collections import Iterable
    print( isinstance([],Iterable ))
    print( isinstance({},Iterable ))
    print( isinstance('abc',Iterable ))
    print( isinstance((1,2,3),Iterable ))
    print( isinstance({1,2,3},Iterable ))
    print( isinstance(100,Iterable ))
    print( isinstance( range(10),Iterable )) # range
    True
    True
    True
    True
    True
    False
    True
    
    # 判断是否为迭代器
    from collections import Iterator
    print( isinstance([],Iterator ))
    print( isinstance({},Iterator ))
    print( isinstance('abc',Iterator ))
    print( isinstance((1,2,3),Iterator ))
    print( isinstance({1,2,3},Iterator ))
    print( isinstance(100,Iterator ))
    print( isinstance( range(10),Iterator )) # range
    print( isinstance(( x for x in range(10)),Iterator ))  # 生成器
    print( isinstance(map(lambda x:x,[1,2,3]),Iterator )) # map
    False
    False
    False
    False
    False
    False
    False
    True
    True
    

    range函数 是一个可迭代的,但不是迭代器
    map函数 自带迭代器

    虽然列表、字符串.....这些不是迭代器,但是我们可以把它变成迭代器

    为什么list、dict、str等数据类型不是Iterator?
    这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
    Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

    使用 Iter() 可以将迭代对象转化为迭代器

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    

    迭代器小结

    • 凡是可作用于for循环的对象都是Iterable(可迭代对象)类型;
    • 凡是可作用于__next__(next()函数)的对象都是Iterator(迭代器)类型,它们表示一个惰性计算的序列;
    • 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
    • Python的for循环本质上就是通过不断调用next()函数实现的
    for x in [1, 2, 3, 4, 5]:
        pass
    实际上完全等价于:
    
    # 首先获得Iterator对象:
    it = iter([1, 2, 3, 4, 5])
    # 循环:
    while True:
        try:
            # 获得下一个值:
            x = next(it)
        except StopIteration:
            # 遇到StopIteration就退出循环
            break
    

    相关文章

      网友评论

          本文标题:三大器(装饰器,迭代器,生成器)

          本文链接:https://www.haomeiwen.com/subject/amqepqtx.html