【Python学习】IO编程
IO在计算机中指Input/Output,也就是输入和输出。由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。IO编程中,Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。Input Stream就是数据从外面(磁盘、网络)流进内存,Output Stream就是数据从.
IO在计算机中指Input/Output
,也就是输入和输出。由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。
IO编程中,Stream
(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。Input Stream
就是数据从外面(磁盘、网络)流进内存,Output Stream
就是数据从内存流到外面去。
- 存在问题:输入和接收速度不匹配
- 同步IO:CPU等着,也就是程序暂停执行后续代码,等100M的数据在10秒后写入磁盘,再接着往下执行(好了叫我)。
- 异步IO:CPU不等待,只是告诉磁盘,慢慢写,不着急,接着干别的事去了,于是,后续代码可以立刻接着执行(好了没好了没)
同步和异步的区别就在于是否等待IO执行的结果
文件读写
读文件
要以读文件的模式打开一个文件对象,使用Python内置的open()
函数,传入文件名和标示符,标示符'r'
表示读:
f = open('D:/code/PycharmProjects/PythonStudy/venv/Include/高级特性/迭代器.py','r')
print(f.read())
f.close()
如果文件不存在,open()
函数就会抛出一个IOError
的错误,并且给出错误码和详细的信息告诉你文件不存在,文件打开成功,接下来,调用read()
方法可以一次读取文件的全部内容。最后一步是调用close()
法关闭文件,文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的。
由于文件读写时都有可能产生IOError
,一旦出错,后面的f.close()
就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally
来实现。
try:
f = open('D:/code/PycharmProjects/PythonStudy/venv/Include/高级特性/迭代器.py', 'r')
print(f.read())
finally:
if f:
f.close()
这么写太繁琐,所以,Python引入了with语句来自动帮我们调用close()方法:
# with
with open('D:/code/PycharmProjects/PythonStudy/venv/Include/高级特性/迭代器.py') as f:
print(f.read())
和前面的try ... finally
是一样的,但是代码更佳简洁,并且不必调用f.close()
方法。
调用read()
会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)
方法,每次最多读取size
个字节的内容。另外,调用readline()
可以每次读取一行内容,调用readlines()
一次读取所有内容并按行返回list
。因此,要根据需要决定怎么调用。如果文件很小,read()
一次性读取最方便;如果不能确定文件大小,反复调用read(size)
比较保险;如果是配置文件,调用readlines()
最方便:
with open('D:/code/PycharmProjects/PythonStudy/venv/Include/高级特性/迭代器.py') as f:
# readlines()
for line in f.readlines():
print(line.strip())
- file-like:在Python中统称为
file-like Object
。除了file
外,还可以是内存的字节流,网络流,自定义流等等。file-like Object
不要求从特定类继承,只要写个read()
方法就行。StringIO
就是在内存中创建的file-like Object
,常用作临时缓冲。 - 二进制文件:读取二进制文件,比如图片、视频等等,用
'rb'
模式打开文件:
f = open('D:/code/PycharmProjects/PythonStudy/venv/Include/IO编程/wallpaper_5242054.jpg','rb')
f.read()
- 字符编码:要读取非
UTF-8
编码的文本文件,需要给open()
函数传入encoding
参数,例如,读取GBK
编码的文件:
f = open('D:/code/PycharmProjects/PythonStudy/venv/Include/IO编程/gbk.txt', 'r', encoding='gbk')
f.read()
遇到有些编码不规范的文件,可能会遇到UnicodeDecodeError
,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open()
函数还接收一个errors
参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:
f = open('D:/code/PycharmProjects/PythonStudy/venv/Include/IO编程/gbk.txt', 'r', encoding='gbk',errors='ignore')
写文件
写文件和读文件是一样的,唯一区别是调用open()
函数时,传入标识符'w'
者'wb'
表示写文本文件或写二进制文件:
f = open('D:/code/PycharmProjects/PythonStudy/venv/Include/IO编程/io.txt', 'w')
f.write('hello world!!!')
f.close()
可以反复调用write()
来写入文件,但是务必要调用f.close()
来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()
方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()
的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用with
语句来得保险:
with open('D:/code/PycharmProjects/PythonStudy/venv/Include/IO编程/io.txt', 'w') as f:
f.write('hi python!!!')
要写入特定编码的文本文件,请给open()
函数传入encoding
参数,将字符串自动转换成指定编码。'w’模式写入文件时,如果文件已存在,会直接覆盖,传入'a'
以追加(append)模式写入。
(二)StringIO和BytesIO
StringIO
StringIO
:内存中读写str
from io import StringIO
# 内存写入
f = StringIO()
print(f.write('hello'))
print(f.write(' '))
print(f.write('world!'))
print(f.getvalue()) # getvalue()方法用于获得写入后的str
读取StringIO
,可以用一个str
初始化StringIO
,然后,像读文件一样读取:
# 内存读取
f = StringIO('Hello Python!')
while True:
s = f.readline()
if s == '':
break
print(s.strip())
BytesIO
StringIO
操作的只能是str
,如果要操作二进制数据,就需要使用BytesIO
from io import BytesIO
f = BytesIO()
f.write('中文'.encode('utf-8')) # 不是str,是经过UTF-8编码的bytes
print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
# 读取
f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
print(f.read())
(三)操作文件和目录
Python内置的os
模块也可以直接调用操作系统提供的接口函数。如果是posix
,说明系统是Linux
、Unix
或Mac OS X
,如果是nt
,就是Windows
系统。os
模块的某些函数是跟操作系统相关的
import os
print(os.name) # 操作系统类型
print(os.uname()) # 详细系统信息 windows不提供
环境变量
在操作系统中定义的环境变量,全部保存在os.environ
这个变量中,可以直接查看:
print(os.environ)
environ({'ALLUSERSPROFILE': 'C:\\ProgramData', 'APPDATA': 'C:\\Users\\Administrator\\AppData\\Roaming', 'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'COMPUTERNAME': 'LENOVO-PC', 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe', 'FP_NO_HOST_CHECK': 'NO', 'HOMEDRIVE': 'C:', 'HOMEPATH': '\\Users\\Administrator', 'IDEA_INITIAL_DIRECTORY': 'D:\\PyCharm 2019.2.3\\bin', 'JAVA_HOME': 'D:\\Program Files\\Java\\jdk-9.0.4', 'LOCALAPPDATA': 'C:\\Users\\Administrator\\AppData\\Local', 'LOGONSERVER': '\\\\LENOVO-PC', 'NODE_PATH': 'D:\\Program Files\\nodejs\\node_global\\node_modules', 'NUMBER_OF_PROCESSORS': '6', 'OS': 'Windows_NT', 'PATH': ...)
获取某个环境变量的值,可以调用os.environ.get('key')
:
print(os.environ.get('PATH'))
D:\code\PycharmProjects\PythonStudy\venv\Scripts;D:\app\Administrator\product\11.2.0\dbhome_1\bin;C:\ProgramData\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;D:\Python37;C:\Program Files\MySQL\MySQL Server 5.7\bin;D:\Program Files\Java\jdk-9.0.4\bin;D:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\;D:\Program Files\Microsoft SQL Server\100\Tools\Binn\;D:\Program Files\Microsoft SQL Server\100\DTS\Binn\;C:\Program Files\Bandizip\;D:\Program Files\nodejs\node_global;C:\Users\Administrator\AppData\Roaming\npm\node_modules\yarn\bin;D:\Program Files (x86)\Yarn\bin
操作文件和目录
操作文件和目录的函数一部分放在os
模块中,一部分放在os.path
模块中。
# 创建和删除目录
# 查看当前目录的绝对路径
print(os.path.abspath('.'))
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
print(os.path.join('D:\code\PycharmProjects\PythonStudy\venv\Include\IO编程', 'testDir'))
# 创建一个目录:
os.mkdir('D:/code/PycharmProjects/PythonStudy/venv/Include/IO编程/testDir')
# 删除一个目录
os.rmdir('D:/code/PycharmProjects/PythonStudy/venv/Include/IO编程/testDir')
把两个路径合成一个时,不要直接拼字符串,而要通过os.path.join()
函数,这样可以正确处理不同操作系统的路径分隔符。在Linux/Unix/Mac
下,os.path.join()
返回这样的字符串:part-1/part-2
。Windows
下会返回这样的字符串:part-1\part-2
。拆分路径时,也不要直接去拆字符串,而要通过os.path.split()
函数,这样可以把一个路径拆分为两部分,后一部分总是最后级别的目录或文件名。os.path.splitext()
可以直接让你得到文件扩展名。合并、拆分路径的函数并不要求目录和文件要真实存在,它们只对字符串进行操作。
# 拆分(后一部分总是最后级别的目录或文件名)
print(os.path.split('D:/code/PycharmProjects/PythonStudy/venv/Include/IO编程/io.txt'))
# 获取文件扩展名
print(os.path.splitext('D:/code/PycharmProjects/PythonStudy/venv/Include/IO编程/io.txt'))
shutil
模块提供了copyfile()
的函数,你还可以在shutil
模块中找到很多实用函数,它们可以看做是os
模块的补充。
Python的特性来过滤文件:
# Python的特性来过滤文件 列出所有.py文件
l = [x for x in os.listdir('D:/code/PycharmProjects/PythonStudy/venv/Include/IO编程') if
os.path.isfile(x) and os.path.splitext(x)[1] == '.py']
print(l)
(四)序列化
程序运行的过程中,所有的变量都是在内存中。定义一个dict:
d = dict(name = 'Tom',age = 19 ,score = 90)
可以随时修改变量,但是一旦程序结束,变量所占用的内存就被操作系统全部回收。如果没有把修改后的变量存储到磁盘上,下次重新运行程序,变量又被初始化修改前的变量。把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling
,序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling
。
Python提供了pickle
模块来实现序列化
import pickle
d = dict(name='tom', age=18, score=90)
# 序列化
p = pickle.dumps(d)
print(p)
b'\x80\x04\x95$\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x03tom\x94\x8c\x03age\x94K\x12\x8c\x05score\x94KZu.'
pickle.dumps()
方法把任意对象序列化成一个bytes
,然后就可以把这个bytes写入文件。或者用另一个方法pickle.dump()
直接把对象序列化后写入一个file-like Object
:
# dump
f = open('D:/PycharmProjects/untitled/venv/Include/IO编程/dump.txt', 'wb')
pickle.dump(d,f)
f.close()
当要把对象从磁盘读到内存时,可以先把内容读到一个bytes
,然后用pickle.loads()
方法反序列化出对象,也可以直接用pickle.load()
方法从一个file-like Object
中直接反序列化出对象:
# 读取
f = open('D:/PycharmProjects/untitled/venv/Include/IO编程/dump.txt', 'rb')
d = pickle.load(f)
f.close()
print(d)
JSON
要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式。JSON
表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON
表示的对象就是标准的JavaScript语言的对象,JSON
和Python
内置的数据类型对应如下:
JSON类型 | Python类型 |
---|---|
{} | dict |
[] | list |
“String” | str |
1234.56 | int\float |
true/false | True\False |
null | None |
import json
d = dict(name='tom', age=20, score=90)
s = json.dumps(d)
print(s)
dumps()
方法返回一个str
,内容就是标准的JSON
。类似的,dump()
方法可以直接把JSON
写入一个file-like Object
。要把JSON
反序列化为Python
对象,用loads()
或者对应的load()
方法,前者把JSON
的字符串反序列化,后者从file-like Object
中读取字符串并反序列化:
json_str = '{"age":20,"score":99,"name":"Tom"}'
str = json.loads(json_str)
print(str)
JSON
标准规定JSON
编码是UTF-8
,所以我们总是能正确地在Python的str
与JSON
的字符串之间转。
JSON进价
Python的dict
对象可以直接序列化为JSON
的{}
,不过,很多时候,我们更喜欢用class
表示对象,比如定义Student
类,然后序列化。默认情况下,dumps()
方法不知道如何将Student
实例变为一个JSON
的{}
对象。可选参数default
就是把任意一个对象变成一个可序列为JSON
的对象,我们只需要为Student
专门写一个转换函数,再把函数传进去即可。
import json
class Student():
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def studentDict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
s = Student('Tom', 14, 89)
print(json.dumps(s, default=studentDict))
# 任意class的实例变为dict
print(json.dumps(s, default=lambda obj: obj.__dict__))
通常class
实例都有一个__dict__
属性,它就是一个dict
,用来存储实例变量。

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。
更多推荐
所有评论(0)