处理文件和目录路径

有关实用文件系统操作的深入探讨,请参阅我们的博客文章:每位开发者都应知道的 10 个基本文件系统操作

Python 中有两个主要模块处理路径操作。 一个是 os.path 模块,另一个是 pathlib 模块。

Pathlib 与 OS 模块

pathlib 提供了比上面列出的更多的功能,例如获取文件名、获取文件扩展名、在不手动打开的情况下读取/写入文件等。如果您打算了解更多信息,请参阅官方文档

Linux 和 Windows 路径

在 Windows 上,路径使用反斜杠 (\) 作为文件夹名称之间的分隔符。在基于 Unix 的操作系统(如 macOS、Linux 和 BSD)上,使用正斜杠 (/) 作为路径分隔符。如果您的代码需要在不同平台上运行,连接路径可能会很麻烦。

幸运的是,Python 的 pathlib 模块提供了一种简单的方法来处理这个问题。

在 *nix 上使用 pathlib

# pathlib.Path: 跨平台路径处理
from pathlib import Path

print(Path('usr').joinpath('bin').joinpath('spam'))  # 连接路径组件
usr/bin/spam

pathlib 还通过 / 运算符提供了 joinpath 的快捷方式:

# Path 运算符 (/): 连接路径的便捷方式(跨平台)
from pathlib import Path

print(Path('usr') / 'bin' / 'spam')  # 使用 / 运算符代替 joinpath()
usr/bin/spam

请注意,路径分隔符在 Windows 和基于 Unix 的操作系统之间是不同的,这就是您想要使用 pathlib 而不是将字符串连接起来以连接路径的原因。

测验

登录后即可答题并追踪学习进度

在 Python 中,使用 pathlib 连接路径的正确方法是什么?
A. Path('usr') + 'bin' + 'spam'
B. Path('usr') / 'bin' / 'spam'
C. Path('usr').join('bin').join('spam')
D. Path('usr/bin/spam')

连接路径在您需要在同一目录下创建不同文件路径时非常有用。

在 *nix 上使用 pathlib

# Path.home(): 获取用户的主目录,与文件名组合
my_files = ['accounts.txt', 'details.csv', 'invite.docx']
home = Path.home()  # 获取主目录路径
for filename in my_files:
    print(home / filename)  # 将主路径与每个文件名组合
/home/labex/project/accounts.txt
/home/labex/project/details.csv
/home/labex/project/invite.docx

展开用户主目录

使用 os.path.expanduser()~ 展开为用户的主目录:

import os.path

# 将 ~ 展开为用户的主目录
print(os.path.expanduser('~'))
/home/labex/project
# 展开 ~/Documents 为完整路径
print(os.path.expanduser('~/Documents'))
/home/labex/project/Documents
# 适用于包含 ~ 的路径
print(os.path.expanduser('~/myfile.txt'))
/home/labex/project/myfile.txt

当前工作目录

您可以使用 pathlib 获取当前工作目录:

# Path.cwd(): 获取当前工作目录
from pathlib import Path

print(Path.cwd())  # 以 Path 对象形式返回当前工作目录
/home/labex/project

创建新文件夹

在 *nix 上使用 pathlib

from pathlib import Path
cwd = Path.cwd()
(cwd / 'delicious' / 'walnut' / 'waffles').mkdir()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/pathlib.py", line 1226, in mkdir
    self._accessor.mkdir(self, mode)
  File "/usr/lib/python3.6/pathlib.py", line 387, in wrapped
    return strfunc(str(pathobj), *args)
FileNotFoundError: [Errno 2] No such file or directory: '/home/labex/project/delicious/walnut/waffles'

哦,糟了,我们遇到了一个棘手的错误!原因是 ‘delicious’ 目录不存在,所以我们无法在它下面创建 ‘walnut’ 和 ‘waffles’ 目录。要修复此问题,请执行以下操作:

# mkdir(parents=True): 如果需要,创建目录和所有父目录
from pathlib import Path
cwd = Path.cwd()
(cwd / 'delicious' / 'walnut' / 'waffles').mkdir(parents=True)  # 创建嵌套目录

一切就绪 :)

绝对路径与相对路径

有两种指定文件路径的方式。

  • 绝对路径,它总是以根文件夹开头
  • 相对路径,它相对于程序的当前工作目录

还有点 (.) 和点点 (..) 文件夹。它们不是真正的文件夹,而是可以在路径中使用的特殊名称。单个句点(“点”)表示“此目录”的简写。两个句点(“点点”)表示“父文件夹”。

处理绝对路径

要使用 pathlib 查看路径是否为绝对路径:

from pathlib import Path
Path('/').is_absolute()
True
Path('..').is_absolute()
False
测验

登录后即可答题并追踪学习进度

Path('/').is_absolute() 返回什么?
A. True
B. False
C. None
D. '/'

您可以使用 pathlib 提取绝对路径:

from pathlib import Path
print(Path.cwd())
/home/labex/project
print(Path('..').resolve())
/home

处理相对路径

您可以使用 pathlib 从起始路径获取到另一路径的相对路径:

from pathlib import Path
print(Path('/etc/passwd').relative_to('/'))
etc/passwd

路径和文件有效性

检查文件/目录是否存在

在 *nix 上使用 pathlib

from pathlib import Path

Path('.').exists()
True
Path('setup.py').exists()
True
Path('/etc').exists()
True
Path('nonexistentfile').exists()
False

检查路径是否为文件

在 *nix 上使用 pathlib

from pathlib import Path

Path('setup.py').is_file()
True
Path('/home').is_file()
False
Path('nonexistentfile').is_file()
False
测验

登录后即可答题并追踪学习进度

如果 setup.py 存在,Path('setup.py').is_file() 将返回什么?
A. 'setup.py'
B. False
C. True
D. None

检查路径是否为目录

在 *nix 上使用 pathlib

from pathlib import Path

Path('/').is_dir()
True
Path('setup.py').is_dir()
False
Path('/spam').is_dir()
False

获取文件大小(以字节为单位)

在 *nix 上使用 pathlib

from pathlib import Path

stat = Path('/bin/python3.6').stat()
print(stat) # stat 还包含有关文件的其他信息
os.stat_result(st_mode=33261, st_ino=141087, st_dev=2051, st_nlink=2, st_uid=0,
--snip--
st_gid=0, st_size=10024, st_atime=1517725562, st_mtime=1515119809, st_ctime=1517261276)
print(stat.st_size) # 以字节为单位的大小
10024

列出目录

在 *nix 上使用 pathlib 列出目录内容:

from pathlib import Path

for f in Path('/usr/bin').iterdir():
    print(f)
...
/usr/bin/tiff2rgba
/usr/bin/iconv
/usr/bin/ldd
/usr/bin/cache_restore
/usr/bin/udiskie
/usr/bin/unix2dos
/usr/bin/t1reencode
/usr/bin/epstopdf
/usr/bin/idle3
...

目录文件大小

警告

目录本身也有大小!因此,您可能需要使用上面讨论的方法中的方法来检查路径是文件还是目录。

在 *nix 上使用 pathlib

from pathlib import Path

total_size = 0
for sub_path in Path('/usr/bin').iterdir():
    total_size += sub_path.stat().st_size

print(total_size)
1903178911

复制文件和文件夹

shutil 模块提供了用于复制文件以及整个文件夹的函数。

import shutil

shutil.copy('/tmp/spam.txt', '/tmp/delicious')
/tmp/delicious/spam.txt
shutil.copy('/tmp/eggs.txt', '/tmp/delicious/eggs2.txt')
/tmp/delicious/eggs2.txt
测验

登录后即可答题并追踪学习进度

您应该使用哪个函数来复制整个目录树,包括所有子目录和文件?
A. shutil.copy()
B. Path.copy()
C. os.copy()
D. shutil.copytree()

虽然 shutil.copy() 会复制单个文件,但 shutil.copytree() 会复制整个文件夹及其包含的所有文件夹和文件:

import shutil

shutil.copytree('/tmp/bacon', '/tmp/bacon_backup')
/tmp/bacon_backup

移动和重命名

import shutil

shutil.move('/tmp/bacon.txt', '/tmp/eggs')
/tmp/eggs/bacon.txt

目标路径也可以指定文件名。在以下示例中,源文件被移动并重命名:

shutil.move('/tmp/bacon.txt', '/tmp/eggs/new_bacon.txt')
/tmp/eggs/new_bacon.txt

如果不存在 eggs 文件夹,则 move() 会将 bacon.txt 重命名为名为 eggs 的文件:

shutil.move('/tmp/bacon.txt', '/tmp/eggs')
/tmp/eggs

删除文件和文件夹

  • 调用 Path.unlink() 将删除路径处的文件
  • 调用 Path.rmdir() 将删除路径处的文件夹。此文件夹必须为空,不包含任何文件或文件夹。
  • 调用 shutil.rmtree(path) 将删除路径处的文件夹,以及其中包含的所有文件和文件夹。
测验

登录后即可答题并追踪学习进度

哪个方法可以删除非空目录及其所有内容?
A. Path.rmdir()
B. shutil.rmtree()
C. Path.unlink()
D. os.remove()

遍历目录树

Path 对象有一个 rglob() 方法,用于递归迭代文件和目录。

from pathlib import Path

p = Path('/tmp/delicious')
for i in p.rglob('*'):
    print(i)
/tmp/delicious/cats
/tmp/delicious/walnut
/tmp/delicious/spam.txt
/tmp/delicious/cats/catnames.txt
/tmp/delicious/cats/zophie.jpg
/tmp/delicious/walnut/waffles
/tmp/delicious/walnut/waffles/butter.txt

相关链接