原文链接:/python-pathlib/#creating-empty-files
(资料图片仅供参考)
by Geir Arne Hjelle Apr 17, 2023
目录
将路径表示为字符串的隐患
用Python的pathlib把路径实例化
使用Path类的方法
传入字符串
拼接路径
用Path进行文件系统操作
获取路径的各个部分
读写文件
重命名文件
复制文件
移动、删除文件
创建空文件
Python pathlib案例
统计文件个数
显示文件夹树状结构
找到最近修改的文件
创建独一无二的文件名
结语
对Python开发者来说,和文件打交道、和文件系统互动都是稀疏平常的事。有时是仅仅读写文件,而有时则更复杂。也许你需要在一个给定文件夹里列出给定类别的所有文件、找到给定文件的父级文件夹或是创建一个先前不存在的独一无二的文件名。这时就可以用 pathlib
。
pathlib
模块在Python标准库里,能帮助你应对上述挑战。它集成了诸多必要功能,你可以方便地调用 Path
对象的方法和属性来达成这些功能。
在本教程中,你将学到:
在Python里处理文件和文件夹的路径
以多种方式实例化一个 Path
对象
使用 pathlib
来读写文件
谨慎地复制、移动和删除文件
操作路径和底层文件系统
获取路径的各个部分
你也将在本教程里学到一大堆代码案例,你完全可以把它们用在你的日常文件操作中。比如,你将深入了解统计文件个数、找到文件夹中最近修改的文件以及创建独一无二的文件名。
pathlib
能提供这么多方法和属性是极好的,但是匆匆忙忙地背下来却很困难。这时一个小抄就显得很有必要啦,点击下方链接获取你的小抄:
Free Download:Click here to claim your
pathlib
cheat sheet so you can tame the file system with Python.
将路径表示为字符串的隐患
有了Python的 pathlib
,你终于不用那么头疼了。它灵活的 Path
类有着直观的语义。先别急着了解这个类,看看 pathlib
诞生前,Python开发者们是怎么处理路径的。
传统方式是,Python用普通的 text strings (文本字符串)来表示文件路径。然而,由于路径所代表的含义不仅仅是一个字符串,与之相关的重要功能就在标准库里散得到处都是,包括os
, glob
, 和 shutil
。
比如,下面这个代码块将文件移动在一个子文件夹里:
就为了把所有的文本文件移动到一个归档文件夹里,你需要三个 import
statements (import 语句)。
Python的 pathlib
提供了一个 Path
类,在不同的操作系统里用法都相同。现在你不用导入多个不同的模块例如glob
, os
, 和 shutil
, pathlib
就够了:
和第一个例子一样,这段代码找到当前文件夹下的文本文件,然后移动到一个 archive/
子文件夹下。然而,有了 pathlib
你用很少的 import
语句和直观的语法就能完成同样的目的,在接下来的章节里你会学到更多。
用Python的pathlib把路径实例化
pathlib
的初衷之一就是用专门的对象来表示文件系统, instead of strings (而不是字符串)。确切地说,official documentation of pathlib
(pathlib
的官方文档)称之为面向对象的文件系统路径(Object-oriented filesystem paths)
。
当你把 pathlib
的语法和老的 的方式相比较时, object-oriented approach (面向对象的方式)是很明显的。当你注意到
pathlib
的核心是 Path
类时,这就更明显了:
如果你从来没用过这个模块或者不知道用哪个类,基本用
Path
就对了。 (Source)
事实上,Path
太常用了以至于你一般可以直接把它导入进来:
由于你基本上都在跟 pathlib
中的 Path
类打交道,这种导入 Path
的方式能让你在码代码时省些功夫。这样的话,你可以直接使用 Path
,而不是将 pathlib
整个模块导入,然后用 。
还有些别的方式实例化一个 Path
对象。在这个章节里,你会学习如何通过 class methods (类方法)、传入字符串或是拼接路径来创建路径。
使用Path类的方法
你导入 Path
之后,就能用现有的方法来获取当前工作路径或是home路径。
当前工作路径是文件系统里当前进程所处的路径。你编程时,比如说,想要创建或打开一个跟你正在执行的脚本同路径的文件,就需要确定当前工作路径。
此外,跟文件打交道时知道用户home路径也很有用。将home路径作为起点,你就可以在不同的机器(操作系统)里指定任意路径,不论用户名是什么。
为了获取当前工作路径,你可以使用 .cwd()
:
当你实例化 ,你得到一个
WindowsPath
或是 PosixPath
对象,取决于你正在使用的操作系统。
在Windows里,.cwd()
返回一个 WindowsPath
。在Linux和macOS里,你得到一个 PosixPath
。尽管内部细节不同,这些对象给你提供了完全一致的接口。
操作系统差异
可能你会显式地要求一个
WindowsPath
或PosixPath
,但这样除了让你的代码受限于操作系统类型之外没有任何好处。一个下面这样的具体路径在别的操作系统就不起作用了:
当如果你就想在Windows机器上操作Unix路径,或是反过来呢?这样的话,你可以在任何系统上直接实例化
PureWindowsPath
或是PurePosixPath
。当你这样来创建路径,在内部就创建了一个PurePath
object 。如果你需要表示一个路径而不去访问底层文件系统,就可以使用这样的对象。
一般地,使用 Path
是不错的。有了 Path
,你就可以实例化一个当前操作系统的具体的路径,而你的代码又不会受操作系统限制。具体的路径允许你对Path
对象进行系统级的调用,但是纯路径(pure path)只允许你操作路径,而不能访问。
使用不依赖操作系统的路径意味着你在Windows操作系统上写进脚本里的 ()
在macOS或Linux上也能运行。当然,这一点对 .home()
也适用:
有了 ()
和 ()
,你在Python脚本里就可以很方便地获取到一个出发点。如果你需要把路径拼出来,或是引用子目录结构,就可以通过字符串实例化 Path
。
传入一个字符串
除了以home路径或工作路径为出发点,你还可以传入字符串来指向它代表的路径:
这些步骤创建了一个 Path
对象。你不用处理字符串,相反,可以用 pathlib
提供的更灵活的方案。
在Windows上,路径分隔符是反斜杠(\)。然而,在许多情况下,反斜杠也被用作 escape character (转义字符),用来代表不可打印的字符。为了避免出问题,使用原始字符串字面量来表示Windows路径:
以 r
开头的字符串是原始字符串字面量。在原始字符串字面量里, \ 代表了一个字面量反斜杠。在普通字符串里,你需要使用两个反斜杠(\ \)来表明你希望使用字面量反斜杠而不是转义字符。
注意:一种获取当前模块工作路径的地道方式是使用
__file__
:
__file__
attribute (__file__
属性)包含了Python当前导入或是运行的文件路径。如果需要操作模块本身的路径,你可以把__file__
传给Path
。比如,也许你想用.parent
获取父级路径。
你也许已经注意到了,即使在Windows里你输入路径时用的反斜杠, pathlib
也会在表示路径时用斜杠(/)作为路径分隔符。这种表示方式称作POSIX风格。
POSIX是 Portable Operating System Interface 的缩写,是一种兼容不同操作系统的标准。这个标准的内容远不止路径的表示法。你在 Open Group Base Specifications Issue 7 里能了解到更多。
同样地,当你把一个Path
对象转回字符串,它也会回到本地形式——例如,Windows里就是反斜杠:
通常来说,为了方便,你的代码里应该尽可能使用 Path
对象,但是在特定情况下转回字符串也是必要的。一些库和API仍然希望你传入字符串形式的文件路径,此时你就需要在传入特定的函数前,先把 Path
对象转成字符串。
拼接路径
第三种构造Path
对象的方法是用斜杠来连接路径的各个部分,这可能是 pathlib
库里最不常用的功能。你可能在本教程刚开始的例子中就已经吃惊过一次了:
正斜杠运算符可以连接多个路径或是连接路径和字符串,只要这中间有一个Path
对象就行。不管你的操作系统实际用什么作为路径分隔符,这里都用正斜杠。
如果你不喜欢这种特殊的正斜杠符号,也可以用 .joinpath()
方法来做到同样的事:
这种方式更接近你以前可能用过的 ()
。尤其是如果你已经习惯了反斜杠来分隔路径,那么.joinpath()
是一种对你来说更熟悉的方式,而不是正斜杠。
在你实例化 Path
之后,你可能想对它做点什么。比如,也许你想执行文件操作,或是从路径里截取一些部分。这就是你接下来要做的。
用Path进行文件系统操作
通过使用 pathlib
,你可以方便地 operations on your file system (对文件系统)做一大堆操作。在这个章节,你将对一些最常用的功能有一个宽泛概念。但是在你开始操作文件之前,看看一个路径有哪些部分。
获取路径的各个部分
一个文件或文件夹路径由不同部分组成。当你使用 pathlib
,这些部分以属性的形式方便地供你获取。基本有这些:
.name
:不含文件夹的纯文件名
.stem
:不含后缀的文件名
.suffix
:文件后缀名
.anchor
:各级文件夹之前的部分
.parent
:包含了该路径的父级文件夹
这里,你可以上手观察这些属性:
Windows:
Linux:
macOS:
注意 .parent
返回的是新的 Path
对象,而其他属性返回的是字符串。这就是说,例如,你可以像最后一个例子那样连着用 .parent
,甚至再跟正斜杠组合使用来创造全新的路径:
要记的属性相当多。如果你想在引用这些 Path
属性方便一点,可以点这个链接下载Real Python的pathlib
小抄:
Free Download:Click here to claim your
pathlib
cheat sheet so you can tame the file system with Python.
读写文件
试想一下,你想 print 出一个 Markdown 文档里的购物清单。这个 shoppping_
长这样:
传统做法来说, read or write a file in Python (在Python里读写文件)都是用内置的 open()
函数。有了 pathlib
,你可以直接对Path
对象使用 open()
。
所以,这个找到并打印shopping_
中物件的脚本的第一份草稿长这样:
事实上, ()
底层就是在调用 built-in open()
(内置open()
)函数。这就是为什么你在 ()
里可以使用 mode
、encoding
这些参数。
除此之外, pathlib
提供了一些便于读写文件的方法:
.read_text()
以文本模式打开路径并返回字符串格式的内容。
.read_bytes()
以二进制模式打开路径并返回字节字符串的内容。
.write_text()
打开路径并写入字符串数据。
.write_bytes()
以二进制模式打开路径并写入数据。
上面每个方法都能打开并关闭文件。因此,你可以用 .read_text()
更新
如果你想创建一个只包含了杂货的购物清单,可以以相似的方式使用 .write_text()
:
当使用 .write_text()
时,Python会在没有提示的情况下覆写同路径的文件。这就是说你一步操作就会让所有辛苦工作付之东流!一如既往地,当你在Python里写入文件时,对代码正在做的事应该很谨慎,重命名时也一样。
重命名文件
当你希望重命名文件,你可以使用 .with_stem()
,.with_suffix()
, 或者 .with_name()
。这些方法会返回原始路径,但替换了文件名/文件后缀/或两者:
使用 .with_suffix()
返回了一个新的路径,接下来你得使用 .replace()
才能实实在在地重命名文件。这会把 txt_path
移动到 md_path
然后在保存时重命名。
如果你想改变整个文件名,包括后缀,那么你可以用 .with_name()
:
上面这段代码把 改成了
。
如果你只想改变文件名,不想改后缀,就可以用 .with_stem
。你会在下一个章节学习这个方法。
复制文件
令人惊讶的是,Path
没有提供任何复制文件的方法。但是有了前面学到的 pathlib
的知识,你可以用较少的代码创造这个功能:
你使用 .with_stem()
在不改变后缀的情况下创建新的文件名。实际的复制行为发生在最后一行,你使用 .read_bytes()
来读取 source
的内容,然后使用 .write_bytes()
把内容写入 destination
。
虽然使用 pathlib
来处理所有跟路径相关的事具有诱惑力,但你确实也会想 using shutil
for copying files (使用shutil
来复制文件)。用 Path
对象来复制文件也是个不错的选择。
移动、删除文件
通过 pathlib
,你也可以进行基础系统级别的文件操作,例如移动、更新,甚至删除文件。大多数情况下,这些方法在删除信息、文件前都没有警告或是等待确认。所以,使用的时候要小心。
要移动一个文件,你可以使用 .replace()
。注意如果目标路径已经存在,那么 .replace()
会覆写它。为了避免可能的覆写,你可以在移动前测试一下目标路径是否存在:
然而,这并没有考虑到可能的 race condition (竞态条件)。另一个进程可能刚好在本进程执行到 if
语句和 .replace()
方法之间时,往 destination
路径下添加了一个文件。如果你担心这样,那么更安全的一种方式是打开目标路径时进行exclusive creation (独占创建)然后显式地复制源数据并在之后删除源文件:
如果 destination
已经存在,那么上述代码就会捕获 FileExistsError
然后打印一个警告。为了执行移动操作,你得在复制结束后用 unlink()
删除 source
。使用 else
能确保文件在复制失败时不会被删除。
创建空文件
要用 pathlib
创建空文件,你可以使用 .touch()
。这个方法意在更新文件的修改时间,但你也可以利用他的副作用来创建一个新的文件:
在上面的例子中,你实例化了一个 Path
对象然后用 .touch()
创建了文件。你使用了2次 exists()
来检查文件事先并不存在,并且在之后被成功创建。如果你再次使用 .touch
,那就会更新这个文件的修改时间。
如果你不想一不小心修改了文件,那么可以使用 exist_ok
参数并设成 False
:
当你对一个不存在的文件路径使用 .touch()
,可以创建一个空文件。如果你想先把文件名存下来待会儿使用,暂时还没什么需要写入的,那么用()
创建一个空的文件就有用。例如,你可能想创建一个空文件来确保某个文件名是可用的,即使现在没什么内容要写入。
Python pathlib 案例
在这个章节,你会看到一些使用 pathlib
解决Python开发者日常挑战的案例。你可以在你的代码中,将这些案例作为出发点,或是存下来之后使用。
统计文件个数
有各种方式来 get a list of all the files in a directory with Python (用Python获取目录下的文件列表)。有了 pathlib
,你可以方便地使用 iterdir()
方法,它会迭代给定目录下的所有文件。在下面的例子中,你结合了 iterdir()
和 class 来统计当前目录下的每种文件的个数:
你可以用 .glob()
和 .rglob()
方法创建更多灵活的文件列表。例如, ().glob(".txt")
返回当前目录下所有含 .txt
后缀的文件。接下来,你只统计后缀以 p
开头的文件:
如果你想递归搜索当前目录及其子目录下的所有文件,就用 .rglob()
。这个方法还提供了炫酷的方式来展示文件夹树,看下一个例子。
展示一个文件夹树
在这个例子中,你定义了一个 tree()
函数,会打印出代表文件结构的可视化树,以给定的目录作为根节点。这通常,举个例子,在你想浏览一遍项目子文件夹时很有用。
要遍历子文件夹, 你使用 .rglob()
method :
注意,你需要知道一个文件的位置离根节点有多远。要做到这点,你先是用了 .relative_to()
来表示与根目录的相对路径。然后,你又用了 .parts
属性来统计路径表示中的文件夹层级数。当函数运行时,就会创建一个下面这样的可视化树:
如果你还想提升这段代码,那么你可以尝试创建 directory tree generator for the command line (命令行中的目录树生成器)。
找到最近修改的文件
.iterdir()
, .glob()
和 .rglob()
方法都很适合 generator expressions (生成器语法)和 list comprehensions (列表推导式)。要找到最近修改的文件,你可以使用 .stat()
方法来获取底层文件的信息。例如, ._mtime
提供了文件的最近修改时间:
像 .stat().st_mtime
这样的属性返回的时间戳代表从1970年1月1日至今的秒数,也称作 epoch 。如果你喜欢别的格式,可以用 或是
来把时间戳转换成更合适的格式。如果上面的例子点燃了你的好奇心,那么你也许想继续了解 how to get and use the current time in Python (在Python里如何获取并使用当前时间)。
创建独一无二的文件名
在最后这个例子里,你会构造独一无二的基于一定字符串模板的数字编号文件名。如果你不想覆写已有文件,这就显得很有用:
在 unique_path()
里,你为文件名指定了一个模板,内部留有计数变量的位置。然后,你检查了组合目录和文件名之后的新路径是否存在,文件名里有计数变量的值。如果已经存在了,就增加计数变量,再次尝试。
现在你可以运行上面的代码来获取独一无二的文件名了:
如果目录下已经有 和
,那上面的代码就会把
path
设成 。
结语
Python的 pathlib
模块提供了现代化、Pythonic的方式来处理文件路径,让代码可读性更强、更容易维护。有了 pathlib
,你可以用专门的 Path
对象来表示文件路径,而不是纯字符串。
在本教程中,你学到了:
在Python里处理文件和文件夹的路径
以多种方式实例化一个 Path
对象
使用 pathlib
来读写文件
谨慎地复制、移动和删除文件
操作路径和底层文件系统
获取路径的各个部分
pathlib
模块通过提供有用的方法和属性,让处理文件路径更加方便。不同系统的特异性也被 Path
对象隐藏了起来,使得你的代码在不同操作系统中能保持一致。
如果你想获取一个总结性的PDF,包含了 pathlib
提供的方便的方法和属性,可以点击下方链接:
Free Download:Click here to claim your
pathlib
cheat sheet so you can tame the file system with Python.
翻译说明:
翻译原稿为markdown文档,超链接复制到B站后会失效。
专有词汇等一般不予翻译,除非特别影响理解。