![](/img/trans.png)
[英]How can I safely merge a nested list in Python by use a unique value?
[英]How can I safely create a nested directory?
我正在使用 Python 編寫文件。 我如何檢查:
在 Python ≥ 3.5 上,使用pathlib.Path.mkdir
:
from pathlib import Path
Path("/my/directory").mkdir(parents=True, exist_ok=True)
對於舊版本的 Python,我看到兩個質量很好的答案,每個都有一個小缺陷,所以我會給出我的看法:
嘗試os.path.exists
,並考慮創建os.makedirs
。
import os
if not os.path.exists(directory):
os.makedirs(directory)
如評論和其他地方所述,存在競爭條件 - 如果在os.path.exists
和os.makedirs
調用之間創建目錄,則os.makedirs
將失敗並返回OSError
。 不幸的是,一攬子捕獲OSError
並繼續並不是萬無一失的,因為它會忽略由於其他因素(例如權限不足、磁盤已滿等)而導致創建目錄失敗的情況。
一種選擇是捕獲OSError
並檢查嵌入的錯誤代碼(請參閱Is there a cross-platform way of getting information from Python's OSError ):
import os, errno
try:
os.makedirs(directory)
except OSError as e:
if e.errno != errno.EEXIST:
raise
或者,可能有第二個os.path.exists
,但假設另一個在第一次檢查后創建了目錄,然后在第二次檢查之前將其刪除——我們仍然可能被愚弄。
根據應用程序的不同,並發操作的危險可能大於或小於文件權限等其他因素帶來的危險。 在選擇實現之前,開發人員必須更多地了解正在開發的特定應用程序及其預期環境。
現代版本的 Python 通過暴露FileExistsError
(在 3.3+ 中)對這段代碼進行了相當多的改進......
try:
os.makedirs("path/to/directory")
except FileExistsError:
# directory already exists
pass
...並通過允許os.makedirs
的關鍵字參數稱為exist_ok
(在 3.2+ 中)。
os.makedirs("path/to/directory", exist_ok=True) # succeeds even if directory exists.
import pathlib
pathlib.Path('/my/directory').mkdir(parents=True, exist_ok=True)
上面使用的pathlib.Path.mkdir
遞歸地創建目錄,如果目錄已經存在,則不會引發異常。 如果您不需要或不希望創建父母,請跳過parents
參數。
使用pathlib
:
如果可以,請安裝名為pathlib2
的當前pathlib
backport。 不要安裝名為pathlib
的較舊的未維護的反向端口。 接下來,參考上面的 Python 3.5+ 部分,同樣使用它。
如果使用 Python 3.4,即使它帶有pathlib
,它也缺少有用的exist_ok
選項。 向后移植旨在提供更新和更好的mkdir
實現,其中包括這個缺失的選項。
使用os
:
import os
os.makedirs(path, exist_ok=True)
上面使用的os.makedirs
遞歸地創建目錄,如果目錄已經存在,則不會引發異常。 僅當使用 Python 3.2+ 時,它才具有可選的exist_ok
參數,默認值為False
。 此參數在 Python 2.x 到 2.7 中不存在。 因此,不需要像 Python 2.7 那樣手動處理異常。
使用pathlib
:
如果可以,請安裝名為pathlib2
的當前pathlib
backport。 不要安裝名為pathlib
的較舊的未維護的反向端口。 接下來,參考上面的 Python 3.5+ 部分,同樣使用它。
使用os
:
import os
try:
os.makedirs(path)
except OSError:
if not os.path.isdir(path):
raise
雖然一個簡單的解決方案可能首先使用os.path.isdir
后跟os.makedirs
,但上面的解決方案顛倒了這兩個操作的順序。 這樣做,它可以防止與創建目錄的重復嘗試有關的常見競爭條件,並且還可以消除目錄中的文件歧義。
請注意,捕獲異常並使用errno
的用處有限,因為OSError: [Errno 17] File exists
,即errno.EEXIST
,對於文件和目錄都會引發。 簡單地檢查目錄是否存在更可靠。
mkpath
創建嵌套目錄,如果該目錄已經存在,則不執行任何操作。 這適用於 Python 2 和 3。
import distutils.dir_util
distutils.dir_util.mkpath(path)
根據Bug 10948 ,此替代方案的一個嚴重限制是它對於給定路徑的每個 python 進程僅工作一次。 換句話說,如果你使用它來創建一個目錄,然后從 Python 內部或外部刪除該目錄,然后再次使用mkpath
重新創建相同的目錄, mkpath
將簡單地使用其先前創建目錄的無效緩存信息,並且實際上不會再次創建目錄。 相反, os.makedirs
不依賴任何此類緩存。 對於某些應用程序,此限制可能沒問題。
關於目錄的模式,如果你關心它,請參考文檔。
使用 try except 和來自 errno 模塊的正確錯誤代碼擺脫了競爭條件並且是跨平台的:
import os
import errno
def make_sure_path_exists(path):
try:
os.makedirs(path)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
換句話說,我們嘗試創建目錄,但如果它們已經存在,我們將忽略錯誤。 另一方面,報告任何其他錯誤。 例如,如果您事先創建 dir 'a' 並從中刪除所有權限,您將收到一個帶有errno.EACCES
的OSError
(權限被拒絕,錯誤 13)。
從 Python 3.5 開始, pathlib.Path.mkdir
有一個exist_ok
標志:
from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True)
# path.parent ~ os.path.dirname(path)
這會遞歸地創建目錄,如果目錄已經存在,則不會引發異常。
(就像os.makedirs
從 python 3.2 開始有一個exist_ok
標志,例如os.makedirs(path, exist_ok=True)
)
注意:當我發布這個答案時,沒有提到其他答案exist_ok
...
我個人建議您使用os.path.isdir()
os.path.exists()
測試。
>>> os.path.exists('/tmp/dirname')
True
>>> os.path.exists('/tmp/dirname/filename.etc')
True
>>> os.path.isdir('/tmp/dirname/filename.etc')
False
>>> os.path.isdir('/tmp/fakedirname')
False
如果你有:
>>> dir = raw_input(":: ")
還有一個愚蠢的用戶輸入:
:: /tmp/dirname/filename.etc
...如果您使用os.path.exists()
進行測試,當您將該參數傳遞給os.makedirs()
時,您最終會得到一個名為filename.etc
的目錄。
檢查os.makedirs
:(它確保完整的路徑存在。)
要處理目錄可能存在的事實,請捕獲OSError
。 (如果exist_ok
為False
(默認值),如果目標目錄已經存在,則會引發OSError
。)
import os
try:
os.makedirs('./path/to/somewhere')
except OSError:
pass
試試os.path.exists
函數
if not os.path.exists(dir):
os.mkdir(dir)
您在特定路徑中提供特定文件,然后從文件路徑中提取目錄。 然后在確保您擁有該目錄之后,您嘗試打開一個文件進行讀取。 要評論此代碼:
filename = "/my/directory/filename.txt" dir = os.path.dirname(filename)
我們希望避免覆蓋內置函數dir
。 此外, filepath
或fullfilepath
可能是比filename
更好的語義名稱,所以這樣寫會更好:
import os
filepath = '/my/directory/filename.txt'
directory = os.path.dirname(filepath)
你的最終目標是打開這個文件,你最初聲明,寫,但你基本上是這樣接近這個目標(基於你的代碼),它打開文件進行閱讀:
if not os.path.exists(directory): os.makedirs(directory) f = file(filename)
你為什么要為一個你希望在那里並且能夠讀取的文件創建一個目錄?
只需嘗試打開文件。
with open(filepath) as my_file:
do_stuff(my_file)
如果目錄或文件不存在,您將收到帶有相關錯誤號的IOError
: errno.ENOENT
將指向正確的錯誤號,無論您的平台如何。 如果你願意,你可以抓住它,例如:
import errno
try:
with open(filepath) as my_file:
do_stuff(my_file)
except IOError as error:
if error.errno == errno.ENOENT:
print 'ignoring error because directory or file is not there'
else:
raise
這可能就是你想要的。
在這種情況下,我們可能不會面臨任何競爭條件。 所以就照原樣做,但請注意,要寫入,您需要以w
模式打開(或a
追加)。 使用上下文管理器打開文件也是 Python 的最佳實踐。
import os
if not os.path.exists(directory):
os.makedirs(directory)
with open(filepath, 'w') as my_file:
do_stuff(my_file)
但是,假設我們有幾個 Python 進程試圖將它們的所有數據放到同一個目錄中。 然后我們可能會爭用目錄的創建。 在這種情況下,最好將makedirs
調用包裝在 try-except 塊中。
import os
import errno
if not os.path.exists(directory):
try:
os.makedirs(directory)
except OSError as error:
if error.errno != errno.EEXIST:
raise
with open(filepath, 'w') as my_file:
do_stuff(my_file)
我已經把以下內容。 不過,這也不是萬無一失的。
import os
dirname = 'create/me'
try:
os.makedirs(dirname)
except OSError:
if os.path.exists(dirname):
# We are nearly safe
pass
else:
# There was an error on creation, so make sure we know about it
raise
現在正如我所說,這並不是萬無一失的,因為我們有可能無法創建目錄,並且在此期間有另一個進程創建它。
檢查目錄是否存在並在必要時創建它?
對此的直接答案是,假設您不希望其他用戶或進程弄亂您的目錄的簡單情況:
if not os.path.exists(d):
os.makedirs(d)
或者,如果使目錄受制於競爭條件(即,如果在檢查路徑存在之后,可能已經有其他東西),請執行以下操作:
import errno
try:
os.makedirs(d)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
但也許更好的方法是通過tempfile
使用臨時目錄來回避資源爭用問題:
import tempfile
d = tempfile.mkdtemp()
以下是在線文檔中的要點:
mkdtemp(suffix='', prefix='tmp', dir=None) User-callable function to create and return a unique temporary directory. The return value is the pathname of the directory. The directory is readable, writable, and searchable only by the creating user. Caller is responsible for deleting the directory when done with it.
pathlib.Path
和exist_ok
有一個新的Path
對象(從 3.4 開始),其中包含許多希望與路徑一起使用的方法 - 其中之一是mkdir
。
(對於上下文,我正在使用腳本跟蹤我的每周代表。以下是腳本中代碼的相關部分,可以讓我避免每天針對相同的數據多次訪問 Stack Overflow。)
首先是相關的進口:
from pathlib import Path
import tempfile
我們現在不必處理os.path.join
- 只需使用/
連接路徑部分:
directory = Path(tempfile.gettempdir()) / 'sodata'
然后我冪等地確保目錄存在exist_ok
參數出現在 Python 3.5 中:
directory.mkdir(exist_ok=True)
這是文檔的相關部分:
如果
exist_ok
為真,FileExistsError
異常將被忽略(與POSIX mkdir -p
命令的行為相同),但前提是最后一個路徑組件不是現有的非目錄文件。
這是腳本的更多內容 - 就我而言,我不受競爭條件的影響,我只有一個進程希望目錄(或包含的文件)在那里,並且我沒有任何嘗試刪除的內容目錄。
todays_file = directory / str(datetime.datetime.utcnow().date())
if todays_file.exists():
logger.info("todays_file exists: " + str(todays_file))
df = pd.read_json(str(todays_file))
必須先將Path
對象強制轉換為str
,然后其他期望str
路徑的 API 才能使用它們。
也許應該更新 Pandas 以接受抽象基類os.PathLike
的實例。
在 Python 3.4 中,您還可以使用全新的pathlib
模塊:
from pathlib import Path
path = Path("/my/directory/filename.txt")
try:
if not path.parent.exists():
path.parent.mkdir(parents=True)
except OSError:
# handle error; you can also catch specific errors like
# FileExistsError and so on.
對於單線解決方案,您可以使用IPython.utils.path.ensure_dir_exists()
:
from IPython.utils.path import ensure_dir_exists
ensure_dir_exists(dir)
從文檔中:確保目錄存在。 如果它不存在,請嘗試創建它並在另一個進程正在執行相同操作時防止出現競爭條件。
IPython 是一個擴展包,不是標准庫的一部分。
在Python3中, os.makedirs
支持設置exist_ok
。 默認設置為False
,這意味着如果目標目錄已經存在,則會引發OSError
。 通過將exist_ok
設置為True
,將忽略OSError
(目錄存在)並且不會創建目錄。
os.makedirs(path,exist_ok=True)
在Python2中, os.makedirs
不支持設置exist_ok
。 您可以在heikki-toivonen 的回答中使用該方法:
import os
import errno
def make_sure_path_exists(path):
try:
os.makedirs(path)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
相關的 Python 文檔建議使用EAFP 編碼風格(Easier to Ask for Forgiveness than Permission) 。 這意味着代碼
try:
os.makedirs(path)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
else:
print "\nBE CAREFUL! Directory %s already exists." % path
比替代品更好
if not os.path.exists(path):
os.makedirs(path)
else:
print "\nBE CAREFUL! Directory %s already exists." % path
文檔表明這一點正是因為這個問題中討論的競爭條件。 此外,正如其他人在這里提到的那樣,查詢一次而不是兩次操作系統具有性能優勢。 最后,在某些情況下(當開發人員知道應用程序正在運行的環境時)提出的論點可能支持第二個代碼,只能在程序設置了私有環境的特殊情況下被提倡。本身(以及同一程序的其他實例)。
即使在這種情況下,這也是一種不好的做法,並且可能導致長時間無用的調試。 例如,我們為目錄設置權限這一事實不應該給我們留下印象權限是為我們的目的適當設置的。 可以使用其他權限安裝父目錄。 一般來說,一個程序應該總是正確地工作,程序員不應該期望一個特定的環境。
在 python 中執行此操作的最佳方法
#Devil
import os
directory = "./out_dir/subdir1/subdir2"
if not os.path.exists(directory):
os.makedirs(directory)
在我對在 Python 中使用目錄時遇到的一些失敗和錯誤感到困惑之后,我發現了這個 Q/A。 我正在使用 Python 3(Arch Linux x86_64 系統上的 Anaconda 虛擬環境中的 v.3.5)。
考慮這個目錄結構:
└── output/ ## dir
├── corpus ## file
├── corpus2/ ## dir
└── subdir/ ## dir
這是我的實驗/筆記,提供了說明:
# ----------------------------------------------------------------------------
# [1] https://stackoverflow.com/questions/273192/how-can-i-create-a-directory-if-it-does-not-exist
import pathlib
""" Notes:
1. Include a trailing slash at the end of the directory path
("Method 1," below).
2. If a subdirectory in your intended path matches an existing file
with same name, you will get the following error:
"NotADirectoryError: [Errno 20] Not a directory:" ...
"""
# Uncomment and try each of these "out_dir" paths, singly:
# ----------------------------------------------------------------------------
# METHOD 1:
# Re-running does not overwrite existing directories and files; no errors.
# out_dir = 'output/corpus3' ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/' ## works
# out_dir = 'output/corpus3/doc1' ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/doc1/' ## works
# out_dir = 'output/corpus3/doc1/doc.txt' ## no error but no file created (os.makedirs creates dir, not files! ;-)
# out_dir = 'output/corpus2/tfidf/' ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/' ## works
# out_dir = 'output/corpus3/a/b/c/d/' ## works
# [2] https://docs.python.org/3/library/os.html#os.makedirs
# Uncomment these to run "Method 1":
#directory = os.path.dirname(out_dir)
#os.makedirs(directory, mode=0o777, exist_ok=True)
# ----------------------------------------------------------------------------
# METHOD 2:
# Re-running does not overwrite existing directories and files; no errors.
# out_dir = 'output/corpus3' ## works
# out_dir = 'output/corpus3/' ## works
# out_dir = 'output/corpus3/doc1' ## works
# out_dir = 'output/corpus3/doc1/' ## works
# out_dir = 'output/corpus3/doc1/doc.txt' ## no error but creates a .../doc.txt./ dir
# out_dir = 'output/corpus2/tfidf/' ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/' ## works
# out_dir = 'output/corpus3/a/b/c/d/' ## works
# Uncomment these to run "Method 2":
#import os, errno
#try:
# os.makedirs(out_dir)
#except OSError as e:
# if e.errno != errno.EEXIST:
# raise
# ----------------------------------------------------------------------------
結論:在我看來,“方法2”更健壯。
[1] 如何安全地創建嵌套目錄?
您可以使用mkpath
# Create a directory and any missing ancestor directories.
# If the directory already exists, do nothing.
from distutils.dir_util import mkpath
mkpath("test")
請注意,它也會創建祖先目錄。
它適用於 Python 2 和 3。
最快最安全的方法是:如果不存在則創建,如果存在則跳過:
from pathlib import Path
Path("path/with/childs/.../").mkdir(parents=True, exist_ok=True)
如果在支持帶有-p
選項的命令mkdir
的機器上運行,為什么不使用子進程模塊? 適用於 python 2.7 和 python 3.6
from subprocess import call
call(['mkdir', '-p', 'path1/path2/path3'])
應該在大多數系統上做到這一點。
在可移植性無關緊要的情況下(例如,使用 docker),解決方案是干凈的 2 行。 您也不必添加邏輯來檢查目錄是否存在。 最后,重新運行是安全的,沒有任何副作用
如果您需要錯誤處理:
from subprocess import check_call
try:
check_call(['mkdir', '-p', 'path1/path2/path3'])
except:
handle...
您必須在創建目錄之前設置完整路徑:
import os,sys,inspect
import pathlib
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
your_folder = currentdir + "/" + "your_folder"
if not os.path.exists(your_folder):
pathlib.Path(your_folder).mkdir(parents=True, exist_ok=True)
這對我有用,希望它也對你有用
如果您將文件寫入變量路徑,您可以在文件路徑上使用它來確保創建父目錄。
from pathlib import Path
path_to_file = Path("zero/or/more/directories/file.ext")
parent_directory_of_file = path_to_file.parent
parent_directory_of_file.mkdir(parents=True, exist_ok=True)
即使path_to_file
是file.ext
(零目錄深度)也可以工作。
我看到Heikki Toivonen和ABB的回答並想到了這種變化。
import os
import errno
def make_sure_path_exists(path):
try:
os.makedirs(path)
except OSError as exception:
if exception.errno != errno.EEXIST or not os.path.isdir(path):
raise
我使用os.path.exists()
,這是一個 Python 3 腳本,可用於檢查目錄是否存在,如果不存在則創建一個,如果存在則刪除它(如果需要)。
它提示用戶輸入目錄並且可以很容易地修改。
使用此命令檢查並創建目錄
if not os.path.isdir(test_img_dir):
os.mkdir(test_img_dir)
在程序/項目的入口點調用函數create_dir()
。
import os
def create_dir(directory):
if not os.path.exists(directory):
print('Creating Directory '+directory)
os.makedirs(directory)
create_dir('Project directory')
您可以為此使用os.listdir
:
import os
if 'dirName' in os.listdir('parentFolderPath')
print('Directory Exists')
如果您考慮以下情況:
os.path.isdir('/tmp/dirname')
表示存在目錄(路徑)並且是目錄。 所以對我來說,這種方式可以滿足我的需要。 所以我可以確保它是文件夾(不是文件)並且存在。
這可能不能完全回答這個問題。 但我猜你的真正意圖是創建一個文件及其父目錄,因為它的內容全部在 1 個命令中。
您可以使用 pathlib 的fastcore
擴展來做到這一點: path.mk_write(data)
from fastcore.utils import Path
Path('/dir/to/file.txt').mk_write('Hello World')
在fastcore 文檔中查看更多信息
import os
if os.path.isfile(filename):
print "file exists"
else:
"Your code here"
此處的代碼使用 (touch) 命令
這將檢查文件是否存在,如果不存在,它將創建它。
您可以使用系統調用創建嵌套目錄dir1/dir2/...
說在Linux下可以這樣做:
import os
dirs='dir1/dir2/dir3'
os.system("mkdir -p {0}".format(dirs))
標志-p
檢查目錄是否存在,在這種情況下不會生成任何錯誤消息。
讓我提一下,這看起來不像是完全由 Python 方法完成的,任何做同樣事情的 Python 庫都應該在內部使用上述類型的系統調用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.