python 装饰器案例解析

发布时间:2018-02-26 20:38:49编辑:admin阅读(3403)

    本文介绍几个装饰器案例,来分析装饰器是如何调用的


    获取函数运行时间的例子

    写装饰器,不可以一步到位,要慢慢一点一点的来


    先写好2个函数

    import time
    def test1():
        time.sleep(1)
        print('in the test1')
    
    def test2():
        time.sleep(2)
        print('in the test2')
    
    test1()
    test2()

    执行输出

    in the test1

    in the test2


    在不修改源代码的情况下,新增一个功能呢?

    这个时候,需要用到 高阶函数

    import time
    
    def deco(func):
        start_time = time.time()
        func()
        stop_time = time.time()
        print('the func run time is %s' % (stop_time - start_time))
    
    def test1():
        time.sleep(1)
        print('in the test1')
    
    def test2():
        time.sleep(2)
        print('in the test2')
    
    deco(test1)
    deco(test2)

    执行输出

    in the test1

    the func run time is 1.0009195804595947

    in the test2

    the func run time is 2.0000431537628174


    注意:

    执行的时候,不能写deco(test1()),为什么呢?这样写,是把test1函数执行的结果,传给deco了。

    deco函数,不需要执行结果,它需要一个函数即可。

    Pycharm编辑器写代码的时候,有自动补全功能,切记这里,要把括号删掉才行。


    上面实现了显示执行时间的功能,下面考虑一个问题,如何在此基础上,直接执行

    test1()和test2() 就可以实现上面的功能

    需要用到变量的引用

    def deco(func):
        start_time = time.time()
        func()
        stop_time = time.time()
        print('the func run time is %s' % (stop_time - start_time))
    
    def test1():
        time.sleep(1)
        print('in the test1')
    
    def test2():
        time.sleep(2)
        print('in the test2')
    
    test1 = deco(test1)
    test2 = deco(test2)
    test1()
    test2()

    执行报错

    TypeError: 'NoneType' object is not callable


    因为deco没有return,无法得到函数内存地址

    再改一下

    import time
    def deco(func):
        start_time = time.time()
        #返回函数内存地址
        return func
        stop_time = time.time()
        print('the func run time is %s' % (stop_time - start_time))
    
    def test1():
        time.sleep(1)
        print('in the test1')
    
    def test2():
        time.sleep(2)
        print('in the test2')
    
    test1 = deco(test1)
    test2 = deco(test2)
    test1()
    test2()

    执行输出

    in the test1

    in the test2


    从结果可以看出,函数的调用方式没有变,执行结果也没有变。搞了半天,啥都没干。

    因为deco中直接returun了,所以打印时间没有输出。


    上面的应用都是高阶函数,还缺嵌套函数,就成了

    下面写一个嵌套函数。

    def timer():
        def deco():
            pass

    能不能把嵌套函数的形式融入到deco函数中呢?


    把deco函数的代码直接拷贝进来,最后return deco

    一个函数只有一个return,把中间的return修改为func()

    将func参数移动到函数最上层

    def timer(func):
        def deco():
            start_time = time.time()
            func()
            stop_time = time.time()
            print('the func run time is %s' % (stop_time - start_time))
        return deco

    完整代码如下:

    import time
    
    def timer(func): #time(test1) func=test1
        def deco():
            start_time = time.time()
            func() #run test1()
            stop_time = time.time()
            print('the func run time is %s' % (stop_time - start_time))
        return deco
    
    def test1():
        time.sleep(1)
        print('in the test1')
    
    def test2():
        time.sleep(2)
        print('in the test2')
    
    print(timer(test1))

    执行输出

    function timer.

    结果返回deco函数的内存地址


    有了函数的内存地址,把它赋值给一个变量,执行变量就可以了

    print(timer(test1))改成

    test1 = timer(test1)
    test1()

    执行输出

    in the test1

    the func run time is 1.0003840923309326


    这里,效果就实现了,源代码和执行方式都没有改变。


    注意: 最后的test1()行函数,已经不是原来的函数了,而是被装饰过的函数。


    执行函数,需要2个步骤,太麻烦了,这不是最终效果

    python 提供一个语法,用来执行装饰器函数,语法

    @函数名
    被装饰的函数名

    这一句,需要加在被装饰函数的上一行


    我删除了test2(),最终完整代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    __author__ = 'www.py3study.com'
    
    import time
    
    def timer(func): #timer(test) func=test1
        def deco():
            start_time = time.time()
            func() #run test1()
            stop_time = time.time()
            print('the func run time is %s' % (stop_time - start_time))
        return deco
    
    @timer #test1=timer(test1)
    def test1():
        time.sleep(1)
        print('in the test1')
        
    test1()

    执行输出

    in the test1

    the func run time is 1.0006184577941895


    test1()函数上面的@timer

    就相当于

    test1 = timer(test1)


    注意:上面写的装饰器,还不够完美

    举个例子,再加一个函数,去装饰它

    #!/usr/bin/env python
    # coding: utf-8
    __author__ = 'www.py3study.com'
    
    import time
    
    def timer(func):
        def deco():
            start_time = time.time()
            func()
            stop_time = time.time()
            print('the func run time is %s' % (stop_time - start_time))
        return deco
    
    @timer
    def test1():
        time.sleep(1)
        print('in the test1')
    
    @timer
    def test2(name):
        print("test",name)
    
    test1()
    test2("zhang")

    执行报错

    TypeError: test2() missing 1 required positional argument: 'name'

    为什么呢?

    test2上面的@timer就相当于

    test2 = timer(test2)

    timer(test2) -> 调用deco() -> 调用func() ->调用原test2(name)

    注意,调用原test2函数的时候,需要传一个参数才行,而func()调用它的时候,没法传参数,所以程序报错。


    为了解决传参的问题,需要调整一下deco函数,要求能接受参数。由于被装饰的函数,千奇百怪,有参数,没参数的都存在。使用*args,**kwargs就可以表示任意的参数。


    最后终极代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    __author__ = 'www.py3study.com'
    
    import time
    
    def timer(func):
        def deco(*args,**kwargs):
            start_time = time.time()
            func(*args,**kwargs)
            stop_time = time.time()
            print('the func run time is %s' % (stop_time - start_time))
        return deco
    
    @timer
    def test1():
        time.sleep(1)
        print('in the test1')
    
    @timer
    def test2(name):
        time.sleep(2)
        print("test",name)
    
    test1()
    test2("zhang")

    执行输出

    in the test1

    the func run time is 1.000878095626831

    test zhang

    the func run time is 2.0003161430358887


    网页登陆

    代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    __author__ = 'www.py3study.com'
    
    import time
    
    user,passwd = 'zhang','abc123'
    def auth(func):
        def wrapper(*args,**kwargs):
            username = input("username:").strip()
            password = input("password:").strip()
    
            if user == username and passwd == password:
                print("\033[32;1mUser has passed authentication\033[0m")
                func(*args,**kwargs)
            else:
                exit("\033[31;1mInvalid username or password\033[0m")
        return  wrapper
    
    def index():
        print("welcome to index page")
    
    @auth
    def home():
        print("welcome to home page")
    @auth
    def bbs():
        print("welcome to bbs page")
    
    index()
    home()
    bbs()

    执行输出

    blob.png

    输出了2次用户名和密码

    为什么呢?因为home和bbs页面,需要登录才能访问。


    下面加一个验证方式

    #!/usr/bin/env python
    # coding: utf-8
    __author__ = 'www.py3study.com'
    
    import time
    
    user,passwd = 'zhang','abc123'
    def auth(auth_type):
        print("auth func:",auth_type)
        def outer_wrapper(func):
            def wrapper(*args, **kwargs):
                print("wrapper func args:",*args, **kwargs)
                username = input("username:").strip()
                password = input("password:").strip()
    
                if user == username and passwd == password:
                    print("\033[32;1mUser has passed authentication\033[0m")
                    res = func(*args, **kwargs)
                    print("---after authentication")
                    return res
                else:
                    exit("\033[31;1mInvalid username or password\033[0m")
    
            return wrapper
        return outer_wrapper
    
    
    def index():
        print("welcome to index page")
    
    @auth(auth_type="local")
    def home():
        print("welcome to home page")
    @auth(auth_type="ldap")
    def bbs():
        print("welcome to bbs page")
    
    index()
    home()
    bbs()

    执行输出

    blob.png

    现在还没有判断是哪种方式


    完整代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    __author__ = 'www.py3study.com'
    
    import time
    
    user,passwd = 'zhang','abc123'
    def auth(auth_type):
        #print("auth func:",auth_type)
        def outer_wrapper(func):
            def wrapper(*args, **kwargs):
                #print("wrapper func args:",*args, **kwargs)
                if auth_type == "local":
    
                    username = input("username:").strip()
                    password = input("password:").strip()
    
                    if user == username and passwd == password:
                        print("\033[32;1mUser has passed authentication\033[0m")
                        res = func(*args, **kwargs)
                        print("---after authentication")
                        return res
                    else:
                        exit("\033[31;1mInvalid username or password\033[0m")
                elif auth_type == "ldap":
                    print("This is LDAP,I can't do it")
                    pass
    
            return wrapper
        return outer_wrapper
    
    
    def index():
        print("welcome to index page")
    
    @auth(auth_type="local")
    def home():
        print("from home page")
    @auth(auth_type="ldap")
    def bbs():
        print("from bbs page")
    
    index()
    home()
    bbs()

    执行输出

    blob.png

    执行bbs,采用的是ldap方式,我就直接输出了一段话,判断逻辑不会写啊!


关键字

上一篇: python 装饰器

下一篇: python 生成器