装饰器(Decorator)
什么是闭包?
官方解释:
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自有变量的函数。
这个被引用的自有变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外
装饰器
装饰器就是修改其他函数功能的函数。使用装饰器可以简化代码,让你的代码看起来更加 pythonic。
python 提供了 @ 符号作为装饰器的语法糖。使用语法糖要求装饰函数必须 return 一个函数对象
1. 对不带参数的函数进行装饰
def logging(func):
def _deco():
print("%s is running"% func.__name__)
func()
return _deco
@logging
def bar():
print('I am bar')
bar()
# 运行结果
#bar is running
#I am bar
2. 对带参数的函数进行装饰
def logging(func):
def _deco(a, b):
print("%s is running"% func.__name__)
func(a, b)
return _deco
@logging
def bar(a, b):
print('I am bar:', a+b)
bar(1, 2)
# 运行结果
#bar is running
#I am bar:3
3. 函数参数数量可变
def logging(func):
def _deco(*args, **kwargs):
print("%s is running"% func.__name__)
func(*args, **kwargs)
return _deco
@logging
def bar(a, b):
print('I am bar:', a+b)
@logging
def foo(a, b, c):
print('I am foo:', a+b+c)
@logging
def foo2(**kwargs):
for k in kwargs:
print('I am foo2:', kwargs[k])
bar(1, 2)
foo(1, 2, 3)
foo2(a=1)
# 运行结果
# bar is running
# I am bar: 3
# foo is running
# I am foo: 6
# foo2 is running
# I am foo2: 1
4. 装饰器带参数
带参数的装饰器函数将有 3 层函数,如下所示:
def logging(level):
def _deco(func):
def __deco(*args, **kwargs):
if level == 'error':
print("%s is running"% func.__name__)
return func(*args, **kwargs)
return __deco
return _deco
@logging('error')
def bar(a, b):
print('I am bar:', a+b)
# 运行结果
# bar is running
# I am bar: 3
# I am foo: 6
5. python 自带的装饰器函数 functools.wraps(func)
使用 @functools.wraps(func)
装饰器,可以返回原函数的元信息,比如 docstring、name、参数列表等
如下未使用该装饰器,打印 bar.name:
使用了该装饰器:
6. 实现带参数 和 不带参数的装饰器的自适应
import functools
def logging(arg):
if callable(arg): #如果传入的参数是个函数,那么走不带参数的装饰器函数分支
@functools.wraps(arg)
def _deco(*args, **kwargs):
print("%s is running" % arg.__name__)
return arg(*args, **kwargs)
return _deco
else: # 否则走带参数的装饰器函数分支
def _deco(func):
@functools.wraps(func)
def __deco(*args, **kwargs):
if arg == 'error':
print("error: %s is running"% func.__name__)
return func(*args, **kwargs)
return __deco
return _deco
@logging('error')
def bar(a, b):
print('I am bar:', a+b)
print(bar.__name__)
@logging
def foo(a, b, c):
print('I am foo:', a+b+c)
bar(1, 2)
foo(1, 2, 3)
# 结果:
#error: bar is running
#I am bar: 3
#bar
#foo is running
#I am foo: 6
7. 装饰器类
上面介绍的都是装饰器函数,但在使用时有时只想打日志到一个文件,有时要把引起问题的异常发送到一个邮件,同时也保留日志。这个时候就需要用到类装饰器、以及继承来实现。
运行结果: