求求你,别用 Os.Path 了

开发 开发工具
学习了一番 pathlib 之后,发现这是一个非常高效便捷的工具,用它来处理文件系统路径相关的操作最合适不过,集成了很多快捷的功能,提升你的编程效率,那是妥妥的。

 [[398653]]

前段时间,在使用新版本的 Django 时,我发现了 settings.py 的第一行代码从

import os 
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 
  • 1.
  • 2.

变成了

from pathlib import Path 
BASE_DIR = Path(__file__).resolve().parent.parent 
  • 1.
  • 2.

于是我就好奇,os 和 pathlib 同样是标准库,为什么 pathlib 得到了 Django 的青睐?学习了一番 pathlib 之后,发现这是一个非常高效便捷的工具,用它来处理文件系统路径相关的操作最合适不过,集成了很多快捷的功能,提升你的编程效率,那是妥妥的。

接下来让一起看一下,为什么 pathlib 更值得我们使用。

pathlib vs os

话不多说,先看下使用对比:比如说

打印当前的路径:

使用 os:

In [13]: import os 
 
In [14]: os.getcwd() 
Out[14]: '/Users/aaron' 
  • 1.
  • 2.
  • 3.
  • 4.

使用 pathlib:

In [15]: from pathlib import Path 
 
In [16]: Path.cwd() 
Out[16]: PosixPath('/Users/aaron'
In [17]: print(Path.cwd()) 
/Users/aaron 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

使用 print 打印的结果是一样的,但 os.getcwd() 返回的是字符串,而 Path.cwd() 返回的是 PosixPath 类,你还可以对此路径进行后续的操作,会很方便。

判断路径是否存在:

使用 os:

In [18]: os.path.exists("/Users/aaron/tmp"
 
Out[18]: True 
  • 1.
  • 2.
  • 3.

使用 pathlib:

In [21]: tmp = Path("/Users/aaron/tmp"
 
In [22]: tmp.exists() 
Out[22]: True 
  • 1.
  • 2.
  • 3.
  • 4.

可以看出 pathlib 更易读,更面向对象。

显示文件夹的内容

In [38]: os.listdir("/Users/aaron/tmp"
Out[38]: ['.DS_Store''.hypothesis''b.txt''a.txt''c.py''.ipynb_checkpoints'
 
In [39]: tmp.iterdir() 
Out[39]: <generator object Path.iterdir at 0x7fa3f20d95f0> 
 
In [40]: list(tmp.iterdir()) 
Out[40]: 
[PosixPath('/Users/aaron/tmp/.DS_Store'), 
 PosixPath('/Users/aaron/tmp/.hypothesis'), 
 PosixPath('/Users/aaron/tmp/b.txt'), 
 PosixPath('/Users/aaron/tmp/a.txt'), 
 PosixPath('/Users/aaron/tmp/c.py'), 
 PosixPath('/Users/aaron/tmp/.ipynb_checkpoints')] 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

可以看出 Path().iterdir 返回的是一个生成器,这在目录内文件特别多的时候可以大大节省内存,提升效率。

通配符支持

os 不支持含有通配符的路径,但 pathlib 可以:

In [45]: list(Path("/Users/aaron/tmp").glob("*.txt")) 
Out[45]: [PosixPath('/Users/aaron/tmp/b.txt'), PosixPath('/Users/aaron/tmp/a.txt')] 
  • 1.
  • 2.

便捷的读写文件操作

这是 pathlib 特有的:

f = Path('test_dir/test.txt')) 
f.write_text('This is a sentence.'
f.read_text() 
  • 1.
  • 2.
  • 3.

也可以使用 with 语句:

>>> p = Path('setup.py'
>>> with p.open() as f: f.readline() 
... 
'#!/usr/bin/env python3\n' 
  • 1.
  • 2.
  • 3.
  • 4.

获取文件的元数据

In [56]: p = Path("/Users/aaron/tmp/c.py"
 
In [57]: p.stat() 
Out[57]: os.stat_result(st_mode=33188, st_ino=35768389, st_dev=16777221, st_nlink=1, st_uid=501, st_gid=20, st_size=20, st_atime=1620633580, st_mtime=1620633578, st_ctime=1620633578) 
 
In [58]: p.parts 
Out[58]: ('/''Users''aaron''tmp''c.py'
 
In [59]: p.parent 
Out[59]: PosixPath('/Users/aaron/tmp'
 
In [60]: p.resolve() 
Out[60]: PosixPath('/Users/aaron/tmp/c.py'
 
In [61]: p.exists() 
Out[61]: True 
 
In [62]: p.is_dir() 
Out[62]: False 
 
In [63]: p.is_file() 
Out[63]: True 
 
In [64]: p.owner() 
Out[64]: 'aaron' 
 
In [65]: p.group() 
Out[65]: 'staff' 
 
In [66]: p.name 
Out[66]: 'c.py' 
 
In [67]: p.suffix 
Out[67]: '.py' 
 
In [68]: p.suffixes 
Out[68]: ['.py'
 
In [69]: p.stem 
Out[69]: 'c' 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.

路径的连接 join

相比 os.path.join,使用一个 / 是不是更为直观和便捷?

>>> p = PurePosixPath('foo'
>>> p / 'bar' 
PurePosixPath('foo/bar'
>>> p / PurePosixPath('bar'
PurePosixPath('foo/bar'
>>> 'bar' / p 
PurePosixPath('bar/foo'
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

当然,也可以使用 joinpath 方法

>>> PurePosixPath('/etc').joinpath('passwd'
PurePosixPath('/etc/passwd'
>>> PurePosixPath('/etc').joinpath(PurePosixPath('passwd')) 
PurePosixPath('/etc/passwd'
>>> PurePosixPath('/etc').joinpath('init.d''apache2'
PurePosixPath('/etc/init.d/apache2'
>>> PureWindowsPath('c:').joinpath('/Program Files'
PureWindowsPath('c:/Program Files'
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

路径匹配

>>> PurePath('a/b.py').match('*.py'
True 
>>> PurePath('/a/b/c.py').match('b/*.py'
True 
>>> PurePath('/a/b/c.py').match('a/*.py'
False 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

pathlib 出现的背景和要解决的问题

pathlib 目的是提供一个简单的类层次结构来处理文件系统的路径,同时提供路径相关的常见操作。那为什么不使用 os 模块或者 os.path 来实现呢?

许多人更喜欢使用 datetime 模块提供的高级对象来处理日期和时间,而不是使用数字时间戳和 time 模块 API。同样的原因,假如使用专用类表示文件系统路径,也会更受欢迎。

换句话说,os.path 是面向过程风格的,而 pathlib 是面向对象风格的。Python 也在一直在慢慢地从复制 C 语言的 API 转变为围绕各种常见功能提供更好,更有用的抽象。

其他方面,使用专用的类处理特定的需求也是很有必要的,例如 Windows 路径不区分大小写。

在这样的背景下,pathlib 在 Python 3.4 版本加入标准库。

pathlib 的优势和劣势分别是什么

pathlib 的优势在于考虑了 Windows 路径的特殊性,同时提供了带 I/O 操作的和不带 I/O 操作的类,使用场景更加明确,API 调用更加易懂。

先看下 pathlib 对类的划分:

图中的箭头表示继承自,比如 Path 继承自 PurePath,PurePath 表示纯路径类,只提供路径常见的操作,但不包括实际 I/O 操作,相对安全;Path 包含 PurePath 的全部功能,包括 I/O 操作。

PurePath 有两个子类,一个是 PureWindowsPath,表示 Windows 下的路径,不区分大小写,另一个是 PurePosixPath,表示其他系统的路径。有了 PureWindowsPath,你可以这样对路径进行比较:

from pathlib import PureWindowsPath 
>>> PureWindowsPath('a') == PureWindowsPath('A'
True 
  • 1.
  • 2.
  • 3.

PurePath 可以在任何操作系统上实例化,也就是说与平台无关,你可以在 unix 系统上使用 PureWindowsPath,也可以在 Windows 系统上使用 PurePosixPath,他们还可以相互比较。

>>> from pathlib import PurePosixPath, PureWindowsPath, PosixPath   
>>> PurePosixPath('a') == PurePosixPath('b'
False 
>>> PurePosixPath('a') < PurePosixPath('b'
True 
>>> PurePosixPath('a') == PosixPath('a'
True 
>>> PurePosixPath('a') == PureWindowsPath('a'
False 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

可以看出,同一个类可以相互比较,不同的类比较的结果是 False。

相反,包含 I/O 操作的类 PosixPath 及 WindowsPath 只能在对应的平台实例化:

In [8]: from pathlib import PosixPath,WindowsPath 
 
In [9]: PosixPath('a'
Out[9]: PosixPath('a'
 
In [10]: WindowsPath('a'
--------------------------------------------------------------------------- 
NotImplementedError                       Traceback (most recent call last
<ipython-input-10-cc7a0d86d4ed> in <module> 
----> 1 WindowsPath('a') 
 
/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/pathlib.py in __new__(cls, *args, **kwargs) 
   1038         self = cls._from_parts(args, init=False
   1039         if not self._flavour.is_supported: 
-> 1040             raise NotImplementedError("cannot instantiate %r on your system" 
   1041                                       % (cls.__name__,)) 
   1042         self._init() 
 
NotImplementedError: cannot instantiate 'WindowsPath' on your system 
 
In [11]: 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

要说劣势,如果有的话,那就是在选择类时会比较困惑,到底用哪一个呢?其实如果你不太确定的话,用 Path 就可以了,这也是它的名称最短的原因,因为更加常用,短点的名称编写的更快。

适用的场景

如果要处理文件系统相关的操作,选 pathlib 就对了。

一些关键点

获取家目录:

In [70]: from pathlib import Path 
 
In [71]: Path.home() 
Out[71]: PosixPath('/Users/aaron'
  • 1.
  • 2.
  • 3.
  • 4.

父目录的层级获取:

>>> p = PureWindowsPath('c:/foo/bar/setup.py'
>>> p.parents[0] 
PureWindowsPath('c:/foo/bar'
>>> p.parents[1] 
PureWindowsPath('c:/foo'
>>> p.parents[2] 
PureWindowsPath('c:/'
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

获取多个文件后缀:

>>> PurePosixPath('my/library.tar.gar').suffixes 
['.tar''.gar'
>>> PurePosixPath('my/library.tar.gz').suffixes 
['.tar''.gz'
>>> PurePosixPath('my/library').suffixes 
[] 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

Windows 风格转 Posix:

>>> p = PureWindowsPath('c:\\windows'
>>> str(p) 
'c:\\windows' 
>>> p.as_posix() 
'c:/windows' 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

获取文件的 uri:

>>> p = PurePosixPath('/etc/passwd'
>>> p.as_uri() 
'file:///etc/passwd' 
>>> p = PureWindowsPath('c:/Windows'
>>> p.as_uri() 
'file:///c:/Windows' 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

判断是否绝对路径:

>>> PurePosixPath('/a/b').is_absolute() 
True 
>>> PurePosixPath('a/b').is_absolute() 
False 
 
>>> PureWindowsPath('c:/a/b').is_absolute() 
True 
>>> PureWindowsPath('/a/b').is_absolute() 
False 
>>> PureWindowsPath('c:').is_absolute() 
False 
>>> PureWindowsPath('//some/share').is_absolute() 
True 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

文件名若有变化:

>>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz'
>>> p.with_name('setup.py'
PureWindowsPath('c:/Downloads/setup.py'
  • 1.
  • 2.
  • 3.

是不是非常方便?

技术的底层原理和关键实现

pathlib 并不是基于 str 的实现,而是基于 object 设计的,这样就严格地区分了 Path 对象和字符串对象,同时也用到了一点 os 的功能,比如 os.name,os.getcwd 等,这一点大家可以看 pathlib 的源码了解更多。

最后的话

本文分享了 pathlib 的用法,后面要处理路径相关的操作时,你应该第一时间想到 pathlib,不会用没有关系,搜索引擎所搜索 pathlib 就可以看到具体的使用方法。

虽然 pathlib 比 os 库更高级,更方便并且提供了很多便捷的功能,但是我们仍然需要知道如何使用 os 库,因为 os 库是 Python 中功能最强大且最基本的库之一,但是,在需要一些文件系统操作时,强烈建议使用 pathlib。

本文转载自微信公众号「Python七号」,可以通过以下二维码关注。转载本文请联系Python七号公众号。

 

责任编辑:武晓燕 来源: Python七号
相关推荐

2020-12-11 09:24:19

Elasticsear存储数据

2023-11-13 22:47:58

PythonPathlib

2020-12-15 08:06:45

waitnotifyCondition

2021-09-24 09:30:05

os.path模块Python

2020-06-15 08:12:51

try catch代码处理器

2022-10-27 21:34:28

数据库机器学习架构

2020-09-22 09:05:45

MySQLUTF-8utf8mb4

2025-02-10 08:05:03

2020-11-09 08:22:29

程序员 IT科技

2010-03-25 12:50:45

Python代码

2020-10-12 10:45:44

nullava程序员

2020-05-09 10:18:31

Java开源工具

2024-04-29 08:32:21

os.path模块Python内置函数

2023-12-08 14:37:51

接口jar包开发

2020-04-08 17:53:40

TypeScriptJavaScript代码

2024-03-14 08:15:18

COUNT(*)数据库LIMIT 1​

2021-09-30 06:13:36

打印日志error

2020-12-04 10:05:00

Pythonprint代码

2020-12-02 11:18:50

print调试代码Python

2021-03-05 22:57:25

递归闭包 Python
点赞
收藏

51CTO技术栈公众号