• Python 装饰器


    装饰器示例

    def w1(func):
        def inner():
            print('...验证权限...')
            func()
        return inner
    
    @w1
    def f1():
        print('f1 called')
    
    f1()

    输出结果

    1 ...验证权限...
    2 f1 called

    装饰器原理

      首先,函数 w1(func) 是一个装饰器,该函数有个参数 func,用来接收一个方法;w1 内部又定义了一个函数 inner() ,

    执行完 print 之后,调用传进来的函数,然后返回值为内部函数 inner(),这就是 “闭包” 函数。

      然后,在 f1 上面的@w1 ,相当于 f1 = w1( f1 ),对其做了简化;此时的 f1 指向了 w1.inner 函数的地址。

    两个装饰器执行流程

    def makeBold(fun):
        print('----a----')
        def inner():
            print('----1----')
            return '<b>' + fun() + '</b>'
        return inner
    
    def makeItalic(fun):
        print('----b----')
        def inner():
            print('----2----')
            return '<i>' + fun() + '</i>'
        return inner
    
    @makeBold
    @makeItalic
    def test():
        print('----c----')
        print('----3----')
        return 'hello python decorator'
    
    ret = test()
    print(ret)

    执行结果

    1 ----b----
    2 ----a----
    3 ----1----
    4 ----2----
    5 ----c----
    6 ----3----
    7 <b><i>hello python decorator</i></b>

      首先 test() 先被第二个装饰器(makeItalic)装饰,接着被第一个装饰器(makeBold);而调用过程中,先执行第一个装饰器(makeBold),

    然后再执行第二个装饰器(makeItalic)。

    接着我们来仔细分析一下:

      1、装饰器只对函数进行装饰,不对装饰器装饰。所以在执行@makeBold的时候,遇到另一个装饰器暂停执行;接下来执行@makeItalic,把 test 函数传入

    装饰器,从而打印 'b' ,在makeItalic装饰完后,此时 test 指向 makeItalic 的 inner 函数地址;然后 返回来执行 @makeBold ,接着把 新的 test 传入 makeBold

    装饰器,因此打印了 'a' 。    

       2、第一次装饰 test = makeItalic( test ),第二次 装饰 test = makeBold( makeItalic( test ) )。经过分析,此时应该先执行 makeBold. inner 函数,因此先打

    印 '1',接下来再调用 makeBold. inner 函数里的 fun,其实就是makeItalic. inner 函数,所以打印 '2',在makeItalic. inner 中调用 fun ,其实就是最原始的 test(),

    所以打印 test 函数的 'c','3',最后一层层返回,打印的结果就是 <b><i>hello python decorator</i></b>。

    对有参函数进行装饰

     指定参数

    def w_say(fun):
        """
        如果原函数有参数,那闭包函数必须保持参数个数一致,并且将参数传递给原方法
        """
        def inner(name):
            """
            如果被装饰的函数有行参,那么闭包函数必须有参数
            """
            print('say inner called')
            fun(name)
        return inner
    
    @w_say
    def hello(name):
        print('hello ' + name)
    
    hello('wangcai')

    执行结果

    1 say inner called
    2 hello wangcai

    上述代码只能装饰指定参数的函数,下面代码就介绍了如何处理不定长参数

    def w_add(func):
        def inner(*args, **kwargs):
            print('add inner called')
            func(*args, **kwargs)
        return inner
    
    @w_add
    def add(a, b):
        print('%d + %d = %d' % (a, b, a + b))
    
    @w_add
    def add2(a, b, c):
        print('%d + %d + %d = %d' % (a, b, c, a + b + c))
    
    add(2, 4)
    add2(2, 4, 6)

    执行结果

    1 add inner called
    2 2 + 4 = 6
    3 add inner called
    4 2 + 4 + 6 = 12

    利用python的可变参数轻松实现装饰带参数的函数。

     对带返回值的函数进行装饰

     下面对有返回值的函数进行装饰,按照之前的写法,代码是这样的

    def w_test(func):
        def inner():
            print('w_test inner called start')
            func()
            print('w_test inner called end')
        return inner
    
    @w_test
    def test():
        print('this is test fun')
        return 'hello'
    
    ret = test()
    print('ret value is %s' % ret)

    执行结果

    1 w_test inner called start
    2 this is test fun
    3 w_test inner called end
    4 ret value is None

    从执行结果来看,没有输出 test 函数 'hello',而是 'None';这是因为在 inner 函数中对 test 进行了调用,但没有接收返回值,也没进行返回,所以就是 None。

    那么我们修改一下代码

    def w_test(func):
        def inner():
            print('w_test inner called start')
            str = func()
            print('w_test inner called end')
            return str
        return inner
    
    @w_test
    def test():
        print('this is test fun')
        return 'hello'
    
    ret = test()
    print('ret value is %s' % ret)

    执行结果

    1 w_test inner called start
    2 this is test fun
    3 w_test inner called end
    4 ret value is hello

    带参数的装饰器

    def func_args(pre='xiaoqiang'):
        def w_test_log(func):
            def inner():
                print('...记录日志...visitor is %s' % pre)
                func()
            return inner
        return w_test_log
    
    # 带有参数的装饰器能够起到在运行时,有不同的功能
    
    # 先执行func_args('wangcai'),返回w_test_log函数的引用
    # @w_test_log
    # 使用@w_test_log对test_log进行装饰
    @func_args('wangcai')
    def test_log():
        print('this is test log')
    
    test_log()

    执行结果

    1 ...记录日志...visitor is wangcai
    2 this is test log

    通过上述代码知道,带参数的装饰器就是在原闭包的基础上又加了一层了闭包。

    和两层嵌套相比,三层嵌套执行效果是这样的

    test_log = func_args('wangcai')(test_log)

    通用装饰器

    万能装饰器

    def w_test(func):
        def inner(*args, **kwargs):
            ret = func(*args, **kwargs)
            return ret
        return inner
    
    @w_test
    def test():
        print('test called')
    
    @w_test
    def test1():
        print('test1 called')
        return 'python'
    
    @w_test
    def test2(a):
        print('test2 called and value is %d ' % a)
    
    test()
    test1()
    test2(9)

    执行结果

    1 test called
    2 test1 called
    3 test2 called and value is 9 

    类装饰器

     

     

                           

  • 相关阅读:
    kafka 简单调试
    audit调优
    html使用的特殊符号&lt; &gt: &amp;等 意义对照
    nohup /dev/null 2>&1 含义详解
    K8S etcd参数优化
    Linux下安装VMware Tools 的方法
    PHP操作MySQL数据库5个步骤
    Win7下mysql root账户登录提示:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)解决方案
    VS 报cmath(19): error C2061: 语法错误: 标识符“acosf” 错误
    win7 装了VB虚拟机 开始挺好用 后来突然就打不开了 提示如下错误:(如图)创建 COM 对象失败.
  • 原文地址:https://www.cnblogs.com/Rain2017/p/9944380.html
Copyright © 2020-2023  润新知