装饰器是用于封装函数或类的工具,在某些应用场景下,如插入日志、性能测试、事务处理、权限校验等业务逻辑中的到很好应用,比如Flask的路由表即用了装饰器。有利于减少重复代码,可扩展性强。
本篇主要介绍如下:
1) python的局部变量和全局变量
2)python 闭包,闭包与变量、装饰器关联
3)python 装饰器
一、python的局部变量和全局变量
全局变量是在整个py文件中都起作用的,局部变量是在函数内定义,只在函数内起作用的变量。
1.1 局部变量和全局变量同一个名字,修改局部变量,全局变量不会改变。
#!/usr/bin/python
# -*- coding: utf-8 -*-
b = 6
def f1(a):
print a
b = 5
print b
f1(3)
print b
结果
3
5
6
1.2. 函数内部直接使用未定义局部变量会出错
#!/usr/bin/python
# -*- coding: utf-8 -*-
b = 6
def f1(a):
print a
print b
b = 5
f1(3)
结果:
F:\test>python test.py
File"test.py", line 9
b = 5
^
IndentationError: unindent does not match anyouter indentation level
原因是,python在编译test.py的时函数f1时,会判断b是局部变量,但是打印b时,发现没有绑定值。
1.3. 函数内使用全局变量,用global修饰
#!/usr/bin/python
# -*- coding: utf-8 -*-
b = 6
def f1(a):
globalb
printb
b =5
f1(3)
print b
结果:
6
5
二、闭包
闭包是指延伸了作用域的函数,它会保留定义函数时存在的自由变量的绑定。
#!/usr/bin/python
# -*- coding: utf-8 -*-
def count():
a = 1
def sum():
c = 1
return a + c # a为自由变量
return sum
count_tmp = count()
print count_tmp ()
结果:
2
函数count整体是闭包,a是自由变量,按以上,a为局部变量,调用sum函数时a已经返回了,但是依旧可以使用a的绑定。
可变对象与不可变对象参考:https://www.cnblogs.com/sun-haiyu/p/7096918.html
python中不可变对象指对象指向的内存中的值不能被改变,当操作该对象时,会重新复制一份再改变,如数值类型int、float、字符串str、元组tuple都是不可变对象。
可变对象是指可以被操作地址不会改变的对象,如列表、字典、集合set。
2.1 自由变量为可变对象的闭包
如下面例子,自由变量为列表。
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import division
def make_averager():
series= []
defaverage(new_value):
series.append(new_value)
total= sum(series)
returntotal / len(series)
returnaverage
avg = make_averager()
print avg(10)
print avg(11)
print avg(12)
结果:
10.0
10.5
11.0
from __future__ import division 是采用精确除法,可以看到,调用make_averager()时,返回一个average对象,每次调用average,会把历史加入的新值保存在可变对象average中,然后计算当前平均值。
2.2 自由变量为不可变对象闭包
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import division
def make_averager():
count= 0
total= 0
defaverage(new_value):
count+= 1
total+= new_value
returntotal / count
returnaverage
avg = make_averager()
print avg(10)
结果:
t0 is 2.05261256527e-06
will sleep 5 seconds
t1 is 5.00363380844
因为count为不可变对象,当对count操作加1时,会将自由变量count变成局部对象,此时局部对象count还未被赋值就进行使用导致出错。
在闭包中,不可变对象只能读取,不能更新,如果更新了,就隐式创建了局部变量,此变量不会保存在闭包中了。
解决方法:Python 3引入了nonlocal声明,它的作用是将变量声明为自由变量,即使对nonlocal声明的变量赋予新值,也会变成能自由变量,保存在闭包中。
三、装饰器
3.1 装饰器与函数交互
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
def clock(func):
defclocked(*args):
t0= time.clock()
print"t0 is %s"% t0
result= func(*args)
t1= time.clock()
print"t1 is %s" % t1
returnresult
returnclocked
@clock
def snooze(seconds):
print"will sleep %s seconds" % seconds
time.sleep(seconds)
snooze(5)
结果:
t0 is 2.05261256527e-06
will sleep 5 seconds
t1 is 5.00363380844
@clock 是装饰器语法,代表一个装饰器,snooze是被装饰的函数,
@clock
def snooze(seconds):
print"will sleep %s seconds" % seconds
time.sleep(seconds)
等价于:
def snooze(seconds):
print"will sleep %s seconds" % seconds
time.sleep(seconds)
snooze = clock(snooze)
clock整体可以看做是一个闭包,func为自由变量,因此clocked可以使用此自由变量。调用clock,返回clocked,那么调用snooze(5)相当于调用clocked(5)。此时snooze变成了clocked的引用。
因此此处装饰器的作用是保证clocked()函数打印出程序运行时间:
t0 is2.05261256527e-06
t1 is 5.00363380844
又通过被装饰函数snooze增加休眠5秒的功能:
will sleep 5 seconds
参考资料《流畅的Python》