每个操作系统都有不同的文件路径构建规则。例如,Linux使用正斜线作为路径,而Windows使用反斜线。
如果你在一个项目中工作,而你想让来自不同操作系统的其他开发者扩展你的代码,那么这个小小的差别可能会引起问题。
幸运的是,如果你在Python中编码,Pathlib模块就可以完成繁重的工作,让你确保你的文件路径在不同的操作系统中工作一致。此外,它还提供了一些功能和操作,帮助你在处理和操作路径时节省时间。
前提条件
Pathlib默认与Python >= 3.4一起出现。然而,如果你使用的Python版本低于3.4,你将无法使用这个模块。
Pathlib是如何工作的?
为了了解如何使用Pathlib构造一个基本路径,让我们创建一个名为example.py
的新的Python文件,并把它放在一个特定的目录中。
打开该文件,并输入以下内容:
import pathlib
file = pathlib.Path(__file__)
print(file)
example.py
在这个例子中,我们导入Pathlib模块。然后,我们创建一个叫做file
的新变量来存储路径。在这里,我们使用Pathlib中的Path对象,用一个叫做file的神奇方法来引用我们当前写在它里面的文件路径example.py
。
如果我们打印file
,我们将得到我们当前所在文件的路径。
/home/rochdikhalid/dev/src/package/example.py
如上图所示,Pathlib通过把这个特殊的脚本放在Path对象中,创建了一个通往这个文件的路径。Pathlib包含许多对象,比如PosixPath()
和PurePath()
,我们将在下面的章节中进一步了解这些对象。
在我们跳进去之前,Pathlib将文件系统的路径分为两个不同的类,代表两种类型的路径对象。Pure Path和Concrete Path。
PurePath()类
纯粹路径提供了处理和操作你的文件路径的实用工具,而不做写入操作,而具体路径允许你操作和对你的文件路径做写入操作。
换句话说,具体路径是纯路径的一个子类。它继承了父类的操作,并增加了做系统调用的输入/输出操作。
Python中的纯路径
纯粹路径可以操作你机器上的文件路径,即使它属于不同的操作系统。
例如,假设你是在Linux上,你想使用Windows的文件路径。在这里,纯路径类对象将帮助你在你的机器上进行一些基本操作,如创建子路径或访问路径的各个部分。
但纯路径将无法模仿其他一些操作,如创建目录或文件,因为你实际上不在那个操作系统中。
如何使用纯路径
正如你在上图中看到的,纯路径由三个类组成,它们可以处理你机器上的任何文件系统路径。
PurePath()是根节点,为Pathlib中的每个路径对象提供处理操作。
当你实例化PurePath()
,它创建了两个类来处理Windows路径和非Windows路径。PurePath()
,创建了一个通用的路径对象 "不可知路径",不管你在什么操作系统上运行。
In [*]: pathlib.PurePath('setup.py')
Out[*]: PurePosixPath('setup.py')
上面的例子中的PurePath()创建了一个PurePosixPath()
,因为我们假设我们是在Linux机器上运行。但是如果你在Windows上实例化它,你会得到类似PureWindowsPath('setup.py')
。
PurePosixPath()是PurePath()的子节点,为非Windows文件系统的路径实现。
In [*]: pathlib.PurePosixPath('setup.py')
Out[*]: PurePosixPath('setup.py')
如果你在Windows上实例化PurePosixPath()
,你不会得到任何错误,因为太简单了,这个类不做系统调用。
ureWindowsPath()是为Windows文件系统路径实现的PurePath()的子节点。
In [*]: pathlib.PureWindowsPath('setup.py')
Out[*]: PureWindowsPath('setup.py')
这同样适用于PureWindowsPath()
,因为这个类不提供系统调用,所以对于其他操作系统来说,实例化它不会引发任何错误。
纯粹的路径属性
中的每个子类 PurePath()
中的每个子类都提供了以下属性。
PurePath().parent输出路径的父级:
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').parent
Out[*]: PurePosixPath('/src/goo/scripts')
在上面的例子中,我们正在使用.parent
属性来获取 的逻辑父类的路径。 **main.py**
.
PurePath().parents[]输出该路径的祖先:
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py')
p.parents[0]
Out[*]: PurePosixPath('/src/goo/scripts')
In [*]: p.parents[1]
Out[*]: PurePosixPath('/src/goo')
你应该总是在方括号中指定祖先的索引,如上图所示。在Python 3.10及以上版本中,你可以使用片状和负数的索引值。
PurePath().name提供了你的路径的最后一个组件的名称:
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').name
Out[*]: 'main.py'
在这个例子中,最后的路径组件是main.py
。所以,.name
属性输出的是文件名 main.py
,它的后缀是**.py**。
另一方面,PurePath().suffix提供了路径中最后一个组件的文件扩展名。
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').suffix
Out[*]: '.py'
与.name
属性相比,.suffix
属性输出的是文件扩展名,不包括文件名。
PurePath().stem只输出你的路径的最后一个组件的名称,不包括后缀。
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').stem
Out[*]: 'main'
如上所述,.stem
属性排除了最终组件的后缀main.py
,只提供文件名。
纯粹的路径方法
PurePath()
的每个子类都提供以下方法。
PurePath().is_absolute()检查你的路径是否是绝对的。
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py')
p.is_absolute()
Out[*]: True
In [*]: o = pathlib.PurePath('scripts/main.py')
o.is_absolute()
Out[*]: False
请注意,绝对路径由根和驱动器名称组成。在这种情况下,PurePath()
,不允许我们知道驱动器的名称。
如果你使用PureWindowsPath()
,你可以表示一个包含驱动器名称的绝对路径,如PureWindowsPath('c:/Program Files')
。
PurePath().is_relative()检查该路径是否属于其他给定路径:
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py')
p.is_relative_to('/src')
Out[*]: True
In [*]: p.is_relative_to('/data')
Out[*]: False
在这个例子中,给定的路径/src
是路径p
的一部分或属于路径 ,而另一个给定的路径/data
会引发False
,因为它与路径p
没有相对关系。
PurePath().joinpath()将路径与给定参数(子路径)连接起来:
In [*]: p = pathlib.PurePath('/src/goo')
p.joinpath('scripts', 'main.py')
Out[*]: PurePosixPath('/src/goo/scripts/main.py')
注意,不需要在你给定的参数中添加斜线,因为.joinpath()
方法为你处理这个问题。
PurePath().match()检查路径是否匹配给定的模式:
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').match('*.py')
Out[*]: True
In [*]: pathlib.PurePath('/src/goo/scripts/main.py').match('goo/*.py')
Out[*]: True
In [*]: pathlib.PurePath('src/goo/scripts/main.py').match('/*.py')
Out[*]: False
根据上面的例子,该模式应该与路径匹配。如果给定的模式是绝对的,路径也必须是绝对的。
PurePath().with_name()用后缀来改变最终组件的名称。
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py')
p.with_name('app.js')
Out[*]: PurePosixPath('/src/goo/scripts/app.js')
In [*]: p
Out[*]: PurePosixPath('/src/goo/scripts/main.py')
.with_name()
方法不会永久地改变最后一个组件的名称。另外,如果给定的路径不包含一个名字,就会出现官方文档中提到的错误。
PurePath().with_stem()只改变路径中最后一个组件的名称。
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py')
p.with_stem('app.py')
Out[*]: PurePosixPath('/src/goo/scripts/app.py')
In [*]: p
Out[*]: PurePosixPath('/src/goo/scripts/main.py')
这与.with_name()
方法类似。.with_stem()
暂时改变最后一个组件的名称。另外,如果给定的路径不包含一个名字,就会发生错误。
PurePath().with_suffix()临时改变路径最后一个组件的后缀或扩展名。
In [*]: p = pathlib.PurePath('/src/goo/scripts/main.py')
p.with_suffix('.js')
Out[*]: PurePosixPath('/src/goo/scripts/main.js')
如果给定路径的名称不包含后缀,.with_suffix()
方法会为你添加后缀。
In [*]: p = pathlib.PurePath('/src/goo/scripts/main')
p.with_suffix('.py')
Out[*]: PurePosixPath('/src/goo/scripts/main.py')
但是,如果我们不包括后缀,并且保持参数为空''
,当前的后缀将被删除。
In [*]: p = pathlib.PurePath('/src/goo/scripts/main')
p.with_suffix('')
Out[*]: PurePosixPath('/src/goo/scripts/main')
一些方法如.with_stem()
, 和.is_relative_to()
最近被添加到 Python 3.9 及以上版本中。所以,如果你使用Python 3.8或更低版本调用这些方法,会产生一个属性错误。
Python中的具体路径
具体路径允许你在不同的文件系统路径上处理、操作和进行写操作。
换句话说,这种类型的路径对象可以帮助你创建例如一个新的文件、一个新的目录,并在不在该操作系统中的情况下做其他输入/输出操作。
如何使用具体路径
具体路径处理任何文件系统路径,并在你的机器上进行系统调用。那些路径对象是纯路径的子路径,和纯路径一样由三个子类组成。
Path()是PurePath()
的子节点,它提供处理操作,能够对你的路径进行写入操作。
当你实例化Path()
,它创建两个类来处理Windows路径和非Windows路径。和PurePath()
一样,Path()
也创建了一个通用的路径对象 "不可知路径",不管你在哪个操作系统上运行。
In [*]: pathlib.Path('setup.py')
Out[*]: PosixPath('setup.py')
Path()
在上面的例子中,创建了一个 ,因为我们假设我们是在Linux机器上运行。但如果你在Windows上实例化它,你会得到类似的东西PosixPath()
WindowsPath('setup.py')
**PosixPath()**是Path()
和PurePosixPath()
的子节点,用来处理和操作非Windows文件系统的路径。
In [*]: pathlib.PosixPath('setup.py')
Out[*]: PosixPath('setup.py')
如果你在Windows机器上实例化PosixPath()
,你会得到一个错误,因为你在不同的操作系统上运行时不能进行系统调用。
WindowsPath()是Path()
和PureWindowsPath()
的子节点,为Windows文件系统路径实现。
In [*]: pathlib.WindowsPath('setup.py')
Out[*]: WindowsPath('setup.py')
这同样适用于WindowsPath()
,因为你是在不同的操作系统上运行的--所以实例化它将引发一个错误。
具体路径的属性
由于具体路径是纯路径的子类,你可以使用PurePath()
的属性对具体路径进行一切操作。这意味着我们可以使用,例如,.with_suffix
属性 ,为具体路径添加一个后缀。
In [*]: p = pathlib.Path('/src/goo/scripts/main')
p.with_suffix('.py')
Out[*]: PosixPath('/src/goo/scripts/main.py')
或者,你可以检查一个给定的路径是否是相对于原始路径的。
In [*]: p = pathlib.Path('/src/goo/scripts/main.py')
p.is_relative_to('/src')
Out[*]: True
永远记住,具体路径继承了纯路径的处理操作,并增加了做系统调用和输入/输出配置的写入操作。
具体路径的方法
Path()
的每个子类都提供以下方法来处理路径和做系统调用。
Path().iterdir()返回一个目录的内容。假设我们有以下文件夹,其中包含以下文件。
data
population.json
density.json
temperature.yml
stats.md
details.txt
data文件夹
要返回/data
目录的内容,你可以在这里使用.iterdir()
方法:
In [*]: p = pathlib.Path('/data')
for child in p.iterdir():
print(child)
Out[*]: PosixPath('/data/population.json')
PosixPath('/data/density.json')
PosixPath('/data/temprature.yml')
PosixPath('/data/stats.md')
PosixPath('/data/details.txt')
.iterdir()
方法创建一个迭代器,随机列出文件。
**Path().existence()**检查文件/目录是否存在于当前路径中。让我们使用前面例子中的目录(我们的当前目录是/data
)。
In [*]: p = pathlib.Path('density.json').exists()
p
Out[*]: True
.exists()方法返回True
,因为给定的文件存在于data
目录中。如果该文件不存在,该方法返回False
。
In [*]: p = pathlib.Path('aliens.py').exists()
p
Out[*]: False
这同样适用于目录,如果给定的目录存在,该方法返回True
,如果不存在则返回False
。
Path().mkdir()在给定路径下创建一个新的目录:
In [*]: p = pathlib.Path('data')
directory = pathlib.Path('data/secrets')
directory.exists()
Out[*]: False
In [*]: directory.mkdir(parents = False, exist_ok = False)
directory.exists()
Out[*]: True
根据官方文档,.mkdir()
方法需要三个参数。我们现在只关注parents
和exist_ok
。
两个参数都默认设置为False
。parent
在缺少父目录的情况下会引发FileNotFound错误,而exist_ok
在给定目录已经存在的情况下会引发FileExists错误。
在上面的例子中,你可以将参数设置为True
,以忽略提到的错误并更新目录。
我们还可以使用Path().touch()
方法在给定的路径上创建一个新文件:
In [*]: file = pathlib.Path('data/secrets/secret_one.md')
file.exists()
Out[*]: False
In [*]: file.touch(exist_ok = False)
file.exists()
Out[*]: True
同样的逻辑也适用于.touch()
方法。在这里,exist_ok
可以被设置为True
,以忽略FileExists错误并更新文件。
Path().rename()重命名给定路径下的文件/目录。让我们用我们的目录/data
来举个例子。
In [*]: p = pathlib.Path('density.json')
n = pathlib.Path('density_2100.json')
p.rename(n)
Out[*]: PosixPath('density_2100.json')
如果你给该方法指定了一个不存在的文件,它会引发一个FileNotFound错误。这同样适用于目录。
Path().read_text()以字符串格式返回文件的内容:
In [*]: p = pathlib.Path('info.txt')
p.read_text()
Out[*]: 'some text added'
另外,你可以使用 **write_text()**
方法在文件中写入一个内容:
In [*]: p = pathlib.Path('file.txt')
p.write_text('we are building an empire')
Out[*]: 'we are building an empire'
注意,该.write_text()
方法已经被添加到Python 3.5中,并且最近在Python 3.10中被更新,增加了一些额外的参数。
重要说明
你可能会问自己为什么要使用Windows文件系统的路径--因为每一个软件包都应该与其他操作系统兼容,而不仅仅是Windows。
如果目标是做一个与操作系统无关的路径,你是对的。但是,有时由于一些Windows或Posix系统特有的设置,我们无法做到这一点。这就是为什么那些对象可用来帮助开发者处理这些用例。
有些包针对的是只存在于 Windows 生态系统中的问题,Python 在这个库中容纳了这些用例。
下一步是什么?
希望这个教程能够帮助你了解如何以及为什么要使用Pathlib,以及它对处理和操作文件系统路径的作用。
如果能把你所学到的东西玩一玩,并把东西变成一个真正的项目,那将是非常好的。如果你有任何问题,欢迎随时在LinkedIn上与我联系和打招呼。
另外,你可以看看我在YouTube上的频道,我在那里分享我学习和用代码构建的视频。
在下一个教程中见,并继续向前迈进!
参考资料
有很多东西需要了解。在这篇博文中,我介绍了在你的项目中使用Pathlib所需的基本知识。
官方文档强调了更多的方法和属性,你可以应用于你的文件系统路径。