什么是闭包呢? Closure
如果在一个函数的内部函数中,有对外部作用域的一个引用,那么内部函数就是一个闭包。在使用时,可以直接调用内部函数,内部函数也照样可以使用外部函数的变量。
例如:
def outter():
a = 1 # a为inner的外部作用域
def inner():
print(a)
return inner
outter()() # 直接调用inner()
打印结果为1
什么是装饰器? Decorator
装饰器就是在给原有的函数加装饰。在不修改原有函数的基础上添加原有函数的功能。
比如:
原有功能函数method1、method2,要给原有功能函数添加计算函数执行时间的功能,并且不能修改原有函数,因为原有函数可能已经在很多地方使用,轻易修改的话可能会造成系统崩溃。
这时,我们就可以用闭包的方法来来给函数添加新功能。
举例:
原有函数:
import time
def method01():
time.sleep(2)
print('method1')
pass
def method02():
time.sleep(3)
print('method2')
pass
现在要给这两个函数添加计算执行时间。
def perform_time(func):
start_time = time.time()
func()
end_time = time.time()
print(end_time - start_time)
perform_time(method1)
perform_time(method2)
虽然,这样也完成了,但是每个函数都要这样调用,太麻烦了。有没有更简单点的办法呢?其实,我们可以使用闭包的方法来完成。
def perform_time(func):
"""
使用闭包的方法,也就是装饰器方法。
func作为inner()的外部变量调用。
"""
def inner():
start_time = time.time()
func() # inner调用外部资源
end_time = time.time()
print(end_time - start_time)
# 返回内部函数
return inner
method01()
print('===================================')
method01 = perform_time(method01)
# 将perform_time(method01) 赋值给method1,实际上是将method01指向perform_time(method01)的内存地址。以后调用method01()就可以得到函数的执行结果。
method01()
执行结果为:
Connected to pydev debugger (build 182.5107.22)
method1
===================================
method1
2.0030062198638916
Process finished with exit code 0
我们可以使用装饰器的写法@
在method2函数的上面添加@perform_time, 这句话的意思其实就是执行 method01 = perform_time(method01)
@perform_time
def method02():
time.sleep(3)
print('method2')
pass
method01()
print('===================================')
method01 = perform_time(method01)
method01()
print('===================================')
method02()
执行结果为:
Connected to pydev debugger (build 182.5107.22)
method1
===================================
method1
2.0060038566589355
===================================
method2
3.006004810333252
Process finished with exit code 0
这样,我们的代码更加简洁,我们也完成了在不修改原有代码的基础上,添加新功能。这就是装饰器的作用。
接着,我们还有带参数的函数也需要加入计算时间。
def add(*args, **kwargs):
"""
计算传入参数的累加
"""
ret = 0
if args:
# 元组处理
for arg in args:
ret += arg
elif kwargs:
# 字典处理
for arg in kwargs.values():
ret += arg
return ret
修改装饰器函数perform_time
def perform_time(func):
def inner(*args, **kwargs): # 传入任意参数
start_time = time.time()
ret = func(*args, **kwargs) # 记录函数返回值
end_time = time.time()
print(end_time - start_time)
return ret # 返回值
return inner
最后在add函数上加上装饰器
@perform_time
@perform_time
def add(*args, **kwargs):
ret = 0
if args:
for arg in args:
ret += arg
elif kwargs:
for arg in kwargs.values():
ret += arg
return ret
method01()
print('===================================')
method01 = perform_time(method01)
method01() # 无参数,无返回值
print('===================================')
method02() # 无参数,无返回值
print('===================================')
print(add(1, 2, 3, 4, 5)) # 传入元组参数
print(add(a=1, b=2, c=3, d=4, e=6)) # 传入字典参数
返回结果:
Connected to pydev debugger (build 182.5107.22)
method1
===================================
method1 # 无参数无返回值,不影响结果
2.0001144409179688
===================================
method2
3.000171661376953
===================================
0.0 # 耗时
15
0.0 # 耗时
16
Process finished with exit code 0
带参数的装饰器:
现在,我们需要给某些函数添加日志,某些函数不需要添加日志。这时,我们就需要用到带参数的装饰器。
为了让装饰器能够接收是否添加日志参数,需要在原有装饰器外,再嵌套一层函数。
def perform_time_logger(is_log=False):
def perform_time(func):
def inner(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
end_time = time.time()
print(end_time - start_time)
# 判断是否打印日志
if is_log:
# 获取参数
if args or kwargs:
if kwargs:
args = kwargs
else:
args = ''
# 打印日志
print('function: {0} has been performed with parameters {2} , on time {1} .'.format(func.__name__, time.ctime(start_time), args))
return ret
return inner
return perform_time
这是需要将所有@perform_time修改为@perform_time_logger(), 如果需要打印日志就将参数设置为True,不打印就无需添加参数。
@perform_time_logger(is_log=True)
def method01():
time.sleep(2)
print('method1')
pass
@perform_time_logger()
def method02():
time.sleep(3)
print('method2')
pass
@perform_time_logger(True)
def add(*args, **kwargs):
ret = 0
if args:
for arg in args:
ret += arg
elif kwargs:
for arg in kwargs.values():
ret += arg
return ret
print('===================================')
method01()
print('===================================')
method02()
print('===================================')
print(add(1, 2, 3, 4, 5))
print(add(a=1, b=2, c=3, d=4, e=6))
执行结果:
Connected to pydev debugger (build 182.5107.22)
===================================
method1
2.0001144409179688
function: method01 has been performed with parameters , on time Mon Jan 7 23:06:49 2019 .
===================================
method2
3.000171661376953
===================================
0.0
function: add has been performed with parameters (1, 2, 3, 4, 5) , on time Mon Jan 7 23:06:54 2019 .
15
0.0
function: add has been performed with parameters {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 6} , on time Mon Jan 7 23:06:54 2019 .
16
Process finished with exit code 0
这样,装饰器就告一段落了。
附完整代码:
# _*_ coding: utf-8 _*_
__author__ = 'jevoly'
import time
# def perform_time(func):
# start_time = time.time()
# func()
# end_time = time.time()
# print(end_time - start_time)
def perform_time_logger(is_log=False):
def perform_time(func):
def inner(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
end_time = time.time()
print(end_time - start_time)
if is_log:
if args or kwargs:
if kwargs:
args = kwargs
else:
args = ''
print('function: {0} has been performed with parameters {2} , on time {1} .'.format(func.__name__, time.ctime(start_time), args))
return ret
return inner
return perform_time
@perform_time_logger(is_log=True)
def method01():
time.sleep(2)
print('method1')
pass
@perform_time_logger()
def method02():
time.sleep(3)
print('method2')
pass
@perform_time_logger(True)
def add(*args, **kwargs):
ret = 0
if args:
for arg in args:
ret += arg
elif kwargs:
for arg in kwargs.values():
ret += arg
return ret
print('===================================')
method01()
print('===================================')
method02()
print('===================================')
print(add(1, 2, 3, 4, 5))
print(add(a=1, b=2, c=3, d=4, e=6))