装饰器模式
有时候我们可能要对一些函数添加一些功能,但又不想,也没必要破坏它的内部功能,比如测执行时间,单元测试,怎么办呢?没关系,Python中内置了一种新语法,允许给函数加上一层封装,称为装饰器函数,调用目标函数将把外层装饰器函数的上下文也一并执行出来,这种执行方式的优点在于,如果新增的功能不需要了,只需要删除装饰器函数即可,原有的目标函数不会受到任何影响。
一个典型的装饰器实现如下
import time
from functools import wraps
def decorator(fn):
@wraps(fn) #使得终端调用func.__name__ == func而不是wrapper
def wrapper():
start_time = time.time()
fn()
end_time = time.time()
print("exec time: %s ms" % (end_time - start_time));
return wrapper;
@decorator #相当于decorator(func)
def func():
print("2023-3-7")
if __name__ == '__main__':
func()
"""
output:
2023-3-7
exec time: 0.0 ms
"""
python能够实现这种设计的原因是,函数在python中属于和List, dict
等类型同等的对象,在函数内定义函数本质也是定义对象,因此上面的decorator函数可看作定义并返回了一个wrapper的对象,这种在函数内部定义的被称为闭包函数,闭包函数正是Python实现装饰器的底层基础。
如果换到C++,虽然lamada也能承担闭包的功能,但是没法像使用原有函数一样使用装饰后的函数。
#include <iostream>
#include <time.h>
#include <functional>
using namespace std;
std::function<void()> decorator(std::function<void()> fn)
{
auto wrapper = [&](){
clock_t start_time = clock();
fn();
clock_t end_time = clock();
cout << "exec time: " << end_time - start_time << "ms" << endl;
};
return wrapper;
}
void func()
{
printf("2023-3-7\n");
}
int main(){
/*
output:
2023-3-7
exec time: 2ms
*/
decorator(func)();
}
由于python支持内部定义闭包,因此如果装饰器本身要传入参数,还可以设计成这种三层的闭包:
import time
from functools import wraps
def log(text):
def decorator(fn):
@wraps(fn) #使得终端调用func.__name__ == func而不是wrapper
def wrapper():
start_time = time.time()
fn()
end_time = time.time()
print("text: %s exec time: %s ms" % (text, end_time - start_time));
return wrapper
return decorator
@log('test') #注意可以通过@语法直接向装饰器传参, 这里相当于 log('test')(func)
def func():
print("2023-3-7")
if __name__ == '__main__':
func()
翻译成C++应该是这样,C++11的auto真的省了很多类型推断的事情。
#include <iostream>
#include <time.h>
#include <functional>
using namespace std;
typedef std::function<void()> CallBack;
auto log(string text){
auto decorator = [&](CallBack fn) -> CallBack{
auto wrapper = [&]() -> void {
clock_t start_time = clock();
fn();
clock_t end_time = clock();
cout << "text: " << text << " exec time: " << end_time - start_time << "ms" << endl;
};
return wrapper;
};
return decorator;
}
void func()
{
printf("2023-3-7\n");
}
int main()
{
/*
output:
2023-3-7
text: text exec time: 1ms
*/
log("test")(func)();
}
总结:在设计模式的概念上, Python通过语法,直接在函数层面实现了装饰器,而像C++,Java这样的静态类型语言欲实现只能通过多态+组合的手法在类层面实现。