別用 os.path 了!

2021-12-18 Python開發者

前段時間,在使用新版本的 Django 時,我發現了 settings.py 的第一行代碼從

import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

變成了

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

於是我就好奇,os 和 pathlib 同樣是標準庫,為什麼 pathlib 得到了 Django 的青睞?學習了一番 pathlib 之後,發現這是一個非常高效便捷的工具,用它來處理文件系統路徑相關的操作最合適不過,集成了很多快捷的功能,提升你的編程效率,那是妥妥的。

接下來讓一起看一下,為什麼 pathlib 更值得我們使用。

pathlib vs os

話不多說,先看下使用對比:比如說

使用 os:

In [13]: import os

In [14]: os.getcwd()
Out[14]: '/Users/aaron'

使用 pathlib:

In [15]: from pathlib import Path

In [16]: Path.cwd()
Out[16]: PosixPath('/Users/aaron')
In [17]: print(Path.cwd())
/Users/aaron

使用 print 列印的結果是一樣的,但 os.getcwd() 返回的是字符串,而 Path.cwd() 返回的是 PosixPath 類,你還可以對此路徑進行後續的操作,會很方便。

使用 os:

In [18]: os.path.exists("/Users/aaron/tmp")
Out[18]: True

使用 pathlib:

In [21]: tmp = Path("/Users/aaron/tmp")

In [22]: tmp.exists()
Out[22]: True

可以看出 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')]

可以看出 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')]

這是 pathlib 特有的:

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

也可以使用 with 語句:

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

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'

相比 os.path.join,使用一個 / 是不是更為直觀和便捷?

>>> p = PurePosixPath('foo')
>>> p / 'bar'
PurePosixPath('foo/bar')
>>> p / PurePosixPath('bar')
PurePosixPath('foo/bar')
>>> 'bar' / p
PurePosixPath('bar/foo')

當然,也可以使用 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')

>>> 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

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

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

可以看出,同一個類可以相互比較,不同的類比較的結果是 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]:

要說劣勢,如果有的話,那就是在選擇類時會比較困惑,到底用哪一個呢?其實如果你不太確定的話,用 Path 就可以了,這也是它的名稱最短的原因,因為更加常用,短點的名稱編寫的更快。

適用的場景

如果要處理文件系統相關的操作,選 pathlib 就對了。

一些關鍵點

獲取家目錄:

In [70]: from pathlib import Path

In [71]: Path.home()
Out[71]: PosixPath('/Users/aaron')

父目錄的層級獲取:

>>> 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:/')

獲取多個文件後綴:

>>> PurePosixPath('my/library.tar.gar').suffixes
['.tar', '.gar']
>>> PurePosixPath('my/library.tar.gz').suffixes
['.tar', '.gz']
>>> PurePosixPath('my/library').suffixes
[]


Windows 風格轉 Posix:

>>> p = PureWindowsPath('c:\\windows')
>>> str(p)
'c:\\windows'
>>> p.as_posix()
'c:/windows'

獲取文件的 uri:

>>> p = PurePosixPath('/etc/passwd')
>>> p.as_uri()
'file:///etc/passwd'
>>> p = PureWindowsPath('c:/Windows')
>>> p.as_uri()
'file:///c:/Windows'

判斷是否絕對路徑:

>>> 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

文件名若有變化:

>>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz')
>>> p.with_name('setup.py')
PureWindowsPath('c:/Downloads/setup.py')

是不是非常方便?

技術的底層原理和關鍵實現

pathlib 並不是基於 str 的實現,而是基於 object 設計的,這樣就嚴格地區分了 Path 對象和字符串對象,同時也用到了一點 os 的功能,比如 os.name,os.getcwd 等,這一點大家可以看 pathlib 的源碼了解更多。

最後的話

本文分享了 pathlib 的用法,後面要處理路徑相關的操作時,你應該第一時間想到 pathlib,不會用沒有關係,搜尋引擎所搜索 pathlib 就可以看到具體的使用方法。

雖然 pathlib 比 os 庫更高級,更方便並且提供了很多便捷的功能,但是我們仍然需要知道如何使用 os 庫,因為 os 庫是 Python 中功能最強大且最基本的庫之一,但是,在需要一些文件系統操作時,強烈建議使用 pathlib。

- EOF -

覺得本文對你有幫助?請分享給更多人

推薦關注「Python開發者」,提升Python技能

點讚和在看就是最大的支持❤️

相關焦點

  • 簡單介紹 os.path 模塊常用方法
    os.path.png1、os.path.abspath獲取文件的絕對路徑path = "test.py"print(os.path.abspath(path)) # C:\Users\lzjun\workspace\python_scripts\test.py
  • pathlib一個優雅的路徑操作模塊,碾壓os!
    對於 Python 中的路徑操作,大多數人第一反應肯定是 os,可以說 os 已經在老一輩 Python coder 心中扎深蒂固,現存的很多教程中操作路徑仍然使用 os,殊不知從 python3.4 開始,pathlib 正式成為標準庫,已經能夠完全取代 os。無論從功能還是易用性都已經超越 os。
  • Python|模塊os
    1、os的基本命令import os #列印當前工作路徑os.getcwd()#創建目錄os.mkdir()#返回指定目錄下的所有文件和目錄名os.listdir(path)#運行shell命令os.system(command)#改變工作目錄到diros.chdir(dir)#檢驗給出的路徑是否一個文件
  • Python內置模塊os介紹
    # 內置模塊# Python語言強大的地方就在於它擁有非常強大的模塊系統# 使用只需要用 import 導入即可
  • python管理文件神器 os.walk
    其實是可以的,因為python已經為你準備好了神器os.walk,進來看看吧!python中os.walk是一個簡單易用的文件、目錄遍歷器,可以幫助我們高效的處理文件、目錄方面的事情。本文將詳細介紹os.walk模塊,最後使用os.walk模塊實現一個在指定日誌整理文件的程序。
  • Python管理文件神器 os.walk
    大家好,我是Python人工智慧技術【導語】:有沒有想過用python寫一個文件管理程序?聽起來似乎沒思路?其實是可以的,因為Python已經為你準備好了神器os.walk,進來看看吧!Python中os.walk是一個簡單易用的文件、目錄遍歷器,可以幫助我們高效的處理文件、目錄方面的事情。本文將詳細介紹os.walk模塊,最後使用os.walk模塊實現一個在指定日誌整理文件的程序。1、基本介紹os.walk():掃描某個指定目錄下所包含的子目錄和文件,返回的是一個迭代器。
  • os用法總結:python中必須掌握的內置模塊os,實現與計算機作業系統的常規交互!
    os模塊說明1'''2os 模塊3Os庫是python標準庫,包含幾百個函數4常用路徑操作、進程管理、環境參數等幾類5路徑操作:os.path子庫,處理文件路徑及信息6進程管理:啟動系統中其他程序7環境參數:獲得系統軟體硬體信息等環境參數8'''路徑操作 1import os.path as path
  • Python模塊 | 這份os模塊知識詳解,太牛逼!
    本著只講最有用的態度,下方我將os模塊中一些我常用的的方法,給大家詳細列舉出來了,希望減少大家的學習負擔。os模塊\test_os模塊"os.listdir(path)結果如下:因此創建文件夾之前,需要使用os.path.exists(path)函數判斷文件夾是否存在;os.getcwd()path1 = os.getcwd()+"\\huang_wei"os.mkdir(path1)結果如下:
  • Python基礎之模塊之os模塊
    os.path.abspath(path)#顯示當前絕對路徑os.path.dirname(path)#返回該路徑的父目錄os.path.join(path,name)  #連接目錄與文件名或目錄 結果為path/name
  • Go os/exec, 你了解多少?
    os/exec包裝了os.StartProcess方法,更方便的進行輸入和輸出的訪問,提供I/O pipe等功能。我會通過兩篇文章專門介紹Go啟動新進程,執行外部命令的方法,這是第一篇,專門介紹os/exec庫。os/exec庫提供方法和POSIX標準中定義的C語言的方法)類似,但是提供了進一步的封裝,更方便使用。接下來我會一一給你介紹。1.
  • 整理了十個Python自動化操作,拿走就用!
    給定一個目標路徑 path ,通過一行代碼就能夠判斷這是文件還是文件夾路徑import ospath = 'xxx'print(os.path.isfile(path))三、獲取路徑中的文件名oos.path.basename 可以直接從絕對路徑中獲取最後的文件名,當然如果用傳統的字符串切割方式也可以,即 path.split('\\')[-1]import ospath = 'xxx'print(os.path.basename)四、創建文件夾
  • python應用(2):寫個python程序給自己用
    然而很多人,特別是不懂程序的人,更需要看到的是一個有界面的,能通過滑鼠操作的程序,畢竟已經邁進「窗口」的時代,雖然Dos還存在。所以,怎麼用python弄出有界面的程序呢?界面也是一個工具,所以,有沒有界面工具庫呢?
  • 別這樣直接運行python命令,否則電腦等於「裸奔」!
    原因當然是Python簡明易用的腳本語法,只需把一段程序放入.py文件中,就能快速運行。而且Python語言很容易上手模塊。比如你編寫了一個模塊my_lib.py,只需在調用這個模塊的程序中加入一行import my_lib即可。
  • 用python理解什麼是幸福
    接下來會用Python給大家展示一下世界各國的幸福指數排名,以及和幸福有關的因素。民意測驗機構蓋洛普從2012年起,每年都會在聯合國計劃下發布《世界幸福指數報告》,報告會綜合兩年內150多個國家的國民對其所處社會、城市和自然環境等因素進行評價後,再根據他們所感知的幸福程度對國家進行排名。
  • 這些用 Python 寫的牛逼程序/腳本,你玩過嗎?
    1import cv2 2 3import sys 4 5import os,random,string 6 7#choices=[ Add a name ] 8 9import os 10 11current_directory=os.path.dirname(os.path.abspath
  • 用Python進行人臉識別「包括原始碼」
    OpenCV是一個用C++編寫的開源庫.它包含了用於計算機視覺任務的各種算法和深度神經網絡的實現。1.準備數據集創建2個目錄,訓練和測試。從網際網路上為每個演員選擇一個圖片,並下載到我們的「火車」目錄中。確保您所選擇的圖像能夠很好地顯示人臉的特徵,以便對分類器進行分類。
  • 技術 | 用python生成無人機影像的正射影像、數字高程模型以及三維建模
    #ecoding:utf-8import datetimefrom PIL import Imageimport piexifimport csvimport osdef main(csv_path, image_root, save_root): csv_data = open(csv_path, "r
  • 用 Python 玩微信跳一跳
    width}".format(height=m.group(2), width=m.group(1))def init():    """    初始化    :return:    """    # 獲取屏幕解析度    screen_size = _get_screen_size()    config_file_path
  • 關於 realpath 方法
    這個方法返回一個絕對路徑,參數是個路徑如果參數的路徑不是絕對值,那返回FALSE如:返回絕對路徑D:\phpstudy_pro\WWW\cjqshop\core\Appecho realpath('\phpstudy_pro\WWW\cjqshop/core/app/');返回falseecho realpath('phpstudy_pro\WWW\cjqshop/core/app/');返回絕對路徑D:\phpstudy_pro\WWW\cjqshop\core\Appecho realpath('D:\phpstudy_pro\WWW
  • 用 KNN 實現驗證碼識別,又 Get 到一招
    選擇較小的k值,就相當於用較小的領域中的訓練實例進行預測,訓練誤差會減小,只有與輸入實例較近或相似的訓練實例才會對預測結果起作用,與此同時帶來的問題是泛化誤差會增大,換句話說,K值的減小就意味著整體模型變得複雜,容易發生過擬合;選擇較大的k值,就相當於用較大領域中的訓練實例進行預測,其優點是可以減少泛化誤差,但缺點是訓練誤差會增大。