上下文管理(为加法函数计时)
为加法函数计时
使用装饰器显示该函数的执行时长
使用上下文管理显示该函数的执行时长
装饰器实现
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__就可以知道)
解决文档字符串问题
方法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