• python开发之装饰器&迭代器&生成器


    闭包的引入

    闭包:首先必须是嵌套函数,再次内部函数调用外部函数的变量,如下代码

    def outer():
        a = 1
        def inner():
            print("内部函数>",a)
        print(inner.__closure__) # 用来判断闭包的,(<cell at 0x004573D0: int object at 0x53416880>,)
    outer()
    

    外部函数的返回值是内部函数的函数名

    def outer():
        a = 1
        def inner():
            print("内部函数>",a)
        return inner
    res = outer()
    res()
    

    闭包的小小应用:

    from urllib.request import urlopen
    def get_url():
        url = 'http://www.xiaohua100.cn/index.html'
        def inner():
            url_res = urlopen(url).read()
            print(url_res)
        return inner
    
    get_url()
    

    总结:闭包好处,多次调用函数,不必多次生成外部函数的变量。

    装饰器

    装饰器的引出,计算程序的执行时间

    import time
    
    def func():
        time.sleep(2)
    
    def timer(f):
        start = time.time()
        f()
        end = time.time()
        print('函数运行的时间是:',end-start)
    
    timer(func)
    
    虽然上面的代码可以将实现将计算时间的函数分离,但是改变了原函数额调用方式
    def func():
        time.sleep(2)
    
    def timer(f): # 装饰器函数
        def inner():
            start = time.time()
            f() # 被装饰函数
            end = time.time()
            print('函数运行的时间是:',end-start)
        return inner
    func = timer(func)
    func()

    装饰器的作用:不想修改原来函数的调用方式,但是还想再原来的函数前后添加功能。

    最终版没有改变函数的调用方式

    def timer(f): # 装饰器函数
        def inner():
            start = time.time()
            f() # 被装饰函数
            end = time.time()
            print('函数运行的时间是:',end-start)
        return inner
    @timer # 语法糖,@装饰器函数名
    def func():
        time.sleep(2)
    
    func()
    

    带返回值的装饰器

    def timer(f): # 装饰器函数
        def inner():
            start = time.time()
            res = f() # 被装饰函数
            end = time.time()
            print('函数运行的时间是:',end-start)
            return res
        return inner
    @timer # 语法糖,@装饰器函数名
    def func():
        time.sleep(2)
        return 'ok'
    res = func()
    print(res)
    

    装饰带参数的函数装饰器

    def timer(f): # 装饰器函数
        def inner(*args,**kwargs):
            start = time.time()
            res = f(*args,**kwargs) # 被装饰函数
            end = time.time()
            print('函数运行的时间是:',end-start)
            return res
        return inner
    @timer # 语法糖,@装饰器函数名
    def func(a):
        time.sleep(2)
        print('参数:',a)
        return 'ok'
    res = func(1)
    print(res)
    

    装饰器的最终形式

    def wrapper(func):
        def inner(*args,**kwargs):
            ret = func(*args,**kwargs)
            return ret
        return inner
    @wrapper
    def qqxing(): # qqing=wrapper(qqxing)
        print(123)
    
    qqxing()
    print(qqxing.__name__) # inner
    

     例子:通过使用装饰器来将要执行的函数的函数名写入文件中

    import datetime
    def log(func):
        def inner(*args,**kwargs):
            with open('log.txt','a',encoding='utf8') as f:
                date_str = str(datetime.datetime.now())
                f.write(date_str+func.__name__+'
    ')
            ret = func(*args,**kwargs)
            return ret
        return inner
    @log
    def qqxing():
        print('qqxing')
    qqxing()
    

    通过别人写的装饰器模块,来得到被装饰函数的函数名

    import datetime
    from functools import wraps
    def log(func):
        @wraps(func)
        def inner(*args,**kwargs):
            with open('log.txt','a',encoding='utf8') as f:
                date_str = str(datetime.datetime.now())
                f.write(date_str+func.__name__+'
    ')
            ret = func(*args,**kwargs)
            return ret
        return inner
    @log
    def qqxing():
        print('qqxing')
    qqxing()
    print(qqxing.__name__) # 如果没有加@wraps(func),qqxing__name__是inner,@wraps也成为装饰器修复技术
    

     装饰器进阶

     1、三层装饰器

    import time
    
    Flag = True
    def timer_out(flag):
         def timer(func):
             def inner(*args,**kwargs):
                 if flag:
                    start = time.time()
                    ret = func(*args,**kwargs)
                    end = time.time()
                    print(end-start)
                    return ret
                 else:
                    ret = func(*args, **kwargs)
                    return ret
    
             return inner
         return timer
    # timer = timer_out(Flag)
    @timer_out(Flag) # <==>@timer
    def wahaha():
        time.sleep(2)
        print('wahahah')
    
    wahaha()
    

    2、@wrapper2

          @wrapper1

    如上所示的装饰器

    def warpper1(func):
        def inner1(*args,**kwargs):
            print("wrapper1,before func")
            func()
            print("wrapper1,after func")
        return inner1
    
    def warpper2(func):
        def inner2(*args,**kwargs):
            print("wrapper2,before func") 
            func()
            print("wrapper2,after func")
        return inner2
    
    @warpper2 # foo=wrapper(foo)-->wrapper(inner1)==inner2
    @warpper1 # 先执行这个装饰器 foo=warpper1(foo)==inner1
    def foo():
        print('foo')
    foo()
    #结果
    # wrapper2,before func
    # wrapper1,before func
    # foo
    # wrapper1,after func
    # wrapper2,after func
    

     3、面试题:免登录版装饰器

    Flag = False
    
    def login(func):
        def inner(*args,**kwargs):
            global Flag
            if Flag:
                ret = func()
                return ret
            else:
                username = input('username:')
                password = input('password:')
                if username=='jack' and password=='123':
                    Flag=True
                    ret = func(*args,**kwargs)
                    return ret
                else:
                    print('登录失败')
        return inner
    

     4、装饰器版的登录校验

       这个例子其中的一些方法会在Django框架中学习到,这里只是为了总结装饰器而已。

    def check_login(func):
        def inner(requset,*args,**kwargs):
            ret = requset.get_signed_cookie("is_login",default="0",salt="salt") # 加盐的cookie
            if ret == '1':
                # 已经登录过的继续执行
                return func(requset,*args,**kwargs)
            else:
                next_url = requset.path_info # 获取当前访问的URL
                return redirect("login/?next={}".format(next_url))
            return inner
    

    生成器 

    如下两种形式都是生成器

    1、生成器函数:只要含有yield关键字的函数都是生成器,类似return,只能写在函数内部,不能和return共用。而且yield语句一次返回一个结果,在每个结果中间挂起原来的状态,以便下次从它开始离开的地方继续执行。

    首先看普通函数的返回值

    def generator():
        print('============')
        return '返回值函数'
    
    g = generator()
    print(g)
    

    再看含有yield 的生成器函数

    def generator():
        print('>>>>>>>>>>')
        yield '生成器函数'
    
    g = generator()
    print(g.__next__())
    print(next(g))
    # 生成器函数
    # Traceback (most recent call last):
    #   File "D:/pythonstudy/装饰器迭代器生成器/生成器.py", line 16, in <module>
    #     print(next(g))
    # StopIteration  如果取不到了值会报错为StopIteration
    
    def generator():
        print(1)
        yield 'a'
        print(2)
        yield 'b'
    
    g = generator()
    print(g.__next__())
    print(next(g))
    

     生成器进阶

    def generator():
        print(123)
        content = yield 1
        print('content:', content)
        print(456)
        yield 2
    
    
    g = generator()
    print(g.__next__())
    print(g.send('hello')) # send()和next()的效果一样
    

     send获取下一个值的效果和next基本一样,只是在获取下一个值的时候,给上一个值的位置传递一个数据。

        注意:第一次使用生成器的时候必须用next获取第一个值。

                  最后一个yield不能接受外部的值。

    生成器面试题

    def demo():
        for i in range(4):
            yield i
    
    g = demo()
    g1 = (i for i in g)
    g2 = (i for i in g1)
    
    print(list(g1)) #[0, 1, 2, 3]
    print(list(g2)) #[]
    

     注释:g1从g中取值直到取完,g2从g1中取到g1末,但是g2开始取值的时候,g1已经为空,所以该结果为空。

    2、生成器表达式:本质迭代器【自带__iter__方法和__next__方法】

         列表解析式 egg_list = ['鸡蛋%s'%i for i in range(10)]

         生成器表达式 laomuji=('鸡蛋%s'%i for i in range(10))

     迭代器

    1、什么叫迭代

    字符串、列表、元祖、字典、集合都可以被for循环,也可以通过Iterable来进行证明,这些都是可迭代的。

    from collections import Iterable
    l = [11,22,33]
    t = (11,22,33)
    d = {"name":"jack"}
    s = {11,22,33}
    
    print(isinstance(l,Iterable)) # True
    print(isinstance(t,Iterable)) #True
    print(isinstance(d,Iterable)) #True
    print(isinstance(s,Iterable)) #True
    

    2、可迭代协议

    可迭代协议定义非常简单,就是内部实现了__iter__方法。

    迭代器遵循迭代器协议:必须使用__iter__方法和__next__方法

    3、类中的__iter__和__next__方法

    class Foo:
        def __init__(self,n):
            self.n = n
        def __iter__(self): # 成为一个可迭代对象。
            return self
        def __next__(self):
            self.n += 1
            return self.n
    
    f = Foo(10)
    f.__next__()
    f.__next__()
    for i in f:
        if i>20:
            break
        print(i)
    

    小练习:迭代器实现斐波那契数列

    class Fib:
        def __init__(self,a,b):
            self._a = a
            self._b = b
        def __iter__(self):
            return self
        def __next__(self):
            self._a,self._b = self._b,self._a + self._b
            return self._a
    f = Fib(1,1)
    print(f.__next__()) # 1
    print(f.__next__()) # 2
    print(f.__next__()) # 3 
    print(f.__next__()) # 5
    print(f.__next__()) # 8
    print(f.__next__()) # 13
    

    迭代器的好处:

    1、从容器中一个一个的取值,会把所有的值都取到

    2、节省内存空间,迭代器并不会在内存中再占用一大块内存,而是随着循环每次生成一个,每次next每次回给出一个值。

  • 相关阅读:
    maven常用插件
    Java反编译工具:Java Decompiler
    好看的电影
    我说
    Fisher准则一维聚类
    Java获取函数参数名称
    推荐几个字体生成网站
    Python中的两种路径
    轻量级数据库简介
    Solr Wiki文档
  • 原文地址:https://www.cnblogs.com/crazyforever/p/5081914.html
Copyright © 2020-2023  润新知