zoukankan      html  css  js  c++  java
  • 装饰器(语法糖)

    一、装饰器

      执行outer函数,将index作为参数传递,

      将outer函数的返回值,重新赋值给index

      装饰器可以在函数执行前和执行后执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator),装饰器的功能非常强大,但是理解起来有些困难,因此我尽量用最简单的例子一步步的说明这个原理。

      写代码要遵循开发封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

    • 封闭:已实现的功能代码块
    • 开放:对扩展开发

    1、不带参数的装饰器:

      假设我定义了一个函数f,想要在不改变原来函数定义的情况下,在函数运行前打印出start,函数运行后打印出end,要实现这样一个功能该怎么实现?看下面如何用一个简单的装饰器来实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 使用@语法放在函数的定义上面 相当于执行 f=outer(f),此时f赋值成为了一个新的outer函数,
    # 此时f函数就指向了outer函数的返回值inner,inner是一个函数名,定义在oute函数里面
    # 原来的f是函数名可简单理解为一个变量,作为outer函数的参数传递进去了 此时参数func相当于f
    def outer(func):                    # 定义一个outer函数作为装饰器
        def inner():            # 如果执行inner()函数的话步骤如下:
            print('start')              # 1、首先打印了字符‘start’,
            r=func()                    # 2、执行func函数,func函数相当于def f(): print('中') 
            print('end')                # 3、接着函数打印‘end’
            return r                    # 4、将func函数的结果返回
        return inner
       
    @outer
    def f():              # f=outer(f)=innner
        print('中')
       
    f()                   # f()相当于inner(),执行inner函数的步骤看上面定义处的注释<BR>#打印结果顺序为   start 中 end

    2、包含任意参数的装饰器:

      在实际中,我们的装饰器可能应用到不同的函数中去,这些函数的参数都不一样,那么我们怎么实现一个对任意参数都能实现功能的装饰器?还记得我写函数那篇博客中,就写一种可以接受任意参数的函数,下面来看看如何将其应用到装饰器中去。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #其实只要将上面一种不带参数的装饰器修改一下就可以了
    #修改也很简单,只需将inner和func的参数改为 (*args,**kwargs)
    #其他实现的过程和上面一种一样,就不再介绍了
    def outer(func):
        def inner(*args,**kwargs):
            print('start')
            r=func(*args,**kwargs)    # 这里func(*args,**kwargs)相当于f(a,b)
            print('end')
            return r
        return inner
       
    @outer
    def index(a,b):
        print(a+b)
    m=index(1,4)                    # f(1,4)相当于inner(1,4) 这里打印的结果为 start 5 end <br>print m
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    1、outer函数加载
    2、遇到outer装饰器
    3、执行outer函数,将index函数(被装饰的函数)作为参数传递func=index()
    4、加载inner函数,
    5、将outer函数的返回值inner(这是一个函数),重新赋值给index(也就是说此时的index指向inner函数)
    6、执行inner函数
    7、打印‘start’
    8、执行res = func(a1, a2)(也就是执行被装饰器装饰的函数(index函数))
    9、将index函数的返回值赋值给res
    10、打印‘end’
    11、将inner函数的返回值 res 赋值给index函数
    12、打印m

      

    3、使用两个装饰器:

      当一个装饰器不够用的话,我们就可以用两个装饰器,当然理解起来也就更复杂了,当使用两个装饰器的话,首先将函数与内层装饰器结合然后在与外层装饰器相结合,要理解使用@语法的时候到底执行了什么,是理解装饰器的关键。这里还是用最简单的例子来进行说明。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    def outer2(func2):
        def inner2(*args,**kwargs):
            print('开始')
            r=func2(*args,**kwargs)
            print('结束')
            return r
        return inner2
       
    def outer1(func1):
        def inner1(*args,**kwargs):
            print('start')
            r=func1(*args,**kwargs)
            print('end')
            return r
        return inner1
       
    @outer2                                # 这里相当于执行了 f=outer1(f)  f=outer2(f),步骤如下
    @outer1                                #1、f=outer1(f) f被重新赋值为outer1(1)的返回值inner1,
    def f():                               #    此时func1为 f():print('f 函数')
        print('f 函数')                     #2、f=outer2(f) 类似f=outer2(inner1) f被重新赋值为outer2的返回值inner2
                                           #    此时func2 为inner1函数 inner1里面func1函数为原来的 f():print('f 函数')
                                                                                
    f()                                    # 相当于执行 outer2(inner1)() 
    >>开始                                  # 在outer函数里面执行,首先打印 ‘开始 ’
    >>start                                # 执行func2 即执行inner1函数 打印 ‘start’
    >>f 函数                               # 在inner1函数里面执行 func1 即f()函数,打印 ‘f 函数’ 
    >>end                                  # f函数执行完,接着执行inner1函数里面的 print('end')
    >>结束                                 # 最后执行inner2函数里面的 print('结束')

    4、带参数的装饰器:

      前面的装饰器本身没有带参数,如果要写一个带参数的装饰器怎么办,那么我们就需要写一个三层的装饰器,而且前面写的装饰器都不太规范,下面来写一个比较规范带参数的装饰器,下面来看一下代码,大家可以将下面的代码自我运行一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import functools
       
    def log(k=''):                                        #这里参数定义的是一个默认参数,如果没有传入参数,默认为空,可以换成其他类型的参数
        def decorator(func):
            @functools.wraps(func)                        #这一句的功能是使被装饰器装饰的函数的函数名不被改变,
            def wrapper(*args, **kwargs):
                print('start')
                print('{}:{}'.format(k, func.__name__))    #这里使用了装饰器的参数k
                = func(*args, **kwargs)
                print('end')
                return r
            return wrapper
        return decorator
       
    @log()                        # fun1=log()(fun1) 装饰器没有使用参数
    def fun1(a):
        print(a + 10)
       
    fun1(10)
    # print(fun1.__name__)        # 上面装饰器如果没有@functools.wraps(func)一句的话,这里打印出的函数名为wrapper
       
    @log('excute')                # fun2=log('excute')(fun2) 装饰器使用给定参数
    def fun2(a):
        print(a + 20)
    fun2(10)

     双层装饰器:

     一个函数可以被多个装饰器装饰吗? 比如两个装饰器 

     会先将 函数交个最下层@装饰器将处理结果在交给其上一层@装饰器  即先交给w2 再交给w1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    def w1(func):
        def inner(*args,**kwargs):
            # 验证1
            # 验证2
            # 验证3
            return func(*args,**kwargs)
        return inner
      
    def w2(func):
        def inner(*args,**kwargs):
            # 验证1
            # 验证2
            # 验证3
            return func(*args,**kwargs)
        return inner
      
      
    @w1
    @w2
    def f1(arg1,arg2,arg3):
        print 'f1'

    双层装饰器原理

     

  • 相关阅读:
    UVA1349 Optimal Bus Route Design 最优巴士路线设计
    POJ3565 Ants 蚂蚁(NEERC 2008)
    UVA1663 Purifying Machine 净化器
    UVa11996 Jewel Magic 魔法珠宝
    NEERC2003 Jurassic Remains 侏罗纪
    UVA11895 Honorary Tickets
    gdb调试coredump(使用篇)
    使用 MegaCLI 检测磁盘状态并更换磁盘
    员工直接坦诚直来直去 真性情
    山东浪潮超越3B4000申泰RM5120-L
  • 原文地址:https://www.cnblogs.com/pyxuexi/p/13871051.html
Copyright © 2011-2022 走看看