Pertanyaan Bagaimana saya dapat dengan aman membuat direktori bersarang dengan Python?


Apa cara paling elegan untuk memeriksa apakah direktori file akan ditulis ada, dan jika tidak, buat direktori menggunakan Python? Inilah yang saya coba:

import os

file_path = "/my/directory/filename.txt"
directory = os.path.dirname(file_path)

try:
    os.stat(directory)
except:
    os.mkdir(directory)       

f = file(filename)

Entah bagaimana, aku rindu os.path.exists (terima kasih kanja, Blair, dan Douglas). Inilah yang saya miliki sekarang:

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

Apakah ada bendera untuk "terbuka", yang membuat ini terjadi secara otomatis?


2983
2017-11-07 18:56


asal


Jawaban:


Saya melihat dua jawaban dengan kualitas yang bagus, masing-masing dengan kekurangan kecil, jadi saya akan memberikan pendapat saya tentang hal itu:

Mencoba os.path.exists, dan pertimbangkan os.makedirs untuk penciptaan.

import os
if not os.path.exists(directory):
    os.makedirs(directory)

Seperti tercantum dalam komentar dan di tempat lain, ada kondisi balapan - jika direktori dibuat di antara os.path.exists dan os.makedirs panggilan, os.makedirs akan gagal dengan OSError. Sayangnya, penangkap selimut OSError dan melanjutkan tidak mudah sekali, karena akan mengabaikan kegagalan untuk membuat direktori karena faktor lain, seperti izin yang tidak memadai, disk penuh, dll.

Satu pilihan adalah menjebak OSError dan periksa kode kesalahan tertanam (lihat Apakah ada cara lintas platform untuk mendapatkan informasi dari OSError Python):

import os, errno

try:
    os.makedirs(directory)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise

Atau, mungkin ada yang kedua os.path.exists, tetapi misalkan yang lain membuat direktori setelah pemeriksaan pertama, lalu menghapusnya sebelum yang kedua - kita masih bisa dibodohi.

Tergantung pada aplikasinya, bahaya operasi bersamaan mungkin lebih atau kurang dari bahaya yang ditimbulkan oleh faktor lain seperti izin file. Pengembang harus tahu lebih banyak tentang aplikasi tertentu yang sedang dikembangkan dan lingkungan yang diharapkan sebelum memilih implementasi.


3679
2017-11-07 19:06



Python 3.5+:

import pathlib
pathlib.Path('/my/directory').mkdir(parents=True, exist_ok=True) 

pathlib.Path.mkdir seperti yang digunakan di atas secara rekursif membuat direktori dan tidak meningkatkan pengecualian jika direktori sudah ada. Jika Anda tidak membutuhkan atau ingin orang tua dibuat, lewati parents argumen.

Python 3.2+:

Menggunakan pathlib:

Jika Anda bisa, instal arus pathlib backport bernama pathlib2. Jangan menginstal backport yang sebelumnya tidak bernama pathlib. Selanjutnya, lihat bagian Python 3.5+ di atas dan gunakan yang sama.

Jika menggunakan Python 3.4, meskipun itu dilengkapi pathlib, itu tidak ada gunanya exist_ok pilihan. Backport dimaksudkan untuk menawarkan implementasi yang lebih baru dan lebih unggul mkdir yang termasuk opsi yang hilang ini.

Menggunakan os:

import os
os.makedirs(path, exist_ok=True)

os.makedirs seperti yang digunakan di atas secara rekursif membuat direktori dan tidak meningkatkan pengecualian jika direktori sudah ada. Ini memiliki opsional exist_ok argumen hanya jika menggunakan Python 3.2+, dengan nilai default False. Argumen ini tidak ada di Python 2.x hingga 2.7. Dengan demikian, tidak diperlukan penanganan manual seperti pada Python 2.7.

Python 2.7+:

Menggunakan pathlib:

Jika Anda bisa, instal arus pathlib backport bernama pathlib2. Jangan menginstal backport yang sebelumnya tidak bernama pathlib. Selanjutnya, lihat bagian Python 3.5+ di atas dan gunakan yang sama.

Menggunakan os:

import os
try: 
    os.makedirs(path)
except OSError:
    if not os.path.isdir(path):
        raise

Sementara solusi naif mungkin pertama kali digunakan os.path.isdir diikuti oleh os.makedirs, solusi di atas membalikkan urutan dua operasi. Dengan demikian, ini mencegah kondisi perlombaan umum yang harus dilakukan dengan upaya duplikat dalam membuat direktori, dan juga menguraikan file dari direktori.

Perhatikan bahwa menangkap pengecualian dan menggunakan errno adalah kegunaan terbatas karena OSError: [Errno 17] File exists, i.e. errno.EEXIST, dinaikkan untuk file dan direktori. Ini lebih dapat diandalkan hanya untuk memeriksa apakah direktori ada.

Alternatif:

mkpath membuat direktori bersarang, dan tidak melakukan apa pun jika direktori sudah ada. Ini berfungsi baik pada Python 2 dan 3.

import distutils.dir_util
distutils.dir_util.mkpath(path)

Per Bug 10948Keterbatasan yang parah dari alternatif ini adalah bahwa ia hanya bekerja sekali per proses python untuk jalur tertentu. Dengan kata lain, jika Anda menggunakannya untuk membuat direktori, kemudian hapus direktori dari dalam atau luar Python, kemudian gunakan mkpath lagi untuk membuat ulang direktori yang sama, mkpath hanya akan diam-diam menggunakan info cache yang tidak valid karena sebelumnya telah membuat direktori, dan tidak akan benar-benar membuat direktori lagi. Sebaliknya, os.makedirs tidak bergantung pada cache semacam itu. Keterbatasan ini mungkin baik-baik saja untuk beberapa aplikasi.


Berkenaan dengan direktori itu mode, silakan lihat dokumentasi jika Anda peduli tentang itu.


809
2018-01-16 17:31



Menggunakan percobaan kecuali dan kode kesalahan yang benar dari modul errno menghilangkan kondisi balapan dan lintas platform:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

Dengan kata lain, kami mencoba membuat direktori, tetapi jika mereka sudah ada, kami mengabaikan kesalahan. Di sisi lain, kesalahan lain dilaporkan. Misalnya, jika Anda membuat dir 'a' sebelumnya dan menghapus semua izin dari itu, Anda akan mendapatkan OSError dibesarkan bersama errno.EACCES (Izin ditolak, kesalahan 13).


572
2018-02-17 17:17



Saya pribadi merekomendasikan Anda menggunakan os.path.isdir() untuk menguji, bukan 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

Jika Anda memiliki:

>>> dir = raw_input(":: ")

Dan masukan pengguna yang bodoh:

:: /tmp/dirname/filename.etc

... Anda akan berakhir dengan direktori bernama filename.etc ketika Anda menyampaikan argumen itu os.makedirs() jika Anda menguji dengan os.path.exists().


85
2018-01-14 17:57



Memeriksa os.makedirs: (Ini memastikan jalur lengkap ada.)
 Untuk menangani fakta, direktori mungkin ada, menangkap OSError. (Jika exist_ok adalah False (default), OSError dinaikkan jika direktori target sudah ada.)

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass

56
2017-11-07 19:01



Wawasan tentang hal-hal spesifik dari situasi ini

Anda memberikan file tertentu pada jalur tertentu dan Anda menarik direktori dari jalur file. Kemudian setelah memastikan Anda memiliki direktori, Anda mencoba membuka file untuk dibaca. Untuk mengomentari kode ini:

filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)

Kami ingin menghindari penimpaan fungsi bawaan, dir. Juga, filepath atau mungkin fullfilepath mungkin nama semantik yang lebih baik daripada filename jadi ini akan lebih baik ditulis:

import os
filepath = '/my/directory/filename.txt'
directory = os.path.dirname(filepath)

Tujuan akhir Anda adalah untuk membuka file ini, Anda awalnya menyatakan, untuk menulis, tetapi Anda pada dasarnya mendekati tujuan ini (berdasarkan kode Anda) seperti ini, yang membuka file untuk bacaan:

if not os.path.exists(directory):
    os.makedirs(directory)
f = file(filename)

Asumsi pembukaan untuk membaca

Mengapa Anda membuat direktori untuk file yang Anda harapkan ada di sana dan dapat membaca?

Hanya mencoba membuka file.

with open(filepath) as my_file:
    do_stuff(my_file)

Jika direktori atau file tidak ada, Anda akan mendapatkan IOError dengan nomor kesalahan terkait: errno.ENOENT akan mengarah ke nomor kesalahan yang benar terlepas dari platform Anda. Anda dapat menangkapnya jika diinginkan, misalnya:

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

Dengan asumsi kami membuka untuk menulis

Ini adalah mungkin apa yang kamu inginkan.

Dalam hal ini, kita mungkin tidak menghadapi kondisi balapan. Jadi lakukan saja seperti Anda, tetapi perhatikan bahwa untuk menulis, Anda perlu membuka dengan w mode (atau a untuk menambahkan). Ini juga merupakan praktik terbaik Python untuk menggunakan manajer konteks untuk membuka file.

import os
if not os.path.exists(directory):
    os.makedirs(directory)
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

Namun, katakanlah kami memiliki beberapa proses Python yang berusaha memasukkan semua data mereka ke dalam direktori yang sama. Maka kita mungkin memiliki pertentangan atas pembuatan direktori. Dalam hal ini yang terbaik adalah membungkus makedirs panggilan dalam blok coba-kecuali.

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)

29
2018-01-22 23:49



Mulai dari Python 3.5, pathlib.Path.mkdir memiliki exist_ok bendera:

from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True) 
# path.parent ~ os.path.dirname(path)

Ini secara rekursif membuat direktori dan tidak meningkatkan pengecualian jika direktori sudah ada.

(sama seperti os.makedirs mendapatkan sebuah exists_ok bendera mulai dari python 3.2).


28
2017-12-14 16:06