原文地址: Why you should be using pathlib - Trey Hunner
当我刚知道 pathlib
的时候,我起初觉得它很不好用,因为已经有了 os.path
模块做相关的事,但后来证明我错了。pathlib
模块太酷了!
在这篇文章里,我希望能鼓舞你在 Python 代码中任何用到文件地方使用它。
- os.path 是笨拙的
- os 模块太拥挤了
- 不要忘记 glob 模块
- pathlib 让简单更简单
- PATH 类型让你的代码更明确
- pathlib 还缺少了什么
- 你应该使用 pathlib 吗
os.path 是笨拙的
os.path
模块已经深入到了 Python 代码里面。你可以用这个模块做到自己想要的,但有时会显得很笨拙。
你会喜欢下面这种?
import os.path
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATES_DIR = os.path.join(BASE_DIR, 'templates')
还是喜欢下面这种?
from os.path import abspath, dirname, join
BASE_DIR = dirname(dirname(abspath(__file__)))
TEMPLATES_DIR = join(BASE_DIR, 'templates')
或者你想对 join 函数重命名,那么就像是:
from os.path import abspath, dirname, join as joinpath
BASE_DIR = dirname(dirname(abspath(__file__)))
TEMPLATES_DIR = joinpath(BASE_DIR, 'templates')
我觉得这有点不方便,我们将字符串传递给返回字符串的函数,然后我们将其传递给返回字符串的其他函数。 所有这些字符串恰好代表路径,但它们仍然只是字符串。
os.path 模块里的这些 字符进-字符出 的函数在嵌套时非常难以处理,因为代码必须从里到外读取。如果我们可以将这些嵌套调用改为链式方法调用,那不是更好吗?
用 pathlib 模块可以做到。
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATES_DIR = BASE_DIR.joinpath('templates')
os.path 模块要求嵌套使用,但是 pathlib 模块的 path 类声明的Path对象可以允许我们链式调用方法或者属性。
我知道你在想什么:这些 Path 对象是一个对象而不是字符串。稍后我会进一步解释(注释:尽管它们也可以像字符串那样被使用)。
os 模块太拥挤了
Python 经典类 os.path 只能操作相关路径。如果你想操作一个路径(例如:创建一个目录),你需要使用其他的模块。
os 模块有大量操作文件和目录的工具:mkdir
,getcwd
,chmod
,stat
, remove
,rename
, 还有rmdir
。同时还有chdir
, link
, walk
, listdir
, makedirs
, renames
, removedirs
, unlink
(等同于 remove
), 和 symlink
。还有一堆和文件系统无关的东西:fork
, getenv
, putenv
, environ
, getlogin
, 和 system
。
python 的 os 模块做了一些事情;它是一种与系统相关的东西的集合。os 模块有很多有意思的东西,但是当你需要找到一些路径相关的操作就需要挖掘下。
pathlib 模块使用 Path 对象上的方法替换了许多与这些文件系统相关的 os 实用程序。
下面是一些代码,它们创建了一个 src/__pypackages__ 目录,并将我们的 .editorconfig 文件重命名为 src/.editorconfig。
import os
import os.path
os.makedirs(os.path.join('src', '__pypackages__'), exist_ok=True)
os.rename('.editorconfig', os.path.join('src', '.editorconfig'))
下面的代码通过 Path 对象实现:
from pathlib import Path
Path('src/__pypackages__').mkdir(parents=True, exist_ok=True)
Path('.editorconfig').rename('src/.editorconfig')
注意这段代码里,路径在第一位,因为方法是链式调用的。
os模块是一个非常大的命名空间,里面有很多东西。 pathlib.Path 类是一个比 os 模块小得多且更具体的命名空间。此 Path 命名空间中的方法返回 Path 对象,它允许方法链接而不是嵌套的 string-iful 函数调用。
不要忘记 glob 模块
os 和 os.path 模块不是 Python 标准库中唯一与文件路径/文件系统相关的实用程序。glob 模块是另一个方便的路径相关模块。
可以通过 glob.glob 方法查找精确的文件:
from glob import glob
top_level_csv_files = glob('*.csv')
all_csv_files = glob('**/*.csv', recursive=True)
pathlib 模块也有类似的方法:
from pathlib import Path
top_level_csv_files = Path.cwd().glob('*.csv')
all_csv_files = Path.cwd().rglob('*.csv')
pathlib 让简单更简单
pathlib模块使许多复杂的情况变得更简单,但它也使一些简单的情况更简单。
需要读取一个或者多个文本文件?
你可以使用 with 加上 open 函数实现文本内容的读取:
from glob import glob
file_contents = []
for filename in glob('**/*.py', recursive=True):
with open(filename) as python_file:
file_contents.append(python_file.read())
或者你可以使用 Path 对象的 read_text 方法来实现,就像是:
from pathlib import Path
file_contents = [
path.read_text()
for path in Path.cwd().rglob('*.py')
]
如果需要写入文件呢?
with open('.editorconfig') as config:
config.write('# config goes here')
Path('.editorconfig').write_text('# config goes here')
如果你更偏爱 open,那么可以:
from pathlib import Path
path = Path('.editorconfig')
with path.open(mode='wt') as config:
config.write('# config goes here')
如果是 Python 3.6,你可以直接将 Path 对象作为参数给 open 函数:
from pathlib import Path
path = Path('.editorconfig')
with open(path, mode='wt') as config:
config.write('# config goes here')
PATH 类型让你的代码更明确
下面的三个变量都指代了什么?它们的值相当于什么?
person = '{"name": "Trey Hunner", "location": "San Diego"}'
pycon_2019 = "2019-05-01"
home_directory = '/home/trey'
每一个变量都是字符串。
但它们有不一样:一个是json数据,一个是日期,一个是文件路径。
我们可以做一些小的变动使它们的指向更明确:
from datetime import date
from pathlib import Path
person = {"name": "Trey Hunner", "location": "San Diego"}
pycon_2019 = date(2019, 5, 1)
home_directory = Path('/home/trey')
JSON对象反序列化为字典,日期使用datetime.date对象本地表示,文件系统路径现在可以使用pathlib.Path对象进行泛型表示。
使用Path对象可以使您的代码更加清晰。如果您尝试表示日期,则可以使用日期对象。如果您尝试表示文件路径,则可以使用Path对象。
感谢PEP 519,文件路径对象现在成为使用路径的标准。从Python 3.6开始,内置的open函数和os,shutil和os.path模块中的各种函数都可以与pathlib.Path对象一起正常工作。您可以立即开始使用pathlib,而无需更改大多数适用于路径的代码!
pathlib 还缺少了什么
尽管 pathlib 足够优秀,它也并不是十全十美的。它还缺少一些我想要的特性。
我注意到的第一个差距是 pathlib.Path 对象缺少 shutil 等价物。
虽然可以将 Path 对象当作字符串给 shutil 函数来操作,但 Path 对象本身不包含这些函数。
因此要复制文件,您需要执行以下操作:
from pathlib import Path
from shutil import copyfile
source = Path('old_file.txt')
destination = Path('new_file.txt')
copyfile(source, destination)
还没有与os.chdir等效的pathlib。
这只是意味着如果您需要更改当前工作目录,则需要导入chdir:
from pathlib import Path
from os import chdir
parent = Path('..')
chdir(parent)
os.walk 函数也没有相应的 pathlib。 虽然你可以很容易地使用 pathlib 制作自己的类似遍历目录的函数。
我希望 pathlib.Path 对象最终可能包含一些缺失操作的方法。但即使有这些缺失的功能,我仍然发现使用 pathlib 比 os.path 更易于管理。
你应该使用 pathlib 吗
从 Python 3.6 开始,pathlib.Path 对象几乎可以在您使用路径字符串的任何地方工作。 因此,如果您使用的是 Python 3.6(或更高版本),我认为没有理由不使用 pathlib。
如果您使用的是 Python 3 的早期版本,则可以始终将您的 Path 对象包装在一个 str 调用中,以便在需要一个返回到字符串 land 的转义符号时从中获取一个字符串。 它很尴尬,但它有效:
from os import chdir
from pathlib import Path
chdir(Path('/home/trey')) # Works on Python 3.6+
chdir(str(Path('/home/trey'))) # Works on earlier versions also
无论您使用的是哪个版本的 Python 3,我都建议您尝试使用 pathlib。
如果你仍然坚持 Python 2(时钟正在滴答作响!)PyPI 上的第三方 pathlib2 模块是一个不错的方案,所以你可以在任何版本的 Python 上使用 pathlib。
我发现使用 pathlib 经常使我的代码更具可读性。 我的大多数使用文件的代码现在默认使用 pathlib,我建议你也这样做。