当我们在说 “永久存储” 的时候,是希望将数据保存到硬盘上,而非内存,因为内存在计算机断电后数据将会丢失。
使用 Python 打开一个文件,我们需要用到 open() 函数:
>>> f = open("FishC.txt", "w")
第一个参数指定的是文件路径和文件名,这里我们没有添加路径的话,那么默认是将文件创建在 Python 的主文件夹下面,因为执行 IDLE 的程序就放在那里嘛(同样的道理,如果我们在桌面创建一个 test.py 的源文件,然后输入打开文件的代码,那么它就会在桌面创建一个 FishC.txt 的文本文件)。
第二个参数是指定文件的打开模式:
(1)有两个方法可以将字符串写入到文本对象种,一个是 write(),一个是 writelines():
>>> f.write("I love Python.")
14
使用 write() 方法,它有一个返回值,就是总共写入到文件对象中的字符个数。
使用 writelines() 方法,则可以将多个字符串同时写入:
>>> f.writelines(["I love FishC.\n", "I love my wife."])
>>>
注意:虽然 writelines() 方法支持传入多个字符串,但它不会帮你添加换行符,所以我们要自己添加才行。[/code]
我们使用 close() 方法来关闭文件:
>>> f.close()
>>>
注意,文件对象关闭之后,我们就没办法对它进行操作了。
不同操作系统,它的这个路径分隔符是不一样的,比如 Windows 系统是使用反斜杠,而其他大多数操作系统则是使用斜杠来分隔。
这个 Windows 采用的反斜杠,跟字符串中的转义字符的反斜杠是同一条杠,那么,如果你想要在 Windows 上使用反斜杠来分隔路径,你就不得不用另一条反斜杠来转移反斜杠,或者使用原始字符串,并不是说这么做有多困难吧,就是觉得这样做不是那么优雅……
相对路径:以当前目录作为基准,进而逐级推导(. . 表示上级目录;. 当前目录)。
绝对路径:文件真正存在的路径,从根目录逐级推导到指定的文件 / 文件夹。
使用 Path 里面的 cwd() 方法来获取当前的工作目录:
>>> Path.cwd()
WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39')
创建路径对象:
>>> p = Path('C:/Users/goodb/AppData/Local/Programs/Python/Python39')
使用斜杠(/)直接进行路径拼接:
>>> q = p / "FishC.txt"
>>> q
WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39/FishC.txt')
使用 is_dir() 方法可以判断一个路径是否为一个文件夹:
>>> p.is_dir()
True
>>> q.is_dir()
False
使用 is_file() 方法可以判断一个路径是否为一个文件:
>>> p.is_file()
False
>>> q.is_file()
True
通过 exists() 方法测试指定的路径是否真实存在:
>>> p.exists()
True
>>> q.exists()
True
>>> Path("C:/404").exists()
False
使用 name 属性去获取路径的最后一个部分:
>>> p.name
'Python39'
>>> q.name
'FishC.txt'
stem 属性用于获取文件名,suffix 属性用于获取文件后缀:
>>> q.stem
'FishC'
>>> q.suffix
'.txt'
parent 属性用于获取其父级目录:
>>> p.parent
WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python')
>>> q.parent
WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39')
加个复数,parents,还可以获得其逻辑祖先路径构成的一个不可变序列:
>>> p.parents
<WindowsPath.parents>
>>> ps = p.parents
>>> for each in ps:
... print(each)
...
C:\Users\goodb\AppData\Local\Programs\Python
C:\Users\goodb\AppData\Local\Programs
C:\Users\goodb\AppData\Local
C:\Users\goodb\AppData
C:\Users\goodb
C:\Users
C:\
还支持索引:
>>> ps[0]
WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python')
>>> ps[1]
WindowsPath('C:/Users/goodb/AppData/Local/Programs')
>>> ps[2]
WindowsPath('C:/Users/goodb/AppData/Local')
parts 属性将路径的各个组件拆分成元组的形式:
>>> p.parts
('C:\\', 'Users', 'goodb', 'AppData', 'Local', 'Programs', 'Python', 'Python39')
>>> q.parts
('C:\\', 'Users', 'goodb', 'AppData', 'Local', 'Programs', 'Python', 'Python39', 'FishC.txt')
最后,还可以查询文件或文件夹的状态信息:
>>> p.stat()
os.stat_result(st_mode=16895, st_ino=281474976983758, st_dev=1289007019, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1648462096, st_mtime=1648205377, st_ctime=1605695407)
>>> q.stat()
os.stat_result(st_mode=33206, st_ino=4503599627467517, st_dev=1289007019, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1648206152, st_mtime=1648206152, st_ctime=1648205377)
比如这个 st_size 就是文件或文件夹的尺寸信息:
>>> p.stat().st_size
4096
>>> q.stat().st_size
0
使用 resolve() 方法可以将相对路径转换为绝对路径:
>>> Path('./doc').resolve()
WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39/Doc')
>>> Path('../FishC').resolve()
WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/FishC')
最后还可以通过 iterdir() 获取当前路径下面的所有子文件和子文件夹对象:
>>> p.iterdir()
<generator object Path.iterdir at 0x0000012D57CBE660>
它生成的是一个迭代器对象,所以可以放到 for 语句中去提取数据:
>>> for each in p.iterdir():
... print(each.name)
...
DLLs
Doc
FishC
FishC.txt
include
Lib
libs
LICENSE.txt
NEWS.txt
python.exe
python3.dll
python39.dll
pythonw.exe
Scripts
tcl
Tools
vcruntime140.dll
vcruntime140_1.dll
如果我们要将当前路径下面的所有文件整理成一个列表,可以这么做(注意,是文件,不包含文件夹,所以我们要加一个条件过滤):
>>> [x for x in p.iterdir() if x.is_file()]
[WindowsPath('FishC.txt'), WindowsPath('LICENSE.txt'), WindowsPath('NEWS.txt'), WindowsPath('python.exe'), WindowsPath('python3.dll'), WindowsPath('python39.dll'), WindowsPath('pythonw.exe'), WindowsPath('vcruntime140.dll'), WindowsPath('vcruntime140_1.dll')]
以上是用得比较多的,与路径查询相关的操作。
那么修改路径也是支持的,比如我们可以使用 mkdir() 方法来创建文件夹:
>>> n = p / "FishC"
>>> n.mkdir()
>>>
注意,如果需要创建的文件夹已经存在,那么它就会报错:
>>> n.mkdir()
Traceback (most recent call last):
File "" , line 1, in <module>
n.mkdir()
File "C:\Users\goodb\AppData\Local\Programs\Python\Python39\lib\pathlib.py", line 1323, in mkdir
self._accessor.mkdir(self, mode)
FileExistsError: [WinError 183] 当文件已存在时,无法创建该文件。: 'C:\\Users\\goodb\\AppData\\Local\\Programs\\Python\\Python39\\FishC'
也可以避开这个报错信息,我们设置其 exist_ok 参数的值为 True 即可:
>>> n.mkdir(exist_ok=True)
>>>
还有一点需要注意的就是,如果路径中有存在多个不存在的父级目录,那么也会出错的,比如这样:
>>> n = p / "FishC/A/B/C"
>>> n.mkdir(exist_ok=True)
Traceback (most recent call last):
File "" , line 1, in <module>
n.mkdir(exist_ok=True)
File "C:\Users\goodb\AppData\Local\Programs\Python\Python39\lib\pathlib.py", line 1323, in mkdir
self._accessor.mkdir(self, mode)
FileNotFoundError: [WinError 3] 系统找不到指定的路径。: 'C:\\Users\\goodb\\AppData\\Local\\Programs\\Python\\Python39\\FishC\\A\\B\\C'
它也定义了一个参数用于对付这种情况,将 parents 参数设置为 True 就可以了:
>>> n.mkdir(parents=True, exist_ok=True)
>>>
Path 内部其实还打包了一个 open() 方法,除了不用传入路径之外,其它参数跟 open() 函数是一摸一样的:
>>> n = n / 'FishC.txt'
>>> n
WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39/FishC/A/B/C/FishC.txt')
>>> f = n.open('w')
>>> f.write("I love FishC.")
13
>>> f.close()
可以给文件或文件夹修改名字,使用 rename() 方法来实现:
>>> n.rename("NewFishC.txt")
WindowsPath('NewFishC.txt')
然后使用 replace() 方法替换文件或文件夹:
>>> m = Path("NewFishC.txt")
>>> n
WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39/FishC/A/B/C/FishC.txt')
>>> m.replace(n)
WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39/FishC/A/B/C/FishC.txt')
还有删除操作,rmdir() 和 unlink() 方法,前者用于删除文件夹,后者用于删除文件:
>>> n.parent.rmdir()
Traceback (most recent call last):
File "" , line 1, in <module>
n.parent.rmdir()
File "C:\Users\goodb\AppData\Local\Programs\Python\Python39\lib\pathlib.py", line 1363, in rmdir
self._accessor.rmdir(self)
OSError: [WinError 145] 目录不是空的。: 'C:\\Users\\goodb\\AppData\\Local\\Programs\\Python\\Python39\\FishC\\A\\B\\C'
可以看到,如果不是空文件夹,它是删不掉的,我们需要先把里面的文件删了:
>>> n.unlink()
>>>
现在再删除文件夹,就 OK 啦:
>>> n.parent.rmdir()
>>>
最后是功能强大的查找,由 glob() 方法来实现:
>>> p = Path('.')
>>> list(p.glob("*.txt"))
[WindowsPath('FishC.txt'), WindowsPath('LICENSE.txt'), WindowsPath('NEWS.txt')]
这就查找当前目录下的所有 .txt 后缀的文件,如果要查找当前目录的下一级目录中的所有 .py 后缀的文件,可以这么写:
>>> p = Path('.')
>>> list(p.glob("*.txt"))
[WindowsPath('FishC.txt'), WindowsPath('LICENSE.txt'), WindowsPath('NEWS.txt')]
这就查找当前目录下的所有 .txt 后缀的文件,如果要查找当前目录的下一级目录中的所有 .py 后缀的文件,可以这么写:
>>> list(p.glob('*/*.py'))
[WindowsPath('Lib/abc.py'), WindowsPath('Lib/aifc.py'), WindowsPath('Lib/antigravity.py'), ...]
好了,那么如果希望进行向下递归搜索,也就是查找当前目录以及该目录下面的所有子目录,可以使用两个星号(**)表示:
>>> list(p.glob('**/*.py'))
[WindowsPath('Lib/abc.py'), WindowsPath('Lib/aifc.py'), WindowsPath('Lib/antigravity.py'), ...]
上下文管理器为文件操作提供了一种更为优雅的实现方式。
我们先来看一下传统的文件操作实现:
>>> f = open("FishC.txt", "w")
>>> f.write("I love FishC.")
13
>>> f.close()
总结下来无非就是三板斧:打开文件 -> 操作文件 -> 关闭文件
那么使用 with 上下文管理器方案,应该如何实现呢?
>>> with open("FishC.txt", "w") as f:
... f.write("I love FishC.")
...
13
两者是等效的,通俗来讲,对于文件操作这样的三板斧来说,上文就是打开文件,下文就是关闭文件,这个就是上下文管理器做的事情。
使用上下文管理器,最大的优势是能够确保资源的释放(在这里就是文件的正常关闭)。
pickle 模块支持你将 Python 的代码序列化,解决的就是一个永久存储 Python 对象的问题。
说白了,就是将咱们的源代码,转变成 0101001 的二进制组合。
掌握 pickle,只需要学习两个函数的用法:一个是 dump(),另一个是 load()。
使用 dump() 函数将数据写入文件中(文件后缀要求是 .pkl):
import pickle
x, y, z = 1, 2, 3
s = "FishC"
l = ["小甲鱼", 520, 3.14]
d = {"one":1, "two":2}
with open("data.pkl", "wb") as f:
pickle.dump(x, f)
pickle.dump(y, f)
pickle.dump(z, f)
pickle.dump(s, f)
pickle.dump(l, f)
pickle.dump(d, f)
使用 load() 函数读取 pickle 文件中的数据:
import pickle
with open("data.pkl", "rb") as f:
x = pickle.load(f)
y = pickle.load(f)
z = pickle.load(f)
s = pickle.load(f)
l = pickle.load(f)
d = pickle.load(f)
print(x, y, z, s, l, d, sep="\n")
如果觉得反复写很多个 dump() 和 load() 太麻烦了,可以将多个对象打包成元组后再进行序列化:
...
pickle.dump((x, y, z, s, l, d), f)
...
x, y, z, s, l, d = pickle.load(f)
...