Python Path - 如何使用Pathlib模块及实例

154 阅读12分钟

每个操作系统都有不同的文件路径构建规则。例如,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 PathConcrete Path

pathlib-diagram

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() 方法需要三个参数。我们现在只关注parentsexist_ok

两个参数都默认设置为Falseparent 在缺少父目录的情况下会引发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所需的基本知识。

官方文档强调了更多的方法和属性,你可以应用于你的文件系统路径。