上下文管理 & contextlib.contextmanager & functool.total_ordering
文件I/O操作可以对文件对象使用上下文管理,使用with as语法
with open('file') as f:
pass
上下文管理对象
当一个对象同时实现了__enter__()和__exit__()方法,就属于上下文管理对象
- __enter__进入与此对象相关的上下文,如果存在该方法,with语法会把该方法的返回值作为绑定到as 字句中指定的变量上
- __exit__ 退出与此对象相关的上下文
class Owl:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
def __exit__(self, exc_type, exc_val, exc_tb):
print(self.__class__.__name__)
with Owl() as f:
pass
class Owl:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
def __exit__(self, exc_type, exc_val, exc_tb):
print(self.__class__.__name__)
o=Owl()
with o as f: # f 为__enter__()返回值
print(f==o)
print(f is o)
print('o => {}'.format(o))
print('f => {}'.format(f))
open内建函数返回的object实现了__enter__()和__exit__()方法
实例化对象的时候,并不会调用__enter__(),进入with语句块调用__enter__方法,然后执行语句体,最后离开语句块with的时候,调用__exit__方法
上下文管理的安全性
class Owl:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(self.__class__.__name__)
o=Owl()
import sys
with o as f: # f 为__enter__()返回值
sys.exit(256)
raise Exception('raise Exception')
print(f==o)
print(f is o)
print('o => {}'.format(o))
print('f => {}'.format(f))
sys.exit()和raise Exception()都不会阻止__exit__的执行
__enter__方法返回值就是上下文中使用的对象,with语法会把它的返回值赋给as字句变量
__enter__ 和 __exit__参数
__enter__没有其他参数
__exit__有三个参数exc_type,exc_val,exc_tb
三个参数都与异常有关
如果该上下文退出时没有异常,则这三个参数都为None
如果有异常,则参数意义如下:
- exc_type 异常类型 <class 'Exception'>
- exc_val 异常值,类型为<class 'Exception'>
- exc_tb 异常的追踪信息 <traceback object at 0x000001AE8E25B600>
class Owl:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(self.__class__.__name__+'*'*40)
print(exc_type)
print(exc_val,type(exc_val))
print(exc_tb)
return 0
o=Owl()
import sys
with o as f: # f 为__enter__()返回值
# sys.exit(256)
raise Exception('raise Exception')
print(f==o)
print(f is o)
print('o => {}'.format(o))
print('f => {}'.format(f))
print('outer scope')
时间装饰器函数
import time,datetime
from functools import wraps
def Cursitor(fn):
@wraps(fn)
def wrapper(*args,**kwargs):
germ=datetime.datetime.now()
ret=fn(*args,**kwargs)
delta=(datetime.datetime.now()-germ).total_seconds()
print('{} consume {}'.format(fn.__name__,delta))
return ret
return wrapper
@Cursitor
def add(x,y):
time.sleep(2)
return x+y
print(add(8,7))
上下文实现
import time,datetime
from functools import wraps
def cursitor(fn):
@wraps(fn)
def wrapper(*args,**kwargs):
germ=datetime.datetime.now()
ret=fn(*args,**kwargs)
delta=(datetime.datetime.now()-germ).total_seconds()
print('{} consume {}'.format(fn.__name__,delta))
return ret
return wrapper
@cursitor
def add(x,y):
time.sleep(2)
return x+y
class Cursitor:
def __init__(self,fn):
self.fn=fn
def __enter__(self):
self.germ=datetime.datetime.now()
return self.fn
# return self
def __exit__(self,exc_type,exc_val,exc_tb):
self.delta=(datetime.datetime.now()-self.germ).total_seconds()
print('{} consume {} context'.format(self.fn.__name__,self.delta))
return None
# with Cursitor(add):
# print(add(8,8))
with Cursitor(add) as f:
print(f(8,9))
import time,datetime
from functools import wraps
def cursitor(fn):
@wraps(fn) # wraps(fn)(wrapper)
def wrapper(*args,**kwargs):
germ=datetime.datetime.now()
ret=fn(*args,**kwargs)
delta=(datetime.datetime.now()-germ).total_seconds()
print('{} consume {}'.format(fn.__name__,delta))
return ret
return wrapper
@cursitor
def add(x,y):
time.sleep(2)
return x+y
class Cursitor:
def __init__(self,fn):
print('init')
self.fn=fn
# self.__doc__=fn.__doc__
# self.__name__=fn.__name__
def __enter__(self):
self.germ=datetime.datetime.now()
# return self.fn
return self
def __exit__(self,exc_type,exc_val,exc_tb):
self.delta=(datetime.datetime.now()-self.germ).total_seconds()
print('{} consume {} context'.format(self.fn.__name__,self.delta))
return None
def __call__(self,*args,**kwargs):
wraps(self.fn)(self)
germ=datetime.datetime.now()
ret=self.fn(*args,**kwargs)
delta=(datetime.datetime.now()-germ).total_seconds()
print('{} consume {} __call__'.format(self.fn.__name__,delta))
return ret
# with Cursitor(add):
# print(add(8,8))
# with Cursitor(add) as f:
# print(f(8,9))
# c=Cursitor(add)
# with c:
# print(c(7,9))
# with Cursitor(add) as f:
# print(f(5,9))
# c=Cursitor(add)
# with c as f:
# print(f(4,9))
@Cursitor
def add(x,y): # add=Cursitor(add)
'add doc'
time.sleep(2)
return x+y
print(add)
print(add(4,8))
print(add.__doc__)
print(add.__name__)
print(add.__dict__)
contextlib.contextmanager
是一个装饰器实现上下文管理,装饰一个函数,不用实现像类一样__enter__和__exit__方法
对下面的函数有要求,必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值
import contextlib
@contextlib.contextmanager
def ore():
# print('__enter__') # amount to __enter__()
# yield 4,6
# print('__exit__') # amount to __exit__()
print('__enter__')
try:
yield 4,9
finally:
print('__exit__')
with ore() as f:
raise Exception
print(f)
在yield发生处为生成器函数增加了上下文管理
import contextlib,datetime,time
@contextlib.contextmanager
def ore(x,y): # 为生成器函数增加了上下文管理
germ=datetime.datetime.now()
try:
yield x+y # yield值只能有一个,作为__enter__方法返回值
finally:
delta=(datetime.datetime.now()-germ).total_seconds()
print(delta)
with ore(8,9) as f:
# raise Exception()
time.sleep(2)
print(f)
from functools import total_ordering
@total_ordering # inefficiency
class B:
def __init__(self, p):
self.p = p
def __lt__(self, other): # choose one
return self.p < other.p
def __eq__(self,other): # required
return self.p == other.p
b=B(5)
v=B(6)
print(b==v)
print(b!=v)
print(b>=v)
print(b<=v)