目录
Exploring the Contents of a Directory
Files and File Systems
在计算机中,文件是通过数据结构和接口系统来管理的,这些系统处理文件的物理和逻辑组织。虽然文件和它们封装的数据被设计用于在各种不同的文件系统上工作,但是文件系统对于计算机的操作环境来说是独一无二的。这意味着,虽然你和我都能够打开和使用.py文件(或任何文件),但管理该文件的文件系统可能是完全不同的。
尽管现在已经创建和使用了许多不同类型的文件系统,但大多数现代计算机操作系统都运行在两种类型中的一种上:Windows或Posix。如您所料,Windows是运行Microsoft Windows操作系统的计算机的规范。Posix用于类unix系统,其中包括Linux和macOS。要在Python文件系统中处理文件,我们需要在如何编写代码方面做出一些有意识的决定。我们将很快了解这些决策的本质,但是现在,让我们运行一个快速测试,看看我们使用的是哪种类型的文件系统。
打开Python脚本,输入如下代码:
>>> from pathlib import Path
>>> p = Path(".")
>>> p
第二行在当前目录实例化一个Path对象。
如果我们在运行Microsoft Windows的计算机上从Python shell运行这段代码,我们将看到以下输出:
WindowsPath('.')
如果我们在类unix系统(例如,macOS)上运行它,我们会看到:
PosixPath('.')
如果您使用的是Microsoft Windows系统,请运行以下代码:
>>> import pathlib
>>> p = pathlib.PosixPath(".")
>>> p
如果您使用的是macOS或Linux系统,请运行以下代码:
>>> import pathlib
>>> p = pathlib.WindowsPath(".")
>>> p
发生了什么事?你应该已经收到了一个类似于以下的traceback(输出时,在Linux系统上运行WindowsPath):
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
p = pathlib.WindowsPath('.')
File "/usr/lib64/python3.10/pathlib.py", line 960, in __new__
raise NotImplementedError("cannot instantiate %r on your system"
NotImplementedError: cannot instantiate 'WindowsPath' on your system
这告诉我们,我们不能在Posix系统上执行类似windows的操作,反之亦然。那么,为什么第一个例子不管操作系统如何都能工作呢?好吧,谢天谢地,Python的abstracts(又出现了那个词!)为我们消除了这种复杂性,并将其封装在一个名为Path的对象中。
这告诉我们,我们不能在Posix系统上执行类似windows的操作,反之亦然。那么,为什么第一个例子不管操作系统如何都能工作呢?好吧,谢天谢地,Python的abstracts(又出现了那个词!)为我们消除了这种复杂性,并将其封装在一个名为Path的对象中。
看这张图,我们可以看到PosixPath和WindowsPath都是通用的Path类型。当我们在代码中使用Path对象时,它会确定它正在访问的文件系统类型,并自动实现正确的xPath对象。
注意
在这门课中,我们不会关心pathlib模块的pure实例。如果你好奇,你可以阅读上面Python文档的链接。目前,最简单的方法是将pure版本看作是实现xPath对象的一组规则或规范,而non-pure版本则是我们可以在程序中使用的具体实现。
很多情况下,直接与文件系统结合需要用到Path。所以我们来看看Path对象能做的一些事。
我们目前所看到的代码片段实例化或创造了一个有目录参数的Path对象。
#files_demo.py
from pathlib import Path
p1 = Path()
p2 = Path('.')
# or
p3 = Path('c:\\users\\marks')
在上面的代码片段中,p1和p2是相同的。如果你没有为Path提供参数,它将默认为当前目录(例如,' . ')。p3是实例化路径到特定目录的示例。那么,你所指定的目录将是你赋予Path对象给变量工作的目录。
Exploring the Contents of a Directory
使用Path对象时要执行的最常见任务之一是查看目录的内容。我们可以使用iterdir函数来帮助我们完成这个任务:
#files_demo.py
from pathlib import Path
p = Path('.') # we'll work with current directory, but feel free to add your preferred directory here.
for obj in p.iterdir():
print(obj)
运行此代码将为用于创建p变量的目录中的每一项打印一个Path对象,该对象存储在obj变量中。由于每个计算机系统的输出不同,所以结果也会不同,但是您应该希望看到当前工作目录中的文件和目录的名称。如果没有输出,那么请确保在当前工作目录中有文件对象,或者在创建Path对象时使用不同的目录。
Iterdir将遍历目录中的所有对象,包括文件和目录。因此,如果我们想了解Path对象的类型,有两个函数可以提供帮助:is_file和is_dir。让我们看看它们是如何工作的。
files = 0
dirs = 0
for obj in p.iterdir():
if obj.is_file():
files = files + 1
elif obj.is_dir():
dirs = dirs + 1
print(f"There are {files} files and {dirs} in {p})
这两个函数都将返回一个布尔值,指示Path对象是表示文件还是目录。Path对象还有更多的函数和属性可以帮助理解它所表示的数据。我们在这里不会详细介绍所有这些方法,因为它们或多或少都是通过相同的行为运行的。相反,你应该访问Python文档来了解更多。但是,在我们继续之前,让我们看一下一些对我们在这门课中要做的工作最有用的性质。
p1 = Path(".") / "temp_dir"
if not p1.exists(): # check to see if temp_dir exists
p1.mkdir() # if temp_dir does not exist, create it
p1 = p1 / "temp_file.txt" # append a file name to the current path
if not p1.exists(): # check to see if temp_file.txt exists
p1.touch() # if temp_file.txt does not exist, create it
print(p1.name) # get the name of the object (file name or directory name)
print(p1.suffix) # get the suffix (.py, .doc, etc) of the file object
在上面的代码中,我们有一个简单的程序,它首先将文件系统路径从当前工作目录(" . ")更改为位于当前工作目录中的一个名为temp_dir的目录。对于更新路径,它然后通过调用exists函数来检查temp_dir是否存在。如果它不存在,则调用mkdir函数来创建新目录。下一行执行类似的任务,创建文件temp_file.txt。对于更新后的路径,它通过调用exists函数来检查temp_file.txt是否实际存在。如果它不存在,那么touch()函数就会被调用来创建它。最后,我们打印路径对象的两个属性,名称和后缀,以了解关于文件的更多信息。继续在您的系统上运行这段代码,但是在此之前,您认为最后两个print语句将打印出什么?
还有一件事,请注意在实例化Path对象和文件名(和目录名)字符串之间使用正斜杠、/。这是joinpath函数的语法简写。让我们更新上面程序的第4行来使用joinpath:
p1 = p1.joinpath("temp_file.txt") # append a file name to the current path
这两种方法都完成了相同的任务,但是您可能会认为使用/符号会减少一些工作。这两种方法都可以工作,并确保当您组合路径时,它们可以安全地在Windows和Posix系统上工作。您应该不惜一切代价避免在路径上使用字符串连接。例如,以下操作可能在你的计算机上工作(如果你在Windows上):
p1 = Path(".") + "//" + "temp_file.txt" # append a file name to the current path
但是它不能在Posix系统上工作,这就消除了使用Path对象的所有好处!
现在我们已经知道了如何定位、创建和了解文件和目录,接下来我们来讨论如何处理我们找到和创建的文件。
Working with Files
在编写处理文件的Python程序时,您可能需要能够在文件中读写信息。要读或写,我们必须首先打开正在用于我们工作的文件。幸运的是,Path对象通过在文件对象上提供一个open函数,使这一过程相当简单。让我们继续扩展上一节的小程序:
p1 = Path(".") / "temp_dir" / "temp_file.txt"
if p1.exists(): # check to see if temp_file exists
f = p1.open()
print(f.readline())
f.close()
这个非常简单的程序打开变量p1中由Path对象表示的文件,并将打开的文件赋给变量f。然后读取文件中的第一行,并将内容打印到shell中。最后,当所有操作完成后,通过调用文件对象上的close函数来关闭文件对象。
如果运行这段代码,您会发现实际上什么也没有打印出来!假设您已经按照下面的步骤做了,那么您的temp_file.txt文件中还没有任何信息。所以让我们添加一些:
p1 = Path(".") / "temp_dir" / "temp_file.txt"
if p1.exists(): # check to see if temp_file exists
f = p1.open('w')
f.write("Hello New File!")
f.close()
f = p1.open('r')
print(f.readline())
f.close()
上面修改过的程序现在将打印使用write命令添加的文件的内容。您可能已经注意到,在这个程序的最终版本中,open函数中增加了字母w和r。“w”表示您打算对打开的文件执行写操作,而“r”表示您打算执行读操作。由于open默认为读取模式,如果您只打算执行读取操作,那么没有必要使用“r”。
注意:
以写模式打开文件的一个后果是,文件中的所有现有内容都将被删除。如果你计划将内容追加到一个现有的文件,那么在打开一个文件时,你需要使用" a "参数选项:f = p1.open('a')
好了,这就是文件和文件系统!这里讨论的代码应该可以为您提供组装一个在文件系统中遍历、读取、写入和创建文件和目录的程序所需的几乎所有知识。有一些操作我们故意没有提到(例如,删除操作)。本课程将为您提供足够的构建模块,当与pathlib模块的Python文档相结合时,将允许您自行识别和实现任何剩余的任务。