类别:同步 IO 和异步 IO
区别:是否等待 IO 执行的结果,但异步的复杂度远高于同步。本章都是同步 IO
读写文件步骤:请求操作系统打开一个文件对象(文件描述符),然后通过操作系统提供的接口从这个文件对象中读取数据,或者把数据写入这个文件对象;
>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.txt', 'r') # 括号内部传入文件名和标示符,'r'表示读
>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.txt', 'r')
Traceback (most recent call last):
File "E:\1_Install_Total\8_Anaconda\lib\site-packages\IPython\core\interactiveshell.py", line 2961, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "" , line 1, in <module>
f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.txt', 'r')
FileNotFoundError: [Errno 2] No such file or directory: 'E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.txt' # 若文件不存在,open()函数会抛出一个 IOError 错误,并给出详细信息
>>> f.read() # 若打开成功,调用read()可以一次性读取文件的全部内容,并用一个str对象表示;
'Hello, World!'
>>> f.close() # 文件使用完毕后必须关闭
IOError
,一旦出错,将无法调用f.close()
,所以,为了保证无论是否出错都能正确关闭文件,可使用try...finally
来实现:try:
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()
with
语句来自动帮我们调用close()
方法:with open('/path/to/file', 'r') as f:
print(f.read())
with open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.txt', 'r') as f:
print(f.read()) # 一次性读取文件全部内容,当文件过大时,内存就爆了
print(f.read(size)) # 每次最多读取size个字节的内容
print(f.readline()) # 每次读取一行内容,同时也可指定size
print(f.readlines()) # 一次读取所有内容并按行返回list
# 运行结果
['Hello,World!\n', '2\n', '3\n', '4']
for line in f.readlines():
print(line.strip()) # 把末尾的‘\n’删除
read()
一次性读取最方便;如果不能确定文件大小,反复调用read(size)
比较保险;如果是配置文件,调用readlines()
最方便;open()
函数返回的这种有个read()
方法的对象,在 Python 中统称为 file-like Object, 除了 file 外,还可以是内存的字节流,网络流,自定义流等等。file-like Object 不要求从特定类继承,只要写个 read() 方法就行。StringIO
就是在内存中创建的 file-like Object,常用作临时缓冲。're'
模式打开二进制文件>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test.png', 'rb')
>>> f.read()
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节
# 读取非UTF-8编码的文本文件,给open()函数传入encoding()参数
>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test1.txt', 'r', encoding='gbk')
>>> f.read()
'测试'
# 遇到些编码不规范的文件,可能会出现UnicodeDecodeError,此时,接受一个errors参数,表示如果遇到编码错误后如何处理,最简单的方式是直接忽略:
>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test1.txt', 'r', encoding='gbk', errors='ignore')
>>> f = open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test1.txt', 'w') # 'w'会覆盖掉文件中原先的内容,相当于删掉后建了个新文件;
>>> f.write('222')
>>> 3
>>> f.close()
# 使用with语句保险:
with open('E:/4_Programe/1_Python/3_Code/1_LiaoDaDa/test1.txt', 'w') as f:
f.write('Hello, world!')
'r'
:读'w'
:写'a'
:追加'r+' == r+w
(可读可写,文件若不存在就报错 (IOError))'w+' == w+r
(可读可写,文件若不存在就创建)'a+' ==a+r
(可追加可写,文件若不存在就创建)- 对应的,如果是二进制文件,就都加一个 b 就好啦:
'rb'
'wb'
'ab'
'rb+'
'wb+'
'ab+'
# 把str写入StringIO,需要先创建一个StringIO,然后像写文件一样写入即可:
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
0
>>> f.write('world!')
6
>>> print(f.getvalue()) # getvalue()方法用于获得写入后的str
hello world!
# 读取StringIO。先用一个str初始化StringIO,然后像读文件一样读取:
from io import StringIO
f = StringIO('Hello!\nHi\nGoodbye!')
while True:
s = f.readline()
if s == '':
break
print(s.strip())
Hello!
Hi!
Goodbye!
# BytesIO实现了在内存中读写bytes:
>>> from io import BytesIO
>>> f = BytesIO() # 创建一个BytesIO
>>> f.write('中文'.encode('utf-8')) # 写入一些bytes(写入的不是str,而是经过UTF-8编码的bytes)
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
>>> from io import BytesIO
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'
os
模块直接调用操作系统的接口函数:>>> import os
>>> os.name # 操作系统类型
'nt' # Windows系统,若是'posix',则是Linux,Unix或Mac OS X
>>> os.uname() # uname()函数在Windows上不提供
posix.uname_result(sysname='Darwin', nodename='MichaelMacPro.local', release='14.3.0', version='Darwin Kernel Version 14.3.0: Mon Mar 23 11:59:05 PDT 2015; root:xnu-2782.20.48~5/RELEASE_X86_64', machine='x86_64')
# 在操作系统中定义的环境变量,全部保存在os.environ这个变量中,可以直接查看
>>> import os
>>> os.environ
environ({
'ALLUSERSPROFILE': 'C:\\ProgramData', 'AMDRMSDKPATH': 'C:\\Program Files\\AMD\\RyzenMasterSDK\\', 'APPDATA': 'C:\\Users\\Jack\\AppData\\Roaming', 'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'COMPUTERNAME': 'DESKTOP-3VRSQ0V', 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe', 'CONDA_DEFAULT_ENV': 'base', 'CONDA_EXE': 'C:\\Anaconda\\Scripts\\conda.exe', 'CONDA_PREFIX': 'C:\\Anaconda', 'CONDA_PROMPT_MODIFIER': '(base) ', 'CONDA_PYTHON_EXE': 'C:\\Anaconda\\python.exe', 'CONDA_SHLVL': '1', 'DRIVERDATA': 'C:\\Windows\\System32\\Drivers\\DriverData', 'HOMEDRIVE': 'C:', 'HOMEPATH': '\\Users\\Jack', 'LOCALAPPDATA': 'C:\\Users\\Jack\\AppData\\Local', 'LOGONSERVER': '\\\\DESKTOP-3VRSQ0V', 'NUMBER_OF_PROCESSORS': '12', 'ONEDRIVE': 'C:\\Users\\Jack\\OneDrive', 'OS': 'Windows_NT', 'PATH': 'C:\\Anaconda;C:\\Anaconda\\Library\\mingw-w64\\bin;C:\\Anaconda\\Library\\usr\\bin;C:\\Anaconda\\Library\\bin;C:\\Anaconda\\Scripts;C:\\Anaconda\\bin;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Windows\\System32\\OpenSSH\\;C:\\Program Files\\Intel\\WiFi\\bin\\;C:\\Program Files\\Common Files\\Intel\\WirelessCommon\\;D:\\01 Installation path\\17 MATLAB\\runtime\\win64;D:\\01 Installation path\\17 MATLAB\\bin;C:\\Anaconda;C:\\Anaconda\\Library\\mingw-w64\\bin;C:\\Anaconda\\Library\\usr\\bin;C:\\Anaconda\\Library\\bin;C:\\Anaconda\\Scripts;C:\\Users\\Jack\\AppData\\Local\\Microsoft\\WindowsApps;D:\\01 Installation path\\07 Bandizip\\;C:\\PyCharm\\PyCharm 2018.3.3\\bin', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC', 'PROCESSOR_ARCHITECTURE': 'AMD64', 'PROCESSOR_IDENTIFIER': 'AMD64 Family 23 Model 8 Stepping 2, AuthenticAMD', 'PROCESSOR_LEVEL': '23', 'PROCESSOR_REVISION': '0802', 'PROGRAMDATA': 'C:\\ProgramData', 'PROGRAMFILES': 'C:\\Program Files', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'PROGRAMW6432': 'C:\\Program Files', 'PROMPT': '(base) $P$G', 'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules;C:\\Program Files\\Intel\\Wired Networking\\', 'PUBLIC': 'C:\\Users\\Public', 'PYCHARM': 'C:\\PyCharm\\PyCharm 2018.3.3\\bin;', 'PYTHONIOENCODING': '936', 'SYSTEMDRIVE': 'C:', 'SYSTEMROOT': 'C:\\Windows', 'TEMP': 'C:\\Users\\Jack\\AppData\\Local\\Temp', 'TMP': 'C:\\Users\\Jack\\AppData\\Local\\Temp', 'USERDOMAIN': 'DESKTOP-3VRSQ0V', 'USERDOMAIN_ROAMINGPROFILE': 'DESKTOP-3VRSQ0V', 'USERNAME': 'Jack', 'USERPROFILE': 'C:\\Users\\Jack', 'WINDIR': 'C:\\Windows'})
# 要想获取某个环境变量的值,可以调用os.environ.get('key'):
>>> os.environ.get('PATH')
'C:\\Anaconda;C:\\Anaconda\\Library\\mingw-w64\\bin;C:\\Anaconda\\Library\\usr\\bin;C:\\Anaconda\\Library\\bin;C:\\Anaconda\\Scripts;C:\\Anaconda\\bin;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Windows\\System32\\OpenSSH\\;C:\\Program Files\\Intel\\WiFi\\bin\\;C:\\Program Files\\Common Files\\Intel\\WirelessCommon\\;D:\\01 Installation path\\17 MATLAB\\runtime\\win64;D:\\01 Installation path\\17 MATLAB\\bin;C:\\Anaconda;C:\\Anaconda\\Library\\mingw-w64\\bin;C:\\Anaconda\\Library\\usr\\bin;C:\\Anaconda\\Library\\bin;C:\\Anaconda\\Scripts;C:\\Users\\Jack\\AppData\\Local\\Microsoft\\WindowsApps;D:\\01 Installation path\\07 Bandizip\\;C:\\PyCharm\\PyCharm 2018.3.3\\bin'
>>> os.environ.get('x', 'default')
'default'
os
模块中,一部分放到os.path
模块中,要注意一下这点# 查看当前目录的绝对路径:
>>> os.path.abspath('.')
'C:\\Windows\\system32'
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
>>> os.path.join('C:\\Windows\\system32', 'testdir')
'C:\\Windows\\system32\\testdir'
# 然后创建一个目录:
>>> os.mkdir('C:\\Windows\\system32\\testdir')
# 删除一个目录:
>>> os.rmdir('C:\\Windows\\system32\\testdir')
os.path.join()
函数来将两个路径合成一个:part-1/part-2 # 在linux/unix/mac下,此函数返回这样的字符串;
part-1\part-2 # 在windows下返回这样的字符串;
os.path.split()
函数来拆分路径>>> os.path.split('C:\\Windows\\system32\\testdir\\file.txt')
('C:\\Windows\\system32\\testdir', 'file.txt') # 后一部分总是最后级别的目录或文件名
>>> os.path.splitext('/path/to/file.txt') # 通过此函数直接得到文件扩展名
('/path/to/file', '.txt')
# 合并,拆分路径的函数不要求真实存在,只是对字符串进行操作
test.txt
文件:# 对文件重命名:
>>> os.rename('test.txt', 'test.py')
# 删除文件:
>>> os.remove('test.py')
复制文件的函数在os
模块中不存在,可以通过shutil
模块中的copyfile()
函数来实现复制文件;
利用 python 的特性来过滤文件:
# 列出当前目录下的所有目录:
>>> [x for x in os.listdir('.') if os.path.isdir(x)]
['0409', 'AdvancedInstallers', 'am-et', 'AppLocker', 'appraiser', 'AppV', 'ar-SA', 'bg-BG', 'Boot', 'Bthprops', 'CatRoot', 'catroot2', 'CodeIntegrity',...]
>>> [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']
name
修改成Bill
,但是一旦程序结束后,变量所占用的内存就会被操作系统全部收回,若没有把修改后的Bill
存储到磁盘上,下次重新运行程序,变量又被初始化为Bob
d = dict(name='Bob', age=20, score=88)
pickling
,序列化之后,就可以把序列化之后的内容写入磁盘或者通过网络传输到别的机器上,反过来,把变量内容从序列化的对象重新读到内存里叫做反序列化,即unpickling
;pickle
模块来实现序列化:# 把一个对象序列化:
>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d) # 通过此方法把任意对象序列化成一个bytes
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x03\x00\x00\x00Bobq\x02X\x03\x00\x00\x00ageq\x03K\x14X\x05\x00\x00\x00scoreq\x04KXu.'
# 将序列化后的对象写入一个file-like Object:
>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()
# 将对象从磁盘读到内存:
>>> import pickle
>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f) # 用此方法从一个file-like Object中直接反序列化出对象
>>> f.close
<built-in method close of _io.BufferedReader object at 0x0000024248596E08>
>>> d
{
'name': 'Bob', 'age': 20, 'score': 88}
# 这个变量和刚才哪个变量只是内容相同,两者完全是不相干的对象
json
模块提供了非常完善的 python 对象到 JSON;# dumps()方法返回一个str,内容就是标准的JSON,类似的dump()方法可以直接把JSON写入一个file-like Object
>>> import json
>>> d = dict(name='Bob', age='20', score=88)
>>> json.dumps(d)
'{"name": "Bob", "age": "20", "score": 88}'
# 同样,把JSON反序列化为python对象,用loads()或者对应的load()方法,前者把JSON的字符串反序列化,后者从file-like Object中读取字符串并反序列化
>>> json_str='{"age": 20, "score": 88, "name": "Bob"}'
>>> json.loads(json_str)
{
'age': 20, 'score': 88, 'name': 'Bob'}
dict
对象可以直接序列化为 JSON 的{}
,那怎么把用class
表示的对象序列化呢,比如定义的Student
类:import json
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
s = Student('Bob', 20, 88)
print(json.dumps(s))
# 运行此代码,会得到一个错误:
Traceback (most recent call last):
...
TypeError: Object of type Student is not JSON serializable
Student
对象不是一个可序列化的 JSON 对象,但这肯定不合理,仔细看dumps()
方法的参数列表,除了第一个必须的obj
参数外,dumps
方法还提供了一大堆可选参数:https://docs.python.org/3/library/json.html#json.dumps 这些可选参数可以允许我们自己定制 JSON 序列化,前面的代码之所以无法把Student
类实例序列化为 JSON,是因为默认情况下,dumps
方法不知道如何将Sdudent
实例变为一个 JSON 的{}
对象;default
就是把任意一个对象变成一个可序列化为 JSON 的对象,我们之需要为Sdudent
专门写一个转换函数,再把函数传进去即可:def student2dict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
Student
实例首先被Student2dict()
函数转换成dict
,然后再被顺序序列化为 JSON:>>> print(json. dumps(s, default=student2dict))
{
"age": 20, "name": "Bob", "score": 88}
Teacher
类的实例,照样无法序列化为 JSON。我们可以偷个懒,把任意class
的实例变为dict
:print(json. dumps(s, default=lambda obj: obj.__dict__))
class
的实例都有一个__dict__
属性,他就是一个dict
,用来储存实例变量,也有少数例外,比如定义了__slots__
的 class;Student
对象实例,loads()
方法首先转换出一个dict
对象,然后,我们传入的object_hook
函数负责把dict
转换为Student
实例:def dict2student(d):
return Student(d['name'], d['age'], d['score'])
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> print(json.loads(json_str, object_hook = dict2student))
<__main__.Student object at 0x10cd3c190>
Student
实例对象。对中文进行 JSON 序列化时,json.dumps()
提供了一个ensure_ascii
参数,观察该参数对结果的影响:
import json
obj = dict(name = '小明', age = 20)
s = json.dumps(obj, ensure_ascii = True)
print(s)
# 运行结果
{
"name": "\u5c0f\u660e", "age": 20}