Python装饰器
· 阅读需 5 分钟
装饰器(Decorators)是 Python 的一个重要部分。举一个不太恰当的比方,装饰器是一个函数,它以函数为参数,先执行一些操作,再调用作为参数的函数,然后再执行以下操作,例如下面的函数:
def my_decorator(func):
print("Do something before call func.")
func() # 调用func()
print("Do something after call func.")
没错,就像C语言里的回调函数一样
可以这样使用它:
def greet():
print("Hello!")
my_decorator(greed) # 注意这里不是调用 greed
"""输出
Do something before call func.
Hello
Do something after call func.
"""
然而,python作为一个优雅的语言,使用了特殊语法简化了上面的操作,下面的例子和上面的是等效的:
def my_decorator(func):
print("Do something before call func.")
func() # 调用func()
print("Do something after call func.")
@my_decorator # 也不是调用
def greet():
print("Hello!")
greet
"""输出
Do something before call func.
Hello
Do something after call func.
"""
装饰器的使用
适配带有参数的函数
前面的最简单的装饰器不能修饰带有参数的函数,而且修饰后的函数不能做参数之类的(想一想为什么)
其实也很容易解决,既然函数可以作为另一个函数的参数,那么它可不可以作为返回值呢?
当然可以!这是因为python一切皆对象,函数和变量究其本质都是对象我怎么联想到了Linux一切皆文件@_@
现在我们将其修改一下:
def a_new_decorator(func):
# python 可以在函数里再定义函数,即嵌套函数
def wrapped(*args, **kwargs):
print("Do something before call func.")
func(*args, **kwargs)
print("Do something after call func.")
return wrapped
@a_new_decorator
def greet():
print("Hello!")
greet()
保持函数名
现在我们在上面的代码后面再加一行:
print(greet.__name__)
"""输出
wrapped
"""
哦不,这不是我们想要的结果,函数名被装饰器改写了!
幸好functiontools
的warps
装饰器可以解决这个问题,再改!
from functools import wraps
def a_new_decorator(func):
@wraps(func)
def wrapped(*args, **kwargs):
print("Do something before call func.")
func(*args, **kwargs)
print("Do something after call func.")
return wrapped
@a_new_decorator
def greet():
print("Hello!")
print(greet.__name__)
"""输出
greet
"""
带参数的装饰器
上例中wraps
也是装饰器,但他为什么要加括号调用,还可以添加参数?
先看看这个:
def a():
def b():
def c():
def d():
return 1
return d
return c
return b
response = a()()()()
# response = 1
这样来看就很简单了,就是再嵌套一个函数嘛。。
有了这个思路,再改!
from functools import wraps
def final_decorator(output_filename="greet.txt"):
def a_new_decorator(func):
@wraps(func)
def wrapped(*args, **kwargs):
with open(output_filename, "a") as file:
greet_str = func(*args, **kwargs)
file.write(greet_str)
return greet_str
return wrapped
return a_new_decorator
@final_decorator("233.txt")
def greet(name):
return "Hello " + name
print(greet("skyone"))
"""输出
Hello skyone
"""
"""文件`123.txt`
Hello skyone
"""
花样写日志
函数做装饰器
from functools import wraps
def logger(logfile="out.log", callback=None):
def logger_decorator(func):
@wraps(func)
def wrapped_func(*args, **kwargs):
log_string = "[logger] function <" + func.__name__ + "> was called"
print(log_string)
with open(logfile, "a", encoding="utf-8") as file:
file.write(log_string + "\n")
if callback is not None:
callback()
return func(*args, *kwargs)
return wrapped_func
return logger_decorator
@logger()
def a():
return 3
print(a())
类做装饰器
from functools import wraps
class Logger:
def __init__(self, logfile="out.log", callback=None):
self.logfile = logfile
self.callback = callback
def __call__(self, func):
@wraps(func)
def wrapped_func(*args, **kwargs):
log_string = "[logger] function <" + func.__name__ + "> was called"
print(log_string)
with open(self.logfile, "a", encoding="utf-8") as file:
file.write(log_string + "\n")
if self.callback is not None:
self.callback()
self.notify()
return func(*args, **kwargs)
return wrapped_func
def notify(self):
pass
@logger()
def a():
return 3
print(a())
这样做的好处是可以改写,而且比嵌套函数的方式更加整洁,下面的例子来自菜鸟教程
创建一个子类:
class email_logger(Logger):
'''
一个logit的实现版本,可以在函数调用时发送email给管理员
'''
def __init__(self, email='[email protected]', *args, **kwargs):
self.email = email
super(email_logger, self).__init__(*args, **kwargs)
def notify(self):
# 发送Email的实现···
pass