【python】魔术方法2
上下文管理
文件IO操作可以对文件对象使用上下文管理,使用with...as语法。
with open('test', 'w') as f:
pass
我们仿照上例写一个自己的类,实现上下文管理
class Point:
pass
with Point() as p: # AttributeError: __exit__
pass
提示属性错误,没有 __exit__ ,看了需要这个属性,某些版本会显示没有 __enter__
上下文管理对象
当一个对象同时实现了 __enter__ ()和__exit__ ()方法,它就属于上下文管理的对象
__enter__ |
进入与此对象相关的上下文。如果存在该方法, with语法会把该方法的返回值作为 绑定到as子句中指定的变量上 |
__exit__ |
退出与此对象相关的上下文。 |
我们将类重新定义下,加入__enter__方法,和__exit__方法
import time
class Point:
def __init__(self):
print('init ~~~~~~~~')
time.sleep(1)
print('init over')
def __enter__(self):
print('enter ~~~~~~~~')
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit ============')
with Point() as p:
print('in with-------------')
time.sleep(2)
print('with over')
print('=======end==========')
# init ~~~~~~~~
# init over
# enter ~~~~~~~~
# in with-------------
# with over
# =======end==========
# exit ============
【小结】实例化对象的时候,并不会调用enter,进入with语句块调用__enter__ 方法,然后执行语句体,最后 离开with语句块的时候,调用__exit__ 方法。
with可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些收尾工作。 注意, with并不开启一个新的作用域。
上下文管理的安全性
看看异常对上下文的影响。
import time
class Point:
def __init__(self):
print('init ~~~~~~~~')
time.sleep(1)
print('init over')
def __enter__(self):
print('enter ~~~~~~~~')
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit ============')
with Point() as p:
print('in with-------------')
raise Exception('error')
time.sleep(2)
print('with over')
print('=======end==========')
可以看出在抛出异常的情况下, with的__exit__照样执行, 上下文管理是安全的。
with语句
# 3.py文件中写入下面代码
class Point:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit')
f = open('3.py')
with f as p:
print(f)
print(p)
print(f is p) # True
print(f == p) # True
p = f = None
p = Point()
with p as f:
print('in with-------------')
print(p == f) # False
print('with over')
print('=======end==========')
问题在于 __enter__ 方法上,它将自己的返回值赋给f。修改上例
class Point:
def __init__(self):
print('init ~~~~~~~~')
def __enter__(self):
print('enter ~~~~~~~~')
return self # 增加返回值
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit ============')
p = Point()
with p as f:
print('in with-------------')
print(p == f) # True
print('with over')
print('=======end==========')
with语法,会调用with后的对象的__enter__方法,如果有as,则将该方法的返回值赋值给as子句的变量。上例,可以等价为 f = p.\__enter__()
上下文应用场景
增强功能: 在代码执行的前后增加代码,以增强其功能。类似装饰器的功能。
资源管理: 打开了资源需要关闭,例如文件对象、网络连接、数据库连接等
权限验证: 在执行代码之前,做权限的验证,在 __enter__ 中处理
上下文应用
如何用支持上下文的类来对add函数计时
import time
import datetime
def add(x, y):
time.sleep(2)
return x + y
class Timeit:
def __init__(self, fn):
self.__fn = fn
def __enter__(self):
self.start = datetime.datetime.now()
print('开始计时')
return self
def __call__(self, *args, **kwargs):
return self.__fn(*args, **kwargs)
def __exit__(self, exc_type, exc_val, exc_tb):
delta = (datetime.datetime.now() - self.start).total_seconds()
print('{} took {}s.' .format(self.__fn.__name__, delta))
with Timeit(add) as t:
print(add(4, 5))
print(t(5, 6))
# 开始计时
# # 9
# # 11
# # add took 4.019243s.
- 点赞
- 收藏
- 关注作者
评论(0)