装饰器

样例代码

import functools

装饰器

def log(func):

    def wrapper(*args, **kw):

        print(‘call %s():’ % func.name)

        return func(*args, **kw)

    return wrapper

装饰器

def log_text(text):

    def decorator(func):

        def wrapper(*args, **kw):

            print(‘%s %s():’ % (text, func.name))

            return func(*args, **kw)

        return wrapper

    return decorator

装饰器,如果使用上面两个装饰器,获取 name 属性的时候得到的是 wrapper,有些依赖函数签名的代码执行就会出错。

def log_new(func):

    @functools.wraps(func) # import functools 使用这个定义即可将参数传递下去。

    def wrapper(*args, **kw):

        print(‘call %s():’ % func.name)

        return func(*args, **kw)

    return wrapper

在这里使用装饰器,调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志,now = log(now)

@log

def now():

    print(“2019-12-09”)

可以收受参数的装饰器

@log_text(“Execute”)

def now_text():

    print(“2019-12-09”)

if name == ‘main’:

    # 函数也是一个对象,函数对象可以被赋值给变量,通过变量也能调用函数。

    # 函数对象有一个 name 属性,可以得到函数的名字。

    print(now.name)

    # 假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,

    # 这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

    # 本质上,decorator就是一个返回函数的高阶函数。

    now()

    now_text()

在面向对象(OOP)的设计模式中,decorator被称为装饰模式。

OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。

Python的decorator可以用函数实现,也可以用类实现。

decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。

Copy

待讨论内容

author = ‘Kaiming’

装饰器按照装饰函数是否带参数和被装饰函数是否带参数可以分为4类

import functools

P1——两者都不带参数

定义装饰函数,被装饰函数作为参数

def decorator_one(f1):

    print(‘start’)

    print(‘call ’ + f1.name)

    print(‘end’)

    # 被装饰函数不带参数,返回被装饰函数,最外层函数必须接受一个返回函数

    return f1

@decorator_one

采取和上面定义不同的名字,否则会出错

def f1():

    pass

f1()

P2——装饰函数不带参数,被装饰函数带参数

定义装饰函数,被装饰函数作为参数

def decorator_two(f2):

    # 定义包装函数,用于传递给被装饰函数当做参数,使用可变参数和关键字参数可以提升灵活性

    def wrapper_two(*args, **kw):

        print(‘start’)

        print(‘call’ + f2.name)

        f2(*args, **kw)

        print(‘end’)

    # 这里的return返回的是decorator_two这个函数的返回值

    return wrapper_two

@decorator_two

def f2(s):

    print(s)

'''流程是:f2=decorator_two(f2),然后运行wrapper_two函数,在f2执行前后进行

处理,最后还要把函数最为一个返回值,这个是装饰函数要求的'''

f2(‘被装饰函数f2的参数’)

P3——装饰函数带参数,被装饰函数不带参数

装饰函数外层加一层函数,用来传递装饰函数的参数

def my_decorator_three(*args, **kw):

    # 这里才是装饰函数开始的地方,传入装饰函数参数f3

    def decorator_three(f3):

        #    @functools.wraps(f3) 这句在这里可加可不加,反正不会出现函数名字指定错误

        print(‘start’)

        print(‘f3的装饰器函数自带参数为:’, *args)

        print(‘call ’ + f3.name)

        print(‘end’)

        # 这里需要返回被装饰函数本身f3,当做

        return f3

    return decorator_three

'''注意P3的调用逻辑是f3=my_decorator_three(*args,**kw)(f3)(),最后的(f3)在这里是

使用前面的返回函数decorator_three(f3),可以完成装饰函数自带参数实现的功能,最后的()实际上

再使用不带参数的f3()函数本身完成被装饰函数本身的功能'''

这里采用最外层的函数

@my_decorator_three(‘my_decorator_three’)

def f3():

    pass

f3()

P4——装饰器函数和被装饰函数都带参数

这层传递的参数是装饰函数本身需要使用的参数

def my_decorator_four(*args_decorator, **kw_decorator):

    # 这一层函数传递需要被装饰的函数f4

    def decorator_four(f4):

        @functools.wraps(f4)

        # 这一层包装函数用于传递被装饰函数f4所需要的参数

        def wrapper(*args, **kw):

            print(‘start’)

            print(f4.name + ’ is working’)

            # 在这个函数内已经使用了带参数的被装饰函数f4,所以最后也不需要返回函数f4了

            f4(*args, **kw)

            print(‘end’)

        return wrapper

    return decorator_four

'''P4的调用逻辑是f4=my_decorator_four(**args_decorator,**kw_decorator)(f4)(*args,**kw);这个逻辑比较长,首

先是他用最外层的函数,传递装饰函数需要的参数;然后用于最外层函数返回的函数是decorator_four(),所以

后面的(f4)实际上等价于decorator_four(f4),然后由于decorator_four函数的返回值是wrapper,所以(*args,**kw)

等价于wrapper(*args,**kw)'''

@my_decorator_four(‘my_decorator_four’)

def f4(s):

    print(s)

f4(‘被装饰函数f4的参数’)

P5 课后习题,通过分析课后习题可以知道,需要设计一个装饰函数带可变参数,被装饰函数不带参数的装饰器

为了保持前后的统一性,函数名我就不用log了,而是用my_decorator_five代替log,f5代替f

def my_decorator_five(*args, **kw):

    def decorator_five(f5):

        print(*args)

        print(‘begin call’)

        # 在函数中使用了被装饰函数,就不用再返回这个函数了,因为已经在这被执行过了

        f5()

        print(‘end call’)

    return decorator_five

@my_decorator_five()

def f5():

    pass

@my_decorator_five(‘execute’)

def f5():

    pass

'''P5的装饰器函数调用逻辑实际上和P3类似,f5=my_decorator_five(*args,**kw)(f5),好P3的差别就是最后不用加(),因

为最后没有返回被装饰函数'''