上下文管理(为加法函数计时)
- 为加法函数计时
- 使用装饰器显示该函数的执行时长
- 使用上下文管理显示该函数的执行时长
装饰器实现
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.178linux.com/88584