浅聊python函数装饰器和闭包
- 1. 直观认识一下装饰器
- 2.修饰函数的特点:
- 3. 变量的作用域规则
- 4.闭包与nonlocal
1. 直观认识一下装饰器
装饰器是可以调用的对象,其参数是另一个函数(被修饰的函数),被修饰的函数进入装饰器以后有两种处理方式; 1) 被处理,单纯解释一下就是可能在原函数的基础上加上另外的操作,然后返回;2)被替换为另一个函数或者可调用的对象
下面看看看一个修饰器的直观印象
@decorate
def target():
print("running target()")
def target():
print("running target()")
target = decorate(target)#表示target函数作为参数传给修饰器
上述例子就是修饰器的最用,上述例子执行完毕得到的tatget函数不一定是原来的target函数了,而是被decorate修饰以后的函数;具体需要看修饰函数的具体行为是怎样的;
2.修饰函数的特点:
- 在加载模块的时候立即执行
- 能把被修饰的函数替换成其他函数
能把被修饰的函数替换成其他函数
例子如下:
# 自定义的修饰器,该修饰器的作用就是将原始被修饰的函数的函数名替换为inner
def deco(func):
def inner():
print("running inner()")
return inner
@deco
def target():
print("running target()")
>> target()
output: running inner()
target()->inner()->"rinning inner()"
在加载模块的时候立即执行
def deco(fun):
def f2():
print("222")
return f2
@deco
def f1():
print("123")
def f3():
print("333")
def main():
print("start")
print(deco)
f3()
if __name__=='__main__':
main()
start
<function deco at 0x000002DD3BB32820>
333
清晰可见,这里的调用main函数以后应该是先输出"start",但是这里很明显是直接西安调用了修饰器函数echo,所以装饰器函数在加载模块的时候会立即执行;
正是因为装饰器在导入的时候就执行,所以python程序就有了导入时和运行时的区别;函数装饰器在导入模块的时候立即执行,而被修饰的函数只有在明确调用的时候才运行
3. 变量的作用域规则
先来说说为为什么提到作用域
b = 6
def f(a):
print(a)
print(b)
b = 4
f(3)
这段代码在运行的时候会报错,原因是python在编译函数的时候,把b当作了局部变量,因为在使用b的时候还没有给他赋值(语句b=4在print(b)后);所以就造成了错误
local variable 'b' referenced before assignment
4.闭包与nonlocal
紧接第三部分,闭包是延伸了作用域的函数,包含了函数定义体中的应用,但是不在定义体中定义的非全局变量;例子如下:
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return total/len(series)
return averager
avg = make_averager()
>>avg(10)
>>10.0
avg(11)
>> 10.5
avg(12)
11.0
其中列表series就是没有在闭包函数体中定义但是在其函数引用的变量,注意在闭包函数体内,使用append函数其实是对变量series进行了修改的
在python中列表是可以被修改的,
闭包函数体示意图;
这里的自由变量的变量类型是可变的,但是如果自由变量是不可变得类型,比如数字、字符串和元组来说,只能读取不能更新;见下面得例子
def make_averager():
count = 0
total = 0
def averager(new_value):
count+=1
total += new_value
return total/count
return averager
这个例子就会出错,因为自由变量count和total作为不可变类型,在闭包函数中不能更新;所以就引入一个关键字nonlocal,作用就是把变量标记为自由变量
def make_averager():
count = 0
total = 0
def averager(new_value):
nonlocal count,total
count+=1
total += new_value
return total/count
return averager