在你打开一个文件的时候,你也许会这样写:
myfile = open('data.txt')
data = myfile.read()
print(data)
这段代码有一个问题就是读取文件可能出错,并且没有关闭文件,那么我们修改代码如下:
myfile = open('data.txt')
try:
data = myfile.read()
print(data)
finally:
myfile.close()
这段代码可以正常运行(其实如果文件不存在也会抛错,我们关注点不在此),有的时候你也可能忘记关闭文件,或者每次都要写try finally,比较麻烦。那么,就是使用with的时候了。
with open('data.txt') as myfile:
data = myfile.read()
print(data)
使用with就不用每次都写try finally了,而且文件也会正常关闭,那么文件为什么会关闭呢,我们看下面的例子:
class Myfile:
def __enter__(self):
print('enter execute')
return 'object returned by enter'
def __exit__(self, type, value, trace):
print('exit execute')
def myopen():
return Myfile()
with myopen() as myfile:
print("result: " + myfile)
这段代码输出结果如下:
enter execute
result: object returned by enter
exit execute
我们看到with statment 首先执行了myfile的enter方法,并且将enter返回值赋值给了as后边的变量myfile,最后with语句块执行完成后调用了myfile的exit方法。
我们再看另外一个例子:
class Myfile:
def __enter__(self):
print('enter execute')
return 'object returned by enter'
def __exit__(self, type, value, trace):
print('exit execute')
print("value:", value)
def myopen():
return Myfile()
with myopen() as myfile:
print("result: " + myfile)
print(1/0)
输入结果如下:
enter execute
result: object returned by enter
exit execute
('value:', ZeroDivisionError('integer division or modulo by zero',))
Traceback (most recent call last):
File "D:\06workspace\eclipse\python\demo\with.py", line 24, in <module>
print(1/0)
ZeroDivisionError: integer division or modulo by zero
可以看到在with语句块中,抛出一个异常,我们同时也看到打印了exit execute,也就是说Myfile的exit方法执行了,并且在exit里获取到了异常信息。也就是说不管什么情况
with语句返回对象的exit方法都会被执行。
其实,我们可以看python里fileobject的定义:
static PyMethodDef file_methods[] = {
{"readline", (PyCFunction)file_readline, METH_VARARGS, readline_doc},
{"read", (PyCFunction)file_read, METH_VARARGS, read_doc},
{"write", (PyCFunction)file_write, METH_VARARGS, write_doc},
{"fileno", (PyCFunction)file_fileno, METH_NOARGS, fileno_doc},
{"seek", (PyCFunction)file_seek, METH_VARARGS, seek_doc},
#ifdef HAVE_FTRUNCATE
{"truncate", (PyCFunction)file_truncate, METH_VARARGS, truncate_doc},
#endif
{"tell", (PyCFunction)file_tell, METH_NOARGS, tell_doc},
{"readinto", (PyCFunction)file_readinto, METH_VARARGS, readinto_doc},
{"readlines", (PyCFunction)file_readlines, METH_VARARGS, readlines_doc},
{"xreadlines",(PyCFunction)file_xreadlines, METH_NOARGS, xreadlines_doc},
{"writelines",(PyCFunction)file_writelines, METH_O, writelines_doc},
{"flush", (PyCFunction)file_flush, METH_NOARGS, flush_doc},
{"close", (PyCFunction)file_close, METH_NOARGS, close_doc},
{"isatty", (PyCFunction)file_isatty, METH_NOARGS, isatty_doc},
{"__enter__", (PyCFunction)file_self, METH_NOARGS, enter_doc},
{"__exit__", (PyCFunction)file_exit, METH_VARARGS, exit_doc},
{NULL, NULL} /* sentinel */
};
file对象定义了enter 和exit方法,也就是读取文件之后,我们可以在exit里做点什么,看下面:
static PyObject *
file_exit(PyObject *f, PyObject *args)
{
PyObject *ret = PyObject_CallMethod(f, "close", NULL);
if (!ret)
/* If error occurred, pass through */
return NULL;
Py_DECREF(ret);
/* We cannot return the result of close since a true
* value will be interpreted as "yes, swallow the
* exception if one was raised inside the with block". */
Py_RETURN_NONE;
}
在文件对象的exit方法里进行了关闭,所以我们在使用with打开一个文件之后,不需要关心文件没有关闭的情况了,因为文件对象定义了enter和exit方法。我们在开发的时候,清理资源,关闭文件等等操作,都可以放在__exit__方法当中。