from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not can_run:
return "Function will not run"
return f(*args, **kwargs)
return decorated
@decorator_name
def func():
return "Function is running"
can_run = True
print(func())
can_run = Flase
print(func())
注意:@wraps接受⼀个函数来进⾏装饰,并加⼊了复制函数名称、注释⽂档、参数列表 等等的功能。这可以让我们在装饰器⾥⾯访问在装饰之前的函数的属性。
使⽤场景
授权(Authorization)
装饰器能有助于检查某个⼈是否被授权去使⽤⼀个web应⽤的端点(endpoint)。它们被⼤量
使⽤于Flask和Django web框架中。这⾥是⼀个例⼦来使⽤基于装饰器的授权:
from functools improt wraps
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
authenticate()
return f(*args, **kwargs)
return decorated
⽇志(Logging)
⽇志是装饰器运⽤的另⼀个亮点。这是个例⼦:
from functools import wraps
def logit(func):
@wraps(func):
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
"""Do some math."""
return x + x
return = addition_func(4)
在函数中嵌⼊装饰器
我们回到⽇志的例⼦,并创建⼀个包裹函数,能让我们指定⼀个⽤于输出的⽇志⽂件。
from functools import wraps
def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(func):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile,并写入内容
with open(logfile, 'a') as open_file:
# 现在将日志打到指定的logfile
opened_file.write(log_string + '\n')
return wrapped_function
return logging_decorator
@logit()
def myfuncl():
pass
myfunc1()
# Output: myfunc1 was called
# 现在⼀个叫做 out.log 的⽂件出现了,⾥⾯的内容就是上⾯的字符串 @logit(logfile='func2.log')
def myfunc2():
pass
myfunc2():
pass
myfunc2()
# Output: myfunc2 was called
# 现在⼀个叫做 func2.log 的⽂件出现了,⾥⾯的内容就是上⾯的字符串
装饰器类
现在我们有了能⽤于正式环境的logit
装饰器,但当我们的应⽤的某些部分还⽐较脆弱
时,异常也许是需要更紧急关注的事情。⽐⽅说有时你只想打⽇志到⼀个⽂件。⽽有时你
想把引起你注意的问题发送到⼀个email,同时也保留⽇志,留个记录。这是⼀个使⽤继承
的场景,但⽬前为⽌我们只看到过⽤来构建装饰器的函数。
幸运的是,类也可以⽤来构建装饰器。那我们现在以⼀个类⽽不是⼀个函数的⽅式,来重
新构建logit
。
class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile并写⼊
with open(self.logfile, 'a') as opened_file:
# 现在将⽇志打到指定的⽂件
opened_file.write(log_string + '\n')
# 现在,发送⼀个通知
self.notify() def notify(self):
# logit只打⽇志,不做别的
pass
这个实现有⼀个附加优势,在于⽐嵌套函数的⽅式更加整洁,⽽且包裹⼀个函数还是使⽤
跟以前⼀样的语法:
@logit()
def myfunc1():
pass
现在,我们给logit
创建⼦类,来添加email的功能(虽然email这个话题不会在这⾥展开)。
class email_logit(logit):
'''
⼀个logit的实现版本,可以在函数调⽤时发送email给管理员
'''
def __init__(self, email='admin@myproject.com', *args, **kwargs)
self.email = email
super(logit, self).__init__(*args, **kwargs)
def notify(self):
# 发送⼀封email到self.email Python进阶
# 这⾥就不做实现了
pass
从现在起,@email_logit
将会和@logit
产⽣同样的效果,但是在打⽇志的基础上,还
会多发送⼀封邮件给管理员。