装饰器是什么?
装饰器(Decorators)是 Python 的一个重要部分。举一个不太恰当的比方,装饰器是一个函数,它以函数为参数,先执行一些操作,再调用作为参数的函数,然后再执行以下操作,例如下面的函数:
1 2 3 4
| def my_decorator(func): print("Do something before call func.") func() print("Do something after call func.")
|
没错,就像C语言里的回调函数一样
可以这样使用它:
1 2 3 4 5 6 7 8 9
| def greet(): print("Hello!")
my_decorator(greed) """输出 Do something before call func. Hello Do something after call func. """
|
然而,python作为一个优雅的语言,使用了特殊语法简化了上面的操作,下面的例子和上面的是等效的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def my_decorator(func): print("Do something before call 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一切皆文件@_@
现在我们将其修改一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def a_new_decorator(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!")
greet()
|
保持函数名
现在我们在上面的代码后面再加一行:
1 2 3 4
| print(greet.__name__) """输出 wrapped """
|
哦不,这不是我们想要的结果,函数名被装饰器改写了!
幸好functiontools
的warps
装饰器可以解决这个问题,再改!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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
也是装饰器,但他为什么要加括号调用,还可以添加参数?
先看看这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def a(): def b(): def c(): def d(): return 1
return d
return c
return b
response = a()()()()
|
这样来看就很简单了,就是再嵌套一个函数嘛。。
有了这个思路,再改!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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 """
|
花样写日志
函数做装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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())
|
类做装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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())
|
这样做的好处是可以改写,而且比嵌套函数的方式更加整洁,下面的例子来自菜鸟教程
创建一个子类:
1 2 3 4 5 6 7 8 9 10 11
| class email_logger(Logger): ''' 一个logit的实现版本,可以在函数调用时发送email给管理员 ''' def __init__(self, email='admin@myproject.com', *args, **kwargs): self.email = email super(email_logger, self).__init__(*args, **kwargs) def notify(self): pass
|