python之函数装饰器
装饰器(闭包函数的一种应用)
那么,我们就要先理解,什么是闭包函数。
首先,让我们先看两段代码:
def foo(): num = 10 # 局部变量 print(num) # NameError: name 'num' is not defined
# 对于print而言,是不知道num这个变量的存在的。
num = 10 # 全局变量
def foo():
print(num) # 10
# 对于print而言,可以找到这个num变量。
(以上两个例子会涉及到变量的作用域的问题,回头再说)
下面再来看一段代码:
def print_msg():
# print_msg 是外围函数
msg = "it is a msg"
def printer():
# printer是嵌套函数
print(msg)
printer()
# 输出 it is a msg
print_msg()
仔细观察上面的代码,你会发现,printer这个方法,是藏在print_msg这个方法的内部的,也就是说,对于使用print_msg的人来说,是不知道printer这个方法的存在的。
但是,有没有一种做法,能够让外面使用的人,知道内部的东西呢?
这时候,闭包函数,就应运而生了。也就是说,闭包函数就是为了是局部变量在函数外被访问成为可能。再来看一段代码:
def print_msg():
# print_msg 是外围函数
msg = "it is a msg"
def printer():
# printer 是嵌套函数
print(msg) # 内嵌函数引用了外围函数的变量
return printer
another = print_msg() # 调用这个print_msg方法,返回的是printer这个函数的内存地址。这是重点。
# 输出 it is a msg
another()
仔细观察上面的代码,你会发现,其实another这个变量,指向的就是printer函数的内存地址。那么,another()不就是printer()吗?你会发现,通过return这个操作,我们就将print_msg方法内部的东西,拿出来了。这就是闭包的意义所在。
ok,理解了啥是闭包,我们来看看啥事装饰器。
再来看一段代码:
def print_msg(out_msg):
# print_msg 是外围函数
msg = out_msg
def printer():
# printer 是嵌套函数
print(msg) # 内嵌函数引用了外围函数的变量
return printer
another = print_msg(out_msg="qwer") # 调用这个print_msg方法,返回的是printer这个函数的内存地址。这是重点。
# 输出 qwer
another()
装饰器(闭包函数的一种应用)
1. 什么是装饰器
首先,女人为啥要化妆(装饰自己)?为了好看,是吧。那么,程序为啥要化妆(装饰自己)呢?那是为了功能更丰满。所以,装饰器就是用来为被装饰器对象添加新功能的工具。
需要注意的是:装饰器本身可以是任意可调用对象,被装饰器的对象也可以是任意可调用对象
2. 为何要用装饰器
因为在实际开发环境中,是不允许对原代码进行操作,而且不能破坏原有的调用关系,根据实际要求还需要增添新功能
开发需要遵循的一个原则
开放封闭原则:封闭指的是对修改封闭,对扩展开放,就是保持原有功能并新增新的功能
装饰器的实现必须遵循两大原则:
1. 不修改被装饰对象的源代码,被操作的函数的源代码是不能进行修改
2. 不修改被装饰器对象的调用方式
装饰器的目标:就是在遵循1和2原则的前提下为被装饰对象添加上新功能
3. 怎么用装饰器
无参装饰器
装饰器不需要参数传入
有参装饰器
装饰器需要参数传入
# 装饰器模板
def wrapper(func):
def inner(*args, **kwargs):
# 在调用函数前加功能
res = func(*args, **kwargs) # 调用被装饰的,也就是最原始的那个函数
# 在调用函数后加功能
return res
return inner
我们来分析一下上面的例子:
首先,有个外部函数,有一个变量,这个函数返回了内部函数的一个内存地址。那么
如果外部这么使用:func = wrapper(func)
那就相当于:func = inner # 因为wrapper的返回值是inner这个函数的内存地址。
那么func()就相当于inner()。仔细看着inner这个函数,其实内部还是执行的func这个函数(是作为wrapper的变量传进去的),
也就是说,最后的最后,其实转了一大圈,又回来了。
那么,为啥要转一大圈呢,不是闲着没事干。
而是为了在真正执行函数之前,加点啥骚操作,
在执行函数之后,加点骚操作。
但是对于运行函数的人而言。并没有察觉到我们加了骚操作。
语法糖
什么是语法糖?
使用了”@”语法糖后,就不需要额外代码来给”被装饰函数”重新赋值了,
其实”@装饰器函数”的本质就是”被装饰函数 = 装饰器函数(被装饰函数#原始的被装饰函数的内存地址)”。
为什么用语法糖?
使用语法糖让代码简化,不需要重复在调用装饰器时,进行重复的赋值操作。
也就是说:
@wrapper
def test_print():
print("这是打印函数")
其实就是:
test_print = wrapper(test_print) <==> @wrapper
那么,上么的代码也可以写为:
def test_print():
print("这是打印函数")
test_print = outter(test_print)
test_print()
例子:
# 装饰器模板
def outter(func):
def wrapper(*args, **kwargs):
# 在调用函数前加功能
print('before')
res = func(*args, **kwargs) # 调用被装饰的\也就是最原始的那个函数
# 在调用函数后加功能
print('after')
return res
return wrapper
@outter
def test_print():
print("这是打印函数")
# test_print = outter(test_print)
test_print()
===========
后话:
注意:
1.需要注意的是装饰器最多有三层
2.解释装饰器时@语法的时候是”自下而上运行“
3.执行装饰器时装饰器内的那个wrapper函数时的是”自上而下“