上下文管理(為加法函數計時)
- 為加法函數計時
- 使用裝飾器顯示該函數的執行時長
- 使用上下文管理顯示該函數的執行時長
裝飾器實現
import time
import datetime
from functools import wraps
def logger(fn):
@wraps(fn) # wraps(fn)(wrapper)
def wrapper(*args, **kw):
start = datetime.datetime.now()
ret = fn(*args, **kw)
delta = (datetime.datetime.now() - start).total_seconds()
print(delta)
return ret
return wrapper
@logger
def add(x, y):
time.sleep(2)
return x + y
上下文實現
- 最簡單實現
- 增加__call__用法
import time import datetime class TimeIt: def __init__(self, fn): self.fn = fn def __enter__(self): self.start = datetime.datetime.now() return self def __exit__(self, exc_type, exc_val, exc_tb): self.delta = (datetime.datetime.now() - self.start).total_seconds() print(self.delta) def __call__(self, *args, **kwargs): ret = self.fn(*args, **kwargs) return ret def add(x, y): """ This is add""" time.sleep(2) return x + y with TimeIt(add) as foo: print(foo(3,4))
- 需要增加初始化方法,為下面__call__使用
- __enter__方法返回self是為了,有了__call__方法以后,可以這樣使用with TimeIt(add) as foo: foo(3,4)
- 因為有了__call__后,實例變成可調用,而foo就是實例化后的實例
- TimeIt(add)是將add函數名作為形參傳進去
- 改成裝飾器
import time import datetime class TimeIt: def __init__(self, fn): self.fn = fn def __enter__(self): self.start = datetime.datetime.now() return self def __exit__(self, exc_type, exc_val, exc_tb): self.delta = (datetime.datetime.now() - self.start).total_seconds() print(self.delta) def __call__(self, *args, **kwargs): print('call') ret = self.fn(*args, **kwargs) return ret @TimeIt # add = TimeIt(add) def add(x, y): """ This is add""" time.sleep(2) return x + y # 上下文用法 # with TimeIt(add) as foo: # print(foo(3,4)) print(add(5,6)) print(add.__doc__) print(add.__dict__) print(add.__name__)
- 裝飾器用法很簡單,直接在add函數上加個@TimeIt
- 這是由于@TimeIt等價于add = TimeIt(add),把add函數名作為實參傳入到TimeIt類中
- 就相當于為add函數加了一個類封裝的功能或屬性
- 而這個時候,我們發現,用裝飾器實現后,直接走的是__call__方法中的語句塊,而上下文沒有執行(因為沒有用with..as語句)
- 也就是說,要么用with..as語句,要么用裝飾器方法,這是兩個方法
- (用with..as語句執行裝飾器方法,這樣比較繁瑣,重復計算,因題而異)
- 但是,如何解決文檔字符串的問題,怎么把add函數的配置信息也弄過來(看__doc__和__dict__就可以知道)
- 裝飾器用法很簡單,直接在add函數上加個@TimeIt
- 解決文檔字符串問題
- 方法1
- 把函數對象的文檔字符串賦給類
class TimeIt: def __init__(self, fn): self.fn = fn self.__doc__ = self.fn.__doc__ self.__name__ = self.fn.__name__ self.__dict__ = self.fn.__dict__
-
- 方法2
- 使用functools.wraps函數
最終完整版
import time
import datetime
from functools import wraps
class TimeIt:
"""This is Class"""
def __init__(self, fn):
self.fn = fn
# 把函數對象的文檔字符串賦給類
# self.__doc__ = self.fn.__doc__
# self.__name__ = self.fn.__name__
# @wraps = wraps(fn)(wrapper)
wraps(fn)(self) # wraps用法
# def __enter__(self):
# self.start = datetime.datetime.now()
# return self
# def __exit__(self, exc_type, exc_val, exc_tb):
# self.delta = (datetime.datetime.now() - self.start).total_seconds()
# print(self.delta)
def __call__(self, *args, **kwargs):
print('call')
self.start = datetime.datetime.now()
ret = self.fn(*args, **kwargs)
delta = (datetime.datetime.now() - self.start).total_seconds()
print(delta)
return ret
@TimeIt # add = TimeIt(add)
def add(x, y):
""" This is add"""
time.sleep(2)
return x + y
# 上下文用法
# with TimeIt(add) as foo:
# print(foo(3,4))
print(add(5,6))
print(add.__doc__)
print(add.__dict__)
print(add.__name__)
print(type(add))
- 第15行的用法wraps(fn)(self)是根據這個@wraps = wraps(fn)(wrapper)來的
- @wraps是帶參裝飾器,fn就是帶參,wrapper是傳入的實參
- 簡單來說,@wraps = wraps(fn)(wrapper)就是把fn的配置信息賦值給wrapper
- 所以wraps(fn)(self)就是把fn的配置信息賦值給實例(self就是實例化后的實例)
本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/88584