1.with open() as file
Python内置了读写文件的函数,用法和C是兼容的。在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。
使用python内置的open()函数,open('路径+文件名', '打开模式') ,第二个参数决定了打开文件的模式:只读r,写入w,追加a等。所有可取值见如下的完全列表。这个参数是非强制的,默认为r。
模式描述
r
以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb
以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
r+
打开一个文件用于读写。文件指针将会放在文件的开头。
rb+
以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w
打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
wb
以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
w+
打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
wb+
以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a
打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab
以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+
打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+
以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
如果文件打开成功,调用read()方法可以一次读取文件的全部内容,并把内容读到内存,用一个str对象表示;
文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的,所以最后要调用close()方法关闭文件;
由于文件读写时都可能产生IOError,为了保证无论是否出错都能正确地关闭文件,可以用 try...finally 来实现:
try:
f= open('/path/to/file', 'r')print(f.read())finally:iff:
f.close()
View Co
虽然这段代码运行良好,但是太冗长了。这时候就是with一展身手的时候了。除了有更优雅的语法,with还可以很好的处理上下文环境产生的异常:
with open('/path/to/file', 'r') as f:print(f.read())
with语句
♦ 调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回要根据需要决定怎么调用。如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便:
for line in f.readlines():
print(line)
♦ 写文件同样:
with open('file', 'w') as f:
f.write('Hello, world!')
当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了。
2.理解with语句
—with语句是什么?
有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。
—with如何工作?
这看起来充满魔法,但不仅仅是魔法,Python对with的处理还很聪明。基本思想是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法。
紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。
下面例子可以具体说明with如何工作:
#!/usr/bin/env python#with_example01.py
classSample:def __enter__(self):print "In __enter__()"
return "Foo"
def __exit__(self, type, value, trace):print "In __exit__()"
defget_sample():returnSample()
with get_sample() as sample:print "sample:", sample
View Code
运行代码,输出如下:
In __enter__()
sample: Foo
In __exit__()
正如我们所看到的,
1. __enter__()方法被执行
2. __enter__()方法返回的值 - 这个例子中是"Foo",赋值给变量'sample'
3. 执行代码块,打印变量"sample"的值为 "Foo"
4. __exit__()方法被调用
with真正强大之处是它可以处理异常。可能你已经注意到Sample类的__exit__方法有三个参数- val, type 和 trace。 这些参数在异常处理中相当有用。我们来改一下代码,看看具体如何工作的。
#!/usr/bin/env python#with_example02.py
classSample:def __enter__(self):returnselfdef __exit__(self, type, value, trace):print "type:", typeprint "value:", valueprint "trace:", tracedefdo_something(self):
bar= 1/0return bar + 10with Sample() as sample:
sample.do_something()
View Code
这个例子中,with后面的get_sample()变成了Sample()。这没有任何关系,只要紧跟with后面的语句所返回的对象有__enter__()和__exit__()方法即可。此例中,Sample()的__enter__()方法返回新创建的Sample对象,并赋值给变量sample。
代码执行后:
1 bash-3.2$ ./with_example02.py2 type:
3 value: integer division ormodulo by zero4 trace:
5 Traceback (most recent call last):6 File "./with_example02.py", line 19, in
7 sample.do_something()8 File "./with_example02.py", line 15, indo_something9 bar = 1/010 ZeroDivisionError: integer division or modulo by zero
View Code
实际上,在with后面的代码块抛出任何异常时,__exit__()方法被执行。正如例子所示,异常抛出时,与之关联的type,value和stack trace传给__exit__()方法,因此抛出的ZeroDivisionError异常被打印出来了。开发库时,清理资源,关闭文件等等操作,都可以放在__exit__方法当中。
Python的with语句是提供一个有效的机制,让代码更简练,同时在异常产生时,清理工作更简单。异常处理相关知识需要补充学习。
------------恢复内容结束------------