[Python-Liao-XX…]系列,系列根据廖雪峰的python3初级教程学习整理。
54. IO编程
IO在计算机中指Input/Output即输入输出,涉及到数据交换的地方,通常是磁盘或网络,就需要IO接口。
IO编程中,Stream流很重要,Input Stream就是数据从外面(磁盘,网络)流进内存,Output Stream就是数据从内存流到外面去。
由于CPU和内存的速度远高于外设速度,所以IO编程中就存在两种模式,同步IO和异步IO。
- 同步IO:IO如将100M数据写入磁盘完毕后,再接下去执行其他部分。
- 异步IO:将100M数据写入磁盘的命令传达后,立即执行其他部分
同步和异步的区别在于是否等待IO执行的结果,使用异步IO来编写程序,性能会远高于同步IO,但是异步IO的缺点是编程模型复杂。 异步IO编程中,有如下两种方式确认IO是否完成:
- 回调模式:磁盘写入完毕后,执行IO的程序过来通知你(当前程序)
- 轮询模式:执行IO的程序发完毕的消息,当前程序不停的确认该消息
总之,异步IO的复杂度远远高于同步IO。
55. 文件读写
Python 内置的 open()函数, 传入文件名和标示符:f = open("c:\\test\\1.log","r")
调用 read()方法可以一次读取文件的全部 内容,Python 把内容读到内存,用一个 str 对象表示:
f.read()
输出如下:
'just a test1\njust a test2\njust a test3'
close()方法关闭文件。文件使用完毕后必须关闭,因 为文件对象会占用操作系统的资源, 并且操作系统同一时间能打开的文 件数量也是有限的: f.close
但是上面存在一个问题,即如果IO出现error,后面的f.close不会被执行,所以需要添加异常处理:
try:
f = open('c:\\test\\1.log', 'r')
print(f.read())
finally:
if f:
f.close()
但是每次都按照上面的方式写太繁琐了,python中可以简化为:
with open('c:\\test\\1.log', 'r') as f:
print(f.read())
- 如下是返回第一行
with open('c:\\test\\1.log', 'r') as f:
print(f.readline())
- 如下返回复数行的一个list:
with open('c:\\test\\1.log', 'r') as f:
print(f.readlines())
输出如下:
['just a test1\n', 'just a test2\n', 'just a test3']
- 如下是分隔之后返回:
with open('c:\\test\\1.log', 'r') as f:
for l in f.readlines():
print(l.strip())
输出如下
just a test1
just a test2
just a test3
1. file-like object
在前面讲过类似的file-like object,传统的面向对象语言中,要实现多态功能,就需要从特定的类下面继承,但是python是动态语言,只需要这个类实现了read方法就行,所以,内存流网络流或自定义流等都是file-like object。
另外,StringIO就是在内存中创建的file-like Object,常用作临时缓冲。
2. 二进制文件
对于非文件形式文件,如图片,使用rb
形式读取,最后输出为一连串的二进制码。
f = open('C:\\test\\1.png', 'rb')
f.read()
3. 字符编码
非UTF-8编码的文本文件时:
f = open('C:\\test\\1.txt', 'r', encoding='utf-8')
f.read()
输出如下(文本文件中只写入了连接
二字):
'\ufeff连接'
遇到有些编码不规范的文件, 你可能会遇到 UnicodeDecodeError, 因为在 文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open()函 数还接收一个 errors 参数,表示如果遇到编码错误后如何处理。最简单 的方式是直接忽略:
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
4. 写文件
f = open('C:\\test\\2.txt', 'w')
f.write('Hello, world!')
f.close()
当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是 放到内存缓存起来,空闲的时候再慢慢写入 只有调用 close()方法时, 操作系统才保证把没有写入的数据全部写入磁盘。
忘记调用 close()的 后果是数据可能只写了一部分到磁盘, 剩下的丢失了。 所以, 还是用 with 语句来得保险:
with open('C:\\Users\\61041150\\Pictures\\3.txt', 'w') as f:
f.write('Hello, world!')
with 语句操作文件 IO 是个好习惯。
56. StringIO和BytesIO
1. StringIO
StringIO 顾名思义就是在内存中读写 str。 StringIO 顾名思义就是在内存中读写 str。
from io import StringIO
f = StringIO()
f.write('hello')
f.write(' ')
f.write('world!')
输出为6
,因为最后写入的world!
是6个字符。
print(f.getvalue())
,时会输出hello world!
,即上面写入到IO中的字符串。
要读取StringIO,可以用一个 str 初始化 StringIO,然后像读文件一样读取:
f = StringIO('Hello!\nHi!\nGoodbye!')
while True:
s = f.readline()
if s == '':
break
print(s.strip())
最后输出:
Hello!
Hi!
Goodbye!
2. BytesIO
BytesIO 实现了在内存中读写 bytes。
from io import BytesIO
f = BytesIO()
f.write('中文'.encode('utf-8'))
print(f.getvalue())
输出b'\xe4\xb8\xad\xe6\x96\x87'
StringIO 和 BytesIO 是在内存中操作 str 和 bytes 的方法,使得和读写文 件具有一致的接口。
57. 操作文件和目录
代码 | 意义 | 其他 |
---|---|---|
import os | 导入os包 | |
os.name | 操作系统类型 | |
os.uname() | 此函数在windows下不可用 | 系统详细信息 |
os.environ | 环境变量 | |
os.environ.get(‘PATH’) | 要获取某个环境变量的值,可以调用 os.environ.get(‘key’) | |
os.environ.get(“lijun-key”,”default-value”) | 找不到对应key时,用default-value返回 | |
os.path.abspath(“.”) | 获取当前路径的绝对路径 |
下面的代码,新建一个新的文件夹,删除一个文件夹。 在生成新的文件路径的时候,不要自己进行字符串组合,使用path.join能够排除操作系统的差异,比如windows和linux的分隔符就是不同的。
# 组合一个新的路径名
newpath = os.path.join("C:\\test","directoryTest")
os.mkdir(newpath) # 新建一个目录
os.rmdir(newpath) # 删除一个目录
split和splittext,用于拆分路径名,生成一个tuple即元祖,效果如下:
newpathfile = os.path.join(newpath,"testtest.txt")
os.path.split(newpathfile)
输出 ('C:\\test\\directoryTest', 'testtest.txt')
# 最后部分是文件的扩展名
os.path.splitext(newpathfile)
输出 ('C:\\test\\directoryTest\\testtest', '.txt')
- 重命名文件和删除文件:
# 重命名文件
os.rename("1227.txt","1227-modified.txt")
# 删除指定文件
os.remove("1.txt")
shutil模块提供了很多如copyfile的函数,是os的很好补充,需要是查阅。 列举当前路径下的文件夹名:
[x for x in os.listdir('.') if os.path.isdir(x)]
列举当前路径下的所有py文件:
[x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
58. 序列化
d = dict(name='Bob', age=20, score=88)
,我们把变量从内存中变成可存储或传输的过程称之为序列化、序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到 别的机器上。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化, 即 unpickling。
import pickle
d = dict(name='Bob', age=20, score=88)
pickle.dumps(d)
输出如下:
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x03\x00\x00\x00Bobq\x02X\x05\x00\x00\x00scoreq\x03KXX\x03\x00\x00\x00ageq\x04K\x14u.'
- 将上面的对象d序列化到文件:
f = open('dump.txt', 'wb')
pickle.dump(d, f)
f.close()
- 从序列化文件中还原对象
f = open('dump.txt', 'rb')
d = pickle.load(f)
f.close()
d
输出 {'age': 20, 'name': 'Bob', 'score': 88}
1. jason文件序列化
import json
d = dict(name='Bob', age=20, score=88)
json.dumps(d)
输出:'{"name": "Bob", "age": 20, "score": 88}'
json_str = '{"age": 20, "score": 88, "name": "Bob"}'
json.loads(json_str)
输出:{'age': 20, 'name': 'Bob', 'score': 88}
2. JSON进阶
将一个class对象序列化为JSON对象. python特定的序列化模块是pickle,但是如果想把序列化实现得更web通用化,最好使用json进行序列化。
import json
class Student(object):
def __init__(self,name,age,score):
self.name = name
self.age = age
self.score = score
def student2dict(std):
return {
"name":std.name,
"age":std.age,
"score":std.score
}
def dict2student(d):
return Student(d['name'],d['age'],d['score'])
s = Student("Bol",20,88)
print(json.dumps(s, default=lambda obj: obj.__dict__))
输出{"name": "Bol", "age": 20, "score": 88}