微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io
过去的代码都是未经测试的代码。
目录

无论是哪种编程语言,IO 操作都是非常重要的部分。I 即Input (输入),O 即Output (输出)。
IO 操作一般分为以下两种:
-
磁盘IO: 即在磁盘上
读写 文件。读文件 是指将文件内容从磁盘读入内存,写文件 是指将内存中的内容写到磁盘。
-
网络IO: 即文件在网络上传输。网络传输一般会有两种角色,分别是
服务端 (如HTTP Server )和客户端 (如浏览器 )。
本节我们主要介绍磁盘IO ,即文件读写 。
1,open 函数介绍
要想读写文件,首先要打开 一个文件。
Python 中的内建函数open 用来打开一个文件,我们可以使用help(open) ,来查看open 函数的原型,如下:
open(file,mode='r',buffering=-1,encoding=None,errors=None,newline=None,closefd=True,opener=None)
该函数成功调用时会返回一个流stream ,用于读写文件等操作;发生错误时会抛出IOError 异常。
被打开的文件占用了系统资源,使用完后要记得close ,否则会浪费系统资源。
不管以读模式 打开文件,还是以写模式 打开文件,成功打开一个文件后,这个可操作文件的流 的内部都有一个隐含的指针 ,一般这个指针会指向文件开头 或者文件末尾 的位置,表示从文件的哪个位置读写文件。
可以看到,该函数支持8 个参数,但最重要的是前两个参数:
-
file :是指要打开的文件的路径
-
mode :是指以什么模式打开文件,要用引号 引住
mode 参数支持的模式(默认为读文本 模式,即rt )如下:
-
r :以读模式 打开文件(默认方式),指针在文件开头
-
w :以写模式 打开文件,如果件已存在,则内容会被清空(指针在文件开头);如果文件不存在,则会创建新文件
-
x :创建一个新文件,并以写模式 打开,指针在文件开头,如果文件已存在,则抛出FileExistsError 异常
-
a :以写模式 打开文件,如果文件已有内容,在写入内容时,会追加 到文件末尾(指针在文件末尾)
-
b :以二进制模式 打开文件,一般用于读写二进制文件,如图片,视频等
-
t :以文本模式 打开文件(默认方式),一般用于读写文本文件
-
+ :以读写模式 打开文件,指针在文件开头
这些模式还可以组合使用,常见的组合如下:
-
rb :以二进制模式 打开一个文件,用于只读
-
r+ :打开一个文件,用于读写
-
rb+ :以二进制模式 打开一个文件,用于读写
-
wb :以二进制模式 打开一个文件,用于写
-
w+ :打开一个文件,用于读写
-
wb+ : 以二进制模式 打开一个文件,用于读写
-
ab : 以二进制模式 打开一个文件,用于追加
-
a+ :打开一个文件用于读写 ,指针在文件末尾
-
ab+ :以二进制模式 打开一个文件,用于读写 ,指针在文件末尾
2,open 函数示例
如下代码,成功打开文件./1.txt :
f = open('./1.txt')
通过type(f) 查看open 函数的返回值的类型:
>>> type(file)
<class '_io.TextIOWrapper'>
可看到,其返回值类型为_io.TextIOWrapper 。
我们用dir(f) 来查看对象 f 支持的属性和方法:
>>> dir(file)
['_CHUNK_SIZE','__class__','__del__','__delattr__','__dict__','__dir__','__doc__','__enter__','__eq__','__exit__','__format__','__ge__','__getattribute__','__getstate__','__gt__','__hash__','__init__','__init_subclass__','__iter__','__le__','__lt__','__ne__','__new__','__next__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','_checkClosed','_checkReadable','_checkSeekable','_checkWritable','_finalizing','buffer','close','closed','detach','encoding','errors','fileno','flush','isatty','line_buffering','mode','name','newlines','read','readable','readline','readlines','seek','seekable','tell','truncate','writable','write','writelines']
可以通过help(f.方法名) 来查看每个方法的帮助手册,也可以使用help(f) 来查看该对象的所有属性和方法,及其简介。
我们来看一下常用方法的作用:
-
mode :打开文件时的模式
-
name :被打开的文件名
-
close :关闭文件流,并刷新缓冲区中的内容,之后不能再操作文件
-
closed :文件流是否已关闭
-
flush :刷新写缓冲区,只写流 与非阻塞流 不适用
-
read :读入文件内容
-
readable :是否可读
-
readline :读入一行内容
-
readlines :读入文件所有的行,直至文件末尾
-
seek :移动文件指针的位置
-
seekable :文件指针是否可被移动
-
tell :返回文件指针 当前位置
-
truncate :截断文件内容
-
writable :是否可写
-
write :向文件中写入内容
-
writelines :向文件中写入多行
3,关闭系统资源
正确的调用close() 函数是关键的。
在成功打开一个文件后,对该文件进行操作(读写)时,有可能发生异常。
比如我们打开的文件只能用来写 ,如果用来读 ,则会发生异常:
>>> f = open('1.txt','w') # 用只读模式打开文件
>>> f.readable() # 查看文件是否可读
False # 返回 False,表示不可读
>>> f.read() # 读文件,发生异常
Traceback (most recent call last):
File "<stdin>",line 1,in <module>
io.UnsupportedOperation: not readable
如果,我们将这段代码写在文件中:
#! /usr/bin/env python3
f = open('1.txt','w')
f.read()
f.close()
用python3 来执行,结果如下:
$ python3 Test.py
Traceback (most recent call last):
File "Test.py",line 4,in <module>
f.read()
io.UnsupportedOperation: not readable
可以看到,在执行到f.read() 这句代码的时候,程序异常退出,那么后边的f.close() 就没有执行到,这就导致程序执行不够完整,系统资源没有关闭。
这时,我们可以用try...finally 来处理,如下:
#! /usr/bin/env python3
f = open('1.txt','w')
try:
f.read()
except Exception as e:
print('read file err:%s' % e)
finally:
f.close()
print('file closed')
上面代码的执行结果如下:
$ python3 Test.py
read file err:not readable
file closed
我们将f.close() 这句代码放在了finally 代码块中,这样,不管遇到什么情况,f.close() 这句话总会被执行,就不会导致系统资源泄漏的问题。
4,with 语句使用
为了确保系统资源能够关闭,Python 中提供了with 语句,能够让我们更加安全方面的使用open 函数,而不用关心资源关闭的问题。
with 语句也叫上下文管理器 ,有了with 语句,我们可以这样使用open 函数:
with open('./1.txt') as f:
print(f.read())
这样的代码,不管在with 语句块内出现怎样的异常,close 函数都会被调用,而我们也不需要自己调用。
使用with 语句,就不再需要使用try...finally 语句,也使得代码更加简洁。
需要特别注意的是,这里的f 只能在with 语句块中使用,一旦离开with 语句块,f 就被关闭了。如果在with 语句块之外使用f 进行读写等操作,将出现异常。
如下代码中,f.closed 将返回True :
with open('./1.txt') as f:
pass
f.closed # True
5,with 语句原理
为什么open 函数能够使用with 语句?
实际上open 函数能够使用with 语句的原因取决于open 的返回值的类型 。我们知道,open 的返回值的类型为_io.TextIOWrapper ,而这个类中有两个方法,__enter__ 方法和__exit__ 方法。
我们再来看下with 语句的格式:
with ... as ... :
pass
with 关键字的后边是一个表达式 ,as 后边是一个变量名,表达式的计算结果会赋值给as 后边的变量。
Python 规定,只要一个类中有__enter__ 和__exit__ 方法,就可以使用with 语句。with 语句后边的表达式执行完毕后,就会执行__enter__ 方法,在退出with 语句块时,会执行__exit__ 方法。
我们自己编写一个测试类,使其能够使用with 语句:
#! /usr/bin/env python3
class TestWith:
def __init__(self):
print('执行__init__')
def __enter__(self):
print('执行__enter__')
def __exit__(self,exc_type,exc_val,exc_tb):
print('执行__exit__')
print('exc_type is %s' % exc_type)
print('exc_val is %s' % exc_val)
print('exc_tb is %s' % exc_tb)
再该类中有三个函数:
-
__init__ :构造函数,创建类的对象时调用
-
__enter__ :进入with 语句块时会调用
-
__exit__ :离开with 语句块时会调用
其中__exit__ 方法有三个参数:
-
exc_type :with 语句块中的代码发生异常时的异常类型
-
exc_val :发生异常时的异常值
-
exc_tb :发生异常时的traceback 类的对象
我们这样使用这个类:
with TestWith() as t:
print('test with')
用python3 来执行,结果如下:
$ python3 Test.py
执行__init__
执行__enter__
test with
执行__exit__
exc_type is None
exc_val is None
exc_tb is None
可以看到执行步骤是这样的:
- 生成该类的对象,执行
__init__ 方法
- 进入
with 语句块,执行__enter__ 方法
- 执行
with 语句块中的代码
- 退出
with 语句块,执行__exit__ 方法
因为with 语句块中没有发生异常,所以__exit__ 方法中的 exc_type ,exc_val ,exc_tb 三个参数均为None 。
下面再示范一个with 语句块中出现异常的代码:
with TestWith() as t:
print('test with1...')
1 / 0 # 除数为 0,抛出异常
print('test with2...')
该代码的执行结果如下:
$ python3 Test.py
执行__init__
执行__enter__
test with1...
执行__exit__
exc_type is <class 'ZeroDivisionError'>
exc_val is division by zero
exc_tb is <traceback object at 0x7fe8b7c98888>
Traceback (most recent call last):
File "Test.py",line 27,in <module>
1 / 0
ZeroDivisionError: division by zero
通过上面的执行结果可以看到,在执行1 / 0 之前,我们不用多说。在执行到1 / 0 时,出现异常,然后会执行__exit__ 方法。
在执行结果中,我们能看到 exc_type ,exc_val ,exc_tb 三个参数的值,最后抛出Traceback 异常。
with 语句中,抛出异常的语句1 / 0 之后的代码不会再执行。
(完。)
推荐阅读:
Python 简明教程 --- 19,Python 类与对象
Python 简明教程 --- 20,Python 类中的属性与方法
Python 简明教程 --- 21,Python 继承与多态
Python 简明教程 --- 22,Python 闭包与装饰器
Python 简明教程 --- 23,Python 异常处理
欢迎关注作者公众号,获取更多技术干货。
 (编辑:北几岛)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|