Cara andal membuka file di direktori yang sama dengan skrip Python

157

Saya biasa membuka file yang berada di direktori yang sama dengan skrip Python yang sedang berjalan dengan hanya menggunakan perintah seperti

open("Some file.txt", "r")

Namun, saya menemukan bahwa ketika skrip dijalankan di Windows dengan mengklik dua kali, skrip akan mencoba membuka file dari direktori yang salah.

Sejak itu saya telah menggunakan perintah form

open(os.path.join(sys.path[0], "Some file.txt"), "r")

setiap kali saya ingin membuka file. Ini berfungsi untuk penggunaan khusus saya, tetapi saya tidak yakin apakah sys.path[0]mungkin gagal dalam beberapa kasus penggunaan lainnya.

Jadi pertanyaan saya adalah: Apa cara terbaik dan paling dapat diandalkan untuk membuka file yang ada di direktori yang sama dengan skrip Python yang sedang berjalan?

Inilah yang saya sudah bisa mencari tahu sejauh ini:

  • os.getcwd()dan os.path.abspath('')kembalikan "direktori kerja saat ini", bukan direktori skrip.

  • os.path.dirname(sys.argv[0])dan os.path.dirname(__file__)kembalikan jalur yang digunakan untuk memanggil skrip, yang mungkin relatif atau bahkan kosong (jika skrip berada di cwd). Juga, __file__tidak ada ketika skrip dijalankan di IDLE atau PythonWin.

  • sys.path[0]dan os.path.abspath(os.path.dirname(sys.argv[0]))sepertinya mengembalikan direktori skrip. Saya tidak yakin apakah ada perbedaan antara keduanya.

Edit:

Saya baru menyadari bahwa apa yang ingin saya lakukan akan lebih baik digambarkan sebagai "buka file di direktori yang sama dengan modul yang mengandung". Dengan kata lain, jika saya mengimpor modul yang saya tulis itu di direktori lain, dan modul itu membuka file, saya ingin mencari file di direktori modul. Saya tidak berpikir apa pun yang saya temukan dapat melakukan itu ...

dln385
sumber

Jawaban:

199

Saya selalu menggunakan:

__location__ = os.path.realpath(
    os.path.join(os.getcwd(), os.path.dirname(__file__)))

The join()panggilan prepends direktori kerja saat ini, tapi dokumentasi mengatakan bahwa jika beberapa jalan adalah mutlak, semua jalur lain yang tersisa dari itu dijatuhkan. Karena itu, getcwd()dijatuhkan ketika dirname(__file__)mengembalikan jalur absolut.

Selain itu, realpathpanggilan akan menyelesaikan tautan simbolik jika ada yang ditemukan. Ini menghindari masalah ketika menggunakan dengan setuptools pada sistem Linux (skrip disinkronkan dengan /usr/bin/- setidaknya pada Debian).

Anda dapat menggunakan yang berikut ini untuk membuka file di folder yang sama:

f = open(os.path.join(__location__, 'bundled-resource.jpg'));
# ...

Saya menggunakan ini untuk menyatukan sumber daya dengan beberapa aplikasi Django pada Windows dan Linux dan itu berfungsi seperti pesona!

André Caron
sumber
4
Jika __file__tidak dapat digunakan, maka gunakan sys.argv[0]sebagai gantinya dirname(__file__). Sisanya harus bekerja seperti yang diharapkan. Saya suka menggunakan __file__karena dalam kode pustaka, sys.argv[0]mungkin tidak menunjuk ke kode Anda sama sekali, terutama jika diimpor melalui beberapa skrip pihak ke-3.
André Caron
1
Masalahnya adalah ini akan bervariasi jika file yang Anda jalankan berasal dari interrupter secara langsung atau jika diimpor. Lihat jawaban saya untuk perbedaan antara file dan sys.argv [0]
Zimm3r
Jadi apakah benar mengatakan bahwa variasi yang dijelaskan dalam jawaban Zimm3r ditangani dengan menggunakan realpath( join( getcwd(), dirname(__file__) ))seperti yang dijelaskan di sini?
pianoJames
44

Mengutip dari dokumentasi Python:

Seperti diinisialisasi pada startup program, item pertama dari daftar ini, path [0], adalah direktori yang berisi skrip yang digunakan untuk memanggil juru bahasa Python. Jika direktori skrip tidak tersedia (mis. Jika penerjemah dipanggil secara interaktif atau jika skrip dibaca dari input standar), path [0] adalah string kosong, yang mengarahkan Python untuk mencari modul dalam direktori saat ini terlebih dahulu. Perhatikan bahwa direktori skrip dimasukkan sebelum entri dimasukkan sebagai hasil dari PYTHONPATH.

sys.path [0] adalah apa yang Anda cari.

MONKEY MERAH
sumber
10
Dan untuk path lengkap dari file tersebut: os.path.join(sys.path[0], 'some file.txt'). Itu harus menangani spasi dan garis miring dengan benar pada semua sistem.
Jacktose
Ini jawaban untuk pertanyaan pertama, bukan yang setelah EDIT.
mcoolive
22

Oke, inilah yang saya lakukan

sys.argv selalu apa yang Anda ketikkan ke terminal atau gunakan sebagai path file ketika mengeksekusinya dengan python.exe atau pythonw.exe

Misalnya Anda dapat menjalankan file text.py beberapa cara, mereka masing-masing memberi Anda jawaban berbeda mereka selalu memberi Anda jalan yang diketikkan python.

    C:\Documents and Settings\Admin>python test.py
    sys.argv[0]: test.py
    C:\Documents and Settings\Admin>python "C:\Documents and Settings\Admin\test.py"
    sys.argv[0]: C:\Documents and Settings\Admin\test.py

Ok jadi ketahuilah Anda bisa mendapatkan nama file, masalah besar, sekarang untuk mendapatkan direktori aplikasi Anda bisa tahu menggunakan os.path, khususnya abspath dan dirname

    import sys, os
    print os.path.dirname(os.path.abspath(sys.argv[0]))

Itu akan menampilkan ini:

   C:\Documents and Settings\Admin\

itu akan selalu menampilkan ini tidak masalah jika Anda mengetik python test.py atau python "C: \ Documents and Settings \ Admin \ test.py"

Masalah dengan menggunakan __file__ Pertimbangkan dua file ini test.py

import sys
import os

def paths():
        print "__file__: %s" % __file__
        print "sys.argv: %s" % sys.argv[0]

        a_f = os.path.abspath(__file__)
        a_s = os.path.abspath(sys.argv[0])

        print "abs __file__: %s" % a_f
        print "abs sys.argv: %s" % a_s

if __name__ == "__main__":
    paths()

import_test.py

import test
import sys

test.paths()

print "--------"
print __file__
print sys.argv[0]

Output dari "python test.py"

C:\Documents and Settings\Admin>python test.py
__file__: test.py
sys.argv: test.py
abs __file__: C:\Documents and Settings\Admin\test.py
abs sys.argv: C:\Documents and Settings\Admin\test.py

Output dari "python test_import.py"

C:\Documents and Settings\Admin>python test_import.py
__file__: C:\Documents and Settings\Admin\test.pyc
sys.argv: test_import.py
abs __file__: C:\Documents and Settings\Admin\test.pyc
abs sys.argv: C:\Documents and Settings\Admin\test_import.py
--------
test_import.py
test_import.py

Jadi seperti yang Anda lihat file memberi Anda selalu file python itu sedang dijalankan, sedangkan sys.argv [0] memberi Anda file yang selalu Anda jalankan dari interpreter. Tergantung pada kebutuhan Anda, Anda harus memilih mana yang paling sesuai dengan kebutuhan Anda.

Zimm3r
sumber
3
Ini adalah bukti yang rumit bahwa implementasinya mencerminkan dokumentasi. __file__adalah seharusnya untuk "selalu memberikan jalan untuk file saat ini", dan sys.argv[0]yang seharusnya untuk "selalu memberikan jalan script yang memulai proses". Bagaimanapun, menggunakan __file__dalam skrip yang dipanggil selalu memberi Anda hasil yang tepat.
André Caron
Jika Anda memiliki referensi __file__di bagian atas skrip, itu akan berfungsi seperti yang diharapkan.
Matthew Schinckel
-1

Saya dapat menggunakan kode yang disediakan oleh dcolish dengan sukses karena saya mengalami masalah serupa dengan membaca file teks tertentu. File tidak dalam cwd yang sama dengan file Python.

Jaclyn Horton
sumber
1
Tolong jangan tambahkan "terima kasih" sebagai jawaban. Setelah Anda memiliki reputasi yang cukup , Anda akan dapat memilih pertanyaan dan jawaban yang menurut Anda sangat membantu. - Dari Ulasan
Roberto Caboni
-3

Saya akan melakukannya dengan cara ini:

from os.path import abspath, exists

f_path = abspath("fooabar.txt")

if exists(f_path):
    with open(f_path) as f:
        print f.read()

Kode di atas membangun path absolut ke file menggunakan abspath dan setara dengan menggunakan normpath(join(os.getcwd(), path))[itu dari pydocs]. Kemudian memeriksa apakah file itu benar-benar ada dan kemudian menggunakan manajer konteks untuk membukanya sehingga Anda tidak harus ingat untuk memanggil tutup pada pegangan file. IMHO, melakukannya dengan cara ini akan menghemat banyak rasa sakit dalam jangka panjang.

dcolish
sumber
Ini tidak menjawab pertanyaan poster. dln385 secara khusus mengatakan bahwa os.path.abspathtidak menyelesaikan jalur ke file di folder yang sama dengan skrip jika skrip tidak ada di direktori saat ini.
André Caron
AH! Saya berasumsi pengguna menjalankan skrip ini dalam direktori yang sama dengan file yang ingin mereka baca, BUKAN di dir modul dari sesuatu di PYTHONPATH mereka. Itu akan mengajari saya untuk membuat asumsi ...
dcolish
abspath tidak akan berfungsi karena tidak mungkin bagi python runtime untuk mencari di sistem file OS menggunakan fungsi seperti ini.
akshat thakar