Dawn's Blogs

分享技术 记录成长

0%

Python杂谈 (1) 装饰器

装饰器

装饰器(Decorators),是 Python 中修改其他函数的功能的函数,有助于让我们的代码更简短。

比如可以用一个简易的装饰器,定义一个函数执行之前或者执行之后做的额外工作

1
2
3
4
5
6
7
8
9
10
11
# 这是一个装饰器
def a_new_decorator(a_func):

def wrapTheFunction():
print("I am doing some boring work before executing a_func()")

a_func()

print("I am doing some boring work after executing a_func()")

return wrapTheFunction

当一个函数需要这个装饰器时,可以这样调用:

1
2
3
4
5
6
7
8
9
10
def a_function_requiring_decoration():
print("I am the function which needs some decoration to remove my foul smell")

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()

a_function_requiring_decoration()
#outputs:I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()

@ 符号

以上就是一个手动实现的装饰器,可以用 @ 符号来更简单的实现。事实上,@a_new_decorator 就是以下语句的简易声明

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

1
2
3
4
5
6
7
8
@a_new_decorator
def a_function_requiring_decoration():
print("I am the function which needs some decoration to remove my foul smell")

a_function_requiring_decoration()
#outputs: I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()

但是,运行如下代码会存在一个问题

1
2
print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction

因为函数 a_function_requiring_decoration 被重写了,所以这个函数被 warpTheFunction 替代了,这里就需要 functools.wraps

functools.wraps

修改修饰器的声明方式即可解决上述问题:

1
2
3
4
5
6
7
8
9
from functools import wraps

def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")
a_func()
print("I am doing some boring work after executing a_func()")
return wrapTheFunction

带参数的修饰器

修饰器也可以像普通函数一样(像 @wraps 一样接受一个参数),携带参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from functools import wraps

def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + "was called"
print(log_string)
# 打开logfile,写入内容
ith open(logfile, 'a') as opened_file:
# 现在将日志打到指定的logfile
opened_file.write(log_string + '\n')
return func(*args, **kwargs)
return wrapped_function
return logging_decorator

# 带参数的修饰器
@logit(logfile='func2.log')
def myfunc():
pass

修饰器类

类也可以构建修饰器,用修饰器类来重新构建上述记录日志的修饰器logit。子类可以重新类中的方法 notify ,来实现将日志发送 email、或者是其他自定义的功能。

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
from functools import wraps

class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile

def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):
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()
return func(*args, **kwargs)
return wrapped_function

def notify(self):
# 子类可以重写这个方法
pass


@logit()
def myfunc1():
pass

修饰器模板

以下是一个修饰器的蓝本规范:

1
2
3
4
5
6
7
8
9
10
11
12
13
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")