python IO有同步IO和异步IO,暂且先只讲同步IO,因为异步IO机理比较复杂。
一.文件读写
python中同步IO文件读写,主要涉及以下几个方法:
- open()
- read() && read(size)
- write()
- readline()
- readlines()
在谈上面方法使用前,先明确一个概念,叫 file-like object (类文件对象):
在python中把 open()
函数返回的对象,该对象拥有 read()
方法,则称之为file-like object.
读文件
python中使用open方法打开一个文件:
>>> f = open('/Users/james/test.txt', 'r')
# 第1个参数表示文件路径
# 第2个参数表示打开方式(mode),主要有以下几种形式:
- 'r': 表示 read
- 'rb': 表示以二进制的形式读,read in bytes
- 'w': 表示 write
- 'wb': 表示写入二进制编码, write in bytes
- 'a': 表示在文件后面追加, append
# 其余还有几种,暂且不谈
如果上面路径中的文件不存在,则python会抛出 IOError 。如果上面的文件成功的读取到,下面可以使用 read()
方法读取其中类容:
>>> f.read()
'Hello, World'
# 千万别忘记使用 'close()' 关闭文件,文件必须关闭
>>> f.close()
由于文件读写可能出错,如果出错,则后面的 close()方法不会被调用,一般使用 'try...finally' 使 'close()' 执行:
try:
f = open('some.txt', 'r')
print(f.read())
finally:
if f:
f.close()
这样写显得很繁琐,python为我们提供了 with
关键词,用于自动调用close()方法:
with open('some.txt', 'r') as f:
print(f.read())
值得注意的是,open的路径,如果使用系统路径,则应使用 r
来表示raw字符串,不对路径进行转义
# 如果不在路径前添加 'r' 表示raw,则会报错
>>> f = open(r'I:\front-end\python\some.txt', 'r')
另外,调用read()会一次性读取文件的全部类容,如果内容很大,会出现内存溢出的情况, 我们需要重复调用 read(size)
方法,每次最多读取size这么多内容
readline()
可以很次读取一行的内容, readlines()
一次性读取所有内容,并且按行返回list
二进制文件
下面介绍使用 rb
模式,读取二进制文件,比如图片和视频
>>> f = open('some.jpg', 'rb')
>>> f.read()
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节
>>> f.close()
字符编码
open()方法还可以添加编码格式,默认是utf-8, 使用默认参数 encoding
来表示, 同时使用 errors
参数来表示,如果以该编码打开出错如何处理
>>> f = open('some-gbk.txt', encoding='gbk')
>>> f.read()
'中文测试'
编码错误会抛出 'UnicodeDecodeError' 错误
# errors='ignore' 表示忽略该错误
>>> f = open('some-gbk.txt', encoding='gbk', errors='ignore')
写文件
写文件就是传入open方法中的mode不一样,写可以使用 w
或者 wb
with open('some.txt', 'w') as f:
f.write('hello, python')
如果该文件不存在,则自动先创建再写
二.StringIO && BytesIO
很多时候,读取的不是文件,而是内存中内容。
StringIO
StringIO表示从内存中读取或者写入str(字符串),首先先引入该模块:
写入:
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world!')
6
>>> print(f.getvalue())
hello world!
使用 getvalue() 方法获取写入后的字符串
另外,可以使用 readline() 来读取该字符串,因为写入字符串之后 stream position 位于最后,直接读会得到空字符串:
>>> f.readline()
''
我们需要将游标指向想要读取的位置, 使用 seek(position)
方法来调整游标位置
>>> f.seek(0)
>>> f.readline()
hello world!
读取:
先在内存中初始化字符串再读取:
>>> from io import StringIO
>>> f = StringIO('Hello\nHi\nGreat')
>>> while True:
... s = f.readline()
... if s == '':
... break
... print(s.strip())
Hello
Hi
Great
BytesIO
这个是操作内存中的二进制数据,和StringIO基本类似:
写入:
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('你好'.encode('utf-8')) # 经过utf-8编码的bytes
6
>>> print(f.getvalue())
b'\xe5\x89\xb2\xe8\xae\xa9'
读取:
>>> from io import BytesIO
> >>> f = BytesIO(b'\xe5\x89\xb2\xe8\xae\xa9')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'
三.os 模块
python提供os模块用于操作目录和文件,函数一部分放在 os 中, 一部分放在 os.path 中
>>> import os
# 查看os的名称
# linux, unix, mac os x中为 ‘posix’
>>> os.name
'nt'
# 查看环境变量
>>> os.environ
# 一长串
# 获取摸个变量的值
>>> os.environ.get('PATH')
# 查看当前目录的绝对路径
>>> os.path.abspath('.')
'I:\\front-end\\1学习疑问\\python\\基础'
# 在某个目录下创建一个新的目录
# 先把新目录的完整路径表示出来
>>> os.path.join(r'I:\front-end\1学习疑问\python\基础', 'newdir')
'I:\\front-end\\1学习疑问\\python\\基础\\newdir'
# 在创建该目录
>>> os.mkdir('I:\\front-end\\1学习疑问\\python\\基础\\newdir')
# 删除一个目录
>>> os.rmdir('I:\\front-end\\1学习疑问\\python\\基础\\newdir')
os.path.split()
则可以将路径拆分为2部分,后一部分总是最后级别的目录或文件名
>>> os.path.split('I:\\front-end\\1学习疑问\\python\\基础')
('I:\\front-end\\1学习疑问\\python', '基础')
os.path.splitext()
用于获取文件的扩展
>>> os.path.splitext('/path/to/file.txt')
('/path/to/file', '.txt')
列出当前目录下的所有文件目录(os.listdir()
, os.path.isdir()
):
>>> [x for x in os.listdir('.') is os.path.isdir(x)]
['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Applications', 'Desktop', ...]
列出所有的 '.py' 文件(os.listdir()
, os.path.isfile()
):
>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1] == '.py']
['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']
四.序列化
把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫 pickling ,在其他语言中也被称之为serialization。
序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即 unpickling。
json 模块
将python对象变为JSON: json.dumps() (相当于js中 JSON.stringify())
>>> import json
>>> d = dict(name='james', age=20, score=10)
>>> print(json.dumps(d))
{"name": "james", "age": 20, "score": 10}
另外 dump()
可以直接把JSON写入一个 file-like object
json.loads()
相当于js中的 JSON.parse(),将字符串反序列化:
>>> json_str = '{"name": "james", "age": 20, "score": 10}'
>>> json.loads(json_str)
{'name': 'james', 'age': 20, 'score': 10}
另外 load()
可以从 file-like object 中读取字符串并反序列化
将类class序列化和反序列化
在 dumps() 方法中有一个 default
参数,可以提供一个转换函数,将类序列化
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score =score
>>> s = Student('james', 20, 100)
# 定义转换函数
def student2dict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
# 序列化
>>> print(json.dumps(s, default=student2dict))
{"name": "james", "age": 20, "score": 100}
这样写如果遇到Teacher类的实例,则无法进行序列化,可以使用class内部属性 __dict__
,将任意class实例变为dict
# default后面添加lambda表达式
>>> print(json.dumps(s, default=lambda obj: obj.__dict__))
{"name": "james", "age": 20, "score": 100}
反序列化使用 loads() 的一个参数 object_hook
def dict2student(d):
return Student(d['name'], d['age'], d['score'])
>>> json_str = '{"name": "james", "age": 20, "score": 100}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x00185530>
总结
本章主要将的是python同步IO操作,os模块对路径和文件目录的操作,以及序列化和反序列化,还有class的序列化和反序列化:
- 同步IO主要涉及:
open()
,read()
,write()
,seek()
等方法,根据mode的不同进行不同的操作 -
file-like object
的概念,调用open()方法返回的对象拥有read()方法的对象,称之为类文件对象 -
with
语句,在IO操作中,自动调用close()
方法 -
StringIO
和BytesIO
对内存中的字符串和二进制进行读写操作 - os模块,主要对目录和文件进行操作
-
dumps()
和loads()
对对象进行序列化和反序列化 - 使用内部
__dict__
属性,将对象变为字典,然后进行序列化;object_hook
则将序列化的json字符串反序列化
2017年3月10日 17:29:00