python基础:函数装饰器基础知识及高级用法
闭包 就是为了解决函数嵌套时,局部变量在赋值前引用的问题(不同作用域调用的问题)。除了使用global声明全局变量外,还可以使用nonlocal 声明自由变量,实现闭包。
在此之前我们先了解一下基础知识。
作用域
内建作用域(.py文件)、全局作用域、局部外的局部作用域(闭包函数外面– 局部外的局部)、局部作用域
g_count = 0 # 全局作用域
def outer():
o_count = 1 # 闭包函数外面的 局部作用域
def inner():
i_count = 2 # 局部作用域
全局变量和局部变量
内部作用域想修改外部作用域的变量时,使用 global 和 nonlocal 关键字了。
1.内部修改全局变量
# 修改全局变量 num
num = 10
print(f'内部修改全局变量前 num:{num}')
def fun1():
global num # 需要使用 global 关键字声明
num = num + 1 # 内部修改全局变量
fun1()
print(f'内部修改全局变量后 num:{num}')
2.嵌套函数内层函数 修改 局部外的 局部变量 ,使用nonlocal 关键字
num = 10
def outer():
num = 555 # 闭包函数外的函数中
print(f'嵌套函数 内部修改 局部外的局部变量 前num:{num}')
def inner():
nonlocal num # nonlocal关键字声明
num = num + 1
inner()
print(f'嵌套函数 内部修改 局部外的局部变量 后num:{num}')
outer()
print(f'全局变量num的值:{num}')

稍微修改一下,去掉nonlocal num # nonlocal关键字声明
# 内层嵌套函数 修改 局部外的局部(非全局作用域)变量则需要 nonlocal
num = 10
def outer():
num = 555 # 闭包函数(inner)外面--局部外的局部变量
print(f'嵌套函数 内部修改 局部外的局部变量 前num:{num}')
def inner():
num = num + 1
inner()
print(f'嵌套函数 内部修改 局部外的局部变量 后num:{num}')
outer()
print(f'全局变量num的值:{num}')
不使用nonlocal进行关键字声明,内嵌函数inner 内 num = num + 1 本来想要调用 outer中的 num的值,会报错 - 变量在赋值前引用。
UnboundLocalError: local variable 'num' referenced before assignment
nonlocal作用具体作用:把变量num标记为自由变量,即使在函数中为变量赋值了,也仍然是自由变量。
自由变量
自由变量是相对 全局变量和局部变量 而言的。 子作用域内(inner)可以取到父作用域内(outer)的变量,对于 子作用域(inner)而言,就是自由变量。
注意点
对于列表、字典等可变类型来说,添加元素不是赋值,赋值 不会改变自有变量的身份。
对于数字、字符串、元组等不可变类型以及None来说,赋值会 将其 改变为 局部变量。
# 情况一 举例
def funA():
# 可变类型
count = {}
def hello(new_value):
print(f'打印:{count}') # 成功
count[new_value] = new_value # 赋值
return hello
a = funA()
a(111)
# 情况2 举例
def funA():
# 不可变类型
count = 1 # 或者 count = None
def hello(new_value):
print(f'打印:{count}') # 报错
count = new_value # 赋值
return hello
被装饰函数是否有入参,如何设计装饰器?
被装饰函数 无入参
函数装饰器funA()是一个可调用对象,它的参数fn是被装饰函数(无入参)。
# funA 作为装饰器函数
def funA(fn):
def hello():
print("在 被装饰函数 <前> 执行")
fn() # 被装饰函数(装饰器的入参)
print("在 被装饰函数 <后> 执行")
return fn()
return hello
@funA
def funB():
print("执行被装饰函数")
return '执行完毕'
a = funB()
程序执行流程为:
在 被装饰函数 <前> 执行
执行被装饰函数
在 被装饰函数 <后> 执行
执行完毕
在此基础上,如果在上面程序末尾 打印funB 函数的返回值
print(a)
# 其输出结果为
执行完毕
被装饰函数有入参,如何写装饰器
# *args 和 **kwargs 作为装饰器内部嵌套函数的参数,*args 和 **kwargs 表示接受任意数量和类型的参数
def funA(fn):
# 定义一个嵌套函数(作用:获取函数的运行时间)
def hello(*args, **kwargs):
print(f"在 被装饰函数:{fn.__name__} <前> 执行")
ret = fn(*args, **kwargs)
print("在 被装饰函数 <后> 执行")
return ret
return hello
@funA
def funB(data1, data2):
print(f"执行被装饰函数,入参:{data1}, {data2}")
funB("oh", "it is ok")

嵌套函数装饰器
@funA
@funB
@funC
def fun():
#...
上面程序的执行顺序是里到外,所以它等效于下面这行代码
fun = funA( funB ( funC (fun) ) )
如果需要装饰器 对 带有函数的返回值进行修改,而不是直接返回被装饰函数的返回值 ,该怎么处理?

函数装饰器小结:
函数装饰器 funA() 去装饰另一个函数 funB(),其底层执行了如下 2 步操作:
将 funB() 作为参数传给funA() 函数;
funA()、funB()接受相同的参数
同时还可以做一些额外的操作
返回被装饰函数funB()本身的返回值
参数化装饰器
怎么让装饰器接受参数呢?答案是:创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。
待补充。。。。。。。。。
实际应用
我写过的几个装饰器。