简单理解with,上下问管理器
背景
对于系统资源如文件,数据库,socket而言,应用程序打开这些资源完成业务逻辑之后,必须关闭资源,不然资源会浪费占用空间,极端时系统会报错例如‘too many open files’
打开文件代码例子
def w1():
f = open('1.txt', 'w')
f.write('python')
f.close()
这样写有个问题,如果在调用write的过程中,出现异常导致close无法正常被调用,该文件描述符资源就不会被释放。
进阶版
def w2():
f = open('1.txt', 'w')
try:
f.write('python')
except IOError:
print('error')
finally:
f.close()
程序无论发生异常否,都肯定会执行到finally,文件每次都被被关闭。
推荐写法(with)
with open('1.txt', 'w') as f:
f.write('python')
一种简单优雅的关键字with。open方法的返回值赋值给变量f,当离开with代码块的时候,系统会自动调用f.close()方法,with的作用和使用try/finally 语句是一样的。 那么with的原理是什么?那就不得不说python中的一个概念,就是上下文管理器(Context Manager)
上下文管理器
任何实现__enter__()和__exit__()方法的对象都可以称之为上下文管理器,上下文管理器对象可以使用with关键字
class File():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.f = open(self.filename,self.mode)
return self.f
def __exit__(self, *args):
self.f.close()
enter()方法返回资源对象,这里就是你打开的那个文件,exit()方法处理一些清除工作
with File('1.txt', 'w') as f:
f.write('python')
使用with时调用File时,发现包含__enter__和__exit__方法,就会先调用__enter__,执行完或者出现异常时调用__exit__。
总结
当我们打开文件时,最好使用with open,根据需求也可以自己封装(包含__enter__和__exit__方法)如:
class open_excel(object):
def __init__(self, path):
self.path = path
self.wb = None
def __enter__(self):
self.wb = openpyxl.load_workbook(self.path)
return self.wb
def __exit__(self, exc_type, exc_value, exc_tb):
if self.wb:
self.wb.save(self.path)
self.wb.close()
python3中提供一个contextmanager的装饰器,更近一步简化了上下文管理器。通过yield将函数分割,yield之前的语句在__enter__方法中执行,yield之后的语句在__exit__方法中执行。yield 函数返回值。
from contextlib import contextmanager
@contextmanager
def my_open(path, mode):
f = open(path, mode)
yield f
f.close()
with my_open('1.txt', 'w') as f:
f.write('python')