Python的裝飾器理解
阿新 • • 發佈:2020-12-25
python的裝飾器其實是一個語法糖,給出一個python裝飾器的程式碼:
import time def timer(func): def warp(*args): t1 = time.time() res = func(*args) t2 = time.time() delta = t2 - t1 return res, delta * 1000 return warp
很顯然,這是一個計算測試函式執行時間的函式,假如我們想測試一個函式的執行時間,我們只需要在待測函式的上方增加@timer即可,程式碼如下:
@timer def test1(): return 10 ** 2
在main函式中我們只需要按照如下方式呼叫即可:
if __name__ == "__main__": res, delta = test1() print(res, delta) # 100, 0.0
如果不使用這個語法糖,我們可以按照如下方式呼叫:
def test2(): return 10 ** 2 if __name__ == "__main__": tmp_fun = timer(test2) res, delta = tmp_fun() print(res, delta) # 100, 0.0
將上述程式碼整合的結果就是:
import time def timer(func): def warp(*args): t1 = time.time() res = func(*args) t2 = time.time() delta = t2 - t1 return res, delta * 1000 return warp @timer def test1(): return 10 ** 2 def test2(): return10 ** 2 if __name__ == "__main__": tmp_fun = timer(test2) res, delta = tmp_fun() print(res, delta) res, delta = test1() print(res, delta)
但是這裡還可能存在這一些問題,比如當我們輸出test1.__name__時我們會得到裝飾器的名字這顯然不是我們想要的:
print(test1.__name__) # warp, 我們希望可以得到test1而非timer print(test2.__name__) # test2
很容易想到一個簡單的解決方法:
def timer(func): @functools.wraps(func) def warp(*args): t1 = time.time() res = func(*args) t2 = time.time() delta = t2 - t1 return res, delta * 1000 warp.__name__ = func.__name__ # 增加這一句程式碼 return warp
但這並不推薦,python為我們專門提供了相關的工具,我們只需要使用即可:
import functools def timer(func): @functools.wraps(func) def warp(*args): t1 = time.time() res = func(*args) t2 = time.time() delta = t2 - t1 return res, delta * 1000 # warp.__name__ = func.__name__ return warp
這樣我們就可以得到我們想要的名字了。另外如果你想在c++裡面實現類似的功能也是可以的,這裡提供一份c++17的程式碼,由於我們可能需要使用decltype(auto)所有至少需要c++14的標準才可以,相信你可以將這份程式碼使用c++14進行重寫。由於這邊文章主要是說python的裝飾器,故對c++程式碼不做過多解釋:
#include <chrono> #include <iostream> #include <tuple> template <typename Fn> decltype(auto) timer(Fn func) { return [&](auto&&... ts) -> decltype(auto) { auto t1 = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> duration; if constexpr (std::is_same_v<std::invoke_result_t<Fn, decltype(ts)...>, void>) { func(std::forward<decltype(ts)>(ts)...); auto t2 = std::chrono::high_resolution_clock::now(); duration = t2 - t1; return duration; } else { auto res = func(std::forward<decltype(ts)>(ts)...); auto t2 = std::chrono::high_resolution_clock::now(); duration = t2 - t1; return std::make_pair(res, duration); } }; } template <typename Fn> decltype(auto) make_timer_decorate(Fn&& fn) { return timer(std::forward<Fn>(fn)); } uint64_t get_sum(int n) { uint64_t res = 0; for (int i = 1; i <= n; ++i) { res += i; } return res; } void test1() { auto d = make_timer_decorate(get_sum); auto [res, t] = d(10000005); std::cout << res << std::endl; std::cout << t.count() * 1000 << "ms" << std::endl; } void print(int& x) { x++; std::cout << x << std::endl; } void test2() { auto d = make_timer_decorate(print); int x = 10; auto res = d(x); std::cout << res.count() * 1000 << "ms" << std::endl; } int main() { test1(); test2(); }