052 装饰器

一、什么是装饰器

装饰器: 装饰的工具(函数),这个函数有装饰的作用.可以理解为装饰器就是一个函数的加工厂

装饰器本质: 装饰器 就是一个函数A装饰的对象 也就是一个函数B
函数B中都是一些功能性的代码,比如函数B中是买东西的逻辑代码而函数A则是一些通用的功能修饰的代码,比如函数A中是登陆验证等等。
现在给一个需求,需要在购物之前加上身份验证,我们就可以使用函数A去装饰函数B,使得函数B具有(函数A+函数B)的全部功能。给函数加功能,但不改变函数的代码和调用方式

装饰器的实现必须遵循两大原则:

  1. 不改变函数B的调用方式
  2. 不改变函数B的源代码
def A():
    """装饰器"""
    pass

def B():
    """被装饰的函数"""
    pass

B()

需要注意的是

  • 装饰器本身其实是可以任意可调用的对象
  • 被装饰的对象也可以是任意可调用的对象

二、无参装饰器(双层装饰器)

2.1 双层装饰器:无参、无返回值的被装饰函数

例:检测index函数的运行的时间,但是不能改变index函数的调用方式,以及index函数的源码

# 写一个装饰器,装饰写好的功能函数,相当于加工厂
def outer(func):   # func = 真正的index
    def inner():
        start = time.time()
        func()
        end = time.time()
        print(end-start)
    return inner	# 返回inner函数对象

# 功能函数
import time
def index():
    print('hello index')
    time.sleep(1)

# 使用装饰器
index = outer(index)  # 函数对象做形参,得到闭包函数对象
index()		# 调用闭包函数

hello index
1.0521571636199951

2.2 双层装饰器:带参,有返回值的被装饰函数

例:检测index函数的运行的时间,但是不能改变index函数的调用方式,以及index函数的源码

# 双层装饰器:带参,有返回值的被装饰函数
import time

def outer(func):
    def inner(*args, **kwargs):		# 可变参数
        print('args:',args)  # (10,)
        print('kwargs:',kwargs)
        start = time.time()
        res = func(*args, **kwargs)  # 真正的index() 解压缩
        end = time.time()
        print("执行时间:",end - start)
        return res
    return inner	# 返回加工后的对象

def index(a=1):
    print('a', a)
    print('hello index')
    time.sleep(1)
    return "执行完了"

index = outer(index)	# 函数对象做参数,得到一个闭包函数对象
print(index())

args: ()
kwargs: {}
a 1
hello index
执行时间: 1.0005826950073242
执行完了

三、装饰器语法糖

装饰器语法糖:就是在被装饰函数正上方,并且是单独一行写上@装饰器名

对之前的代码稍作修改,加入语法糖

# 双层装饰器:带参,有返回值的被装饰函数
import time

def outer(func):
    def inner(*args, **kwargs):		# 可变参数
        print('args:',args)  # (10,)
        print('kwargs:',kwargs)
        start = time.time()
        res = func(*args, **kwargs)  # 真正的index() 解压缩
        end = time.time()
        print("执行时间:",end - start)
        return res
    return inner	# 返回加工后的对象

@outer # 语法糖(更精简的代码)   相当于 index = deco(index)
def index(a=1):
    print('a', a)
    print('hello index')
    time.sleep(1)
    return "执行完了"

# 使用了语法糖的被装饰函数可以直接调用
print(index(3))

args: (3,)
kwargs: {}
a 3
hello index
执行时间: 1.0002954006195068
执行完了

四、有参装饰器(三层装饰器)

三层装饰器: 就是给双层装饰器加参数

例:在购物函数之前加上登陆功能,并判断账号密码的来源

# 三层装饰器: 给双层装饰器加参数

# 判断账号密码来自于哪个地方
def auth(engine):   # 来源
    def login(func):    # 函数对象形参
        def inner(*args, **kwargs):     # 形参
            # 登录功能
            if engine == 'file':
                username = input('usrename:')
                pwd = input('pwd:')
                if username == 'nick' and pwd == '123':
                    print('登录成功')
                    res = func(*args, **kwargs)  # shopping()   # 实参
                    return res
                else:
                    print('登录失败')

            elif engine == 'db':
                print('账号密码来自于数据库,非法请求')

        return inner
    return login

@auth('db')     # 使用语法糖给装饰器传参
def shopping():
    print('shopping')

# 使用了语法糖的被装饰函数可以直接调用,但先判断了装饰器的参数
print(shopping(3))

账号密码来自于数据库,非法请求
None

五、装饰器模板

所谓前人栽树,后人乘凉。你写框架,我用模板。会用就行了。

5.1 双层装饰器模板

# 双层装饰器模板
def outter(func): # 函数对象形参
    def inner(*args, **kwargs):   # 函数形参
        # 加功能
        print(f"这里是函数参数args:{args}, kwargs:{kwargs}")
        res = func(*args, **kwargs)   # 解压缩 func是被装饰的函数 函数实参
        return res

    return inner

@outter     # 语法糖(表示使用outter函数进行装饰) 相当于 func = outter(func)
def func(*args, **kwargs):
    print('双层装饰器模板')
    
# 使用
func("位置参数1",kv="关键字参数1")  # 可以传递任意实参

这里是函数参数args:('位置参数1',), kwargs:{'kv': '关键字参数1'}
双层装饰器模板

5.2 三层装饰器模板

# 三层装饰器模板: 给双层装饰器加参数的
def outter(engine): # 给装饰器传参
    def inner(func):    # func是真正的功能函数
        def wrapper(*args, **kwargs):  # wrapper是未来要运行的函数
            # 加功能
            print(f"这里是装饰器参数engine:{engine}")
            print(f"这里是函数参数args:{args}, kwargs:{kwargs}")
            res = func(*args, **kwargs)  # func是被装饰的函数
            return res

        return wrapper

    return inner


# 语法糖(表示使用outter函数进行装饰并传参)
# 相当于inner = outter("file")
# func = inner(func)
@outter('装饰器参数')
def func(*args, **kwargs):
    print('三层装饰器模板')

# 使用
func("位置参数1",kv="关键字参数1")  # 可以传递任意实参

这里是装饰器参数engine:装饰器参数
这里是函数参数args:('位置参数1',), kwargs:{'kv': '关键字参数1'}
三层装饰器模板

总结:

其实我是不太喜欢写总结的。。。但是因为这章比较难,怕读者理解不深刻,还是总结一下。嘿嘿

其实我个人理解。装饰器就相当于是一个加工厂。可以这么认为。

把你需要加工的函数放到装饰器中会得到一个加工后的函数对象。直接调用他就有了装饰器之后的功能。

其实原理就是之前说的闭包函数。通过闭包函数返回的函数对象进行调用。就有了闭包函数中的一些元素和功能。

重点来了。实际上大家只要会用装饰器+语法糖的格式就可以了。实在不行,不是还有装饰器模板嘛。嘿嘿嘿

原文地址:https://www.cnblogs.com/XuChengNotes/p/11341354.html