Come aprire in modo affidabile un file nella stessa directory di uno script Python


157

Ho usato per aprire i file che si trovavano nella stessa directory dello script Python attualmente in esecuzione semplicemente usando un comando simile

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

Tuttavia, ho scoperto che quando lo script veniva eseguito in Windows facendo doppio clic su di esso, avrebbe tentato di aprire il file dalla directory errata.

Da allora ho usato un comando del modulo

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

ogni volta che volevo aprire un file. Questo funziona per il mio particolare utilizzo, ma non sono sicuro che sys.path[0]potrebbe non riuscire in qualche altro caso d'uso.

Quindi la mia domanda è: qual è il modo migliore e più affidabile per aprire un file che si trova nella stessa directory dello script Python attualmente in esecuzione?

Ecco cosa sono stato in grado di capire finora:

  • os.getcwd()e os.path.abspath('')restituisce la "directory di lavoro corrente", non la directory di script.

  • os.path.dirname(sys.argv[0])e os.path.dirname(__file__)restituisce il percorso utilizzato per chiamare lo script, che può essere relativo o addirittura vuoto (se lo script è nel CWD). Inoltre, __file__non esiste quando lo script viene eseguito in IDLE o PythonWin.

  • sys.path[0]e os.path.abspath(os.path.dirname(sys.argv[0]))sembra restituire la directory degli script. Non sono sicuro che ci sia differenza tra questi due.

Modificare:

Mi sono appena reso conto che ciò che voglio fare sarebbe meglio descritto come "apri un file nella stessa directory del modulo contenitore". In altre parole, se importare un modulo ho scritto che si trova in un'altra directory e quel modulo apre un file, voglio che cerchi il file nella directory del modulo. Non penso che nulla di ciò che ho trovato sia in grado di farlo ...

Risposte:


199

Uso sempre:

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

La join()chiamata antepone l'attuale directory di lavoro, ma la documentazione dice che se un percorso è assoluto, tutti gli altri percorsi rimasti vengono eliminati. Pertanto, getcwd()viene eliminato quando dirname(__file__)restituisce un percorso assoluto.

Inoltre, la realpathchiamata risolve i collegamenti simbolici se presenti. Questo evita problemi durante la distribuzione con setuptools su sistemi Linux (gli script sono collegati a /usr/bin/- almeno su Debian).

È possibile utilizzare quanto segue per aprire i file nella stessa cartella:

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

Lo uso per raggruppare le risorse con diverse applicazioni Django sia su Windows che su Linux e funziona come un fascino!


4
Se __file__non può essere utilizzato, utilizzare sys.argv[0]invece di dirname(__file__). Il resto dovrebbe funzionare come previsto. Mi piace usarlo __file__perché nel codice della libreria, sys.argv[0]potrebbe non indicare affatto il tuo codice, specialmente se importato tramite uno script di terze parti.
André Caron,

1
Il problema è che varierà se il file in esecuzione proviene direttamente dall'interruttore o se viene importato. Vedi la mia risposta per le differenze tra file e sys.argv [0]
Zimm3r

Quindi è corretto affermare che la variazione descritta nella risposta di Zimm3r è indirizzata usando realpath( join( getcwd(), dirname(__file__) ))come descritto qui?
pianoJames,

44

Per citare dalla documentazione di Python:

Come inizializzato all'avvio del programma, il primo elemento di questo elenco, percorso [0], è la directory contenente lo script utilizzato per richiamare l'interprete Python. Se la directory dello script non è disponibile (ad es. Se l'interprete viene richiamato in modo interattivo o se lo script viene letto dall'input standard), il percorso [0] è la stringa vuota che indirizza Python a cercare prima i moduli nella directory corrente. Si noti che la directory di script viene inserita prima delle voci inserite come risultato di PYTHONPATH.

sys.path [0] è quello che stai cercando.


10
E per il percorso completo del file: os.path.join(sys.path[0], 'some file.txt'). Ciò dovrebbe gestire correttamente spazi e barre su tutti i sistemi.
Jacktose,

Questa risposta alla prima domanda, non quella successiva all'EDIT.
mcoolive,

22

Ok ecco cosa faccio

sys.argv è sempre ciò che si digita nel terminale o si utilizza come percorso del file quando lo si esegue con python.exe o pythonw.exe

Ad esempio puoi eseguire il file text.py in diversi modi, ognuno ti dà una risposta diversa ti danno sempre il percorso in cui è stato digitato 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, quindi sappi che puoi ottenere il nome del file, un grosso problema, ora per ottenere la directory dell'applicazione puoi sapere usare os.path, in particolare abspath e dirname

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

Questo produrrà questo:

   C:\Documents and Settings\Admin\

produrrà sempre questo, non importa se si digita python test.py o python "C: \ Documents and Settings \ Admin \ test.py"

Il problema con l'utilizzo di __file__ Considera questi due file 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 di "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 di "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

Quindi, come puoi vedere, il file ti dà sempre il file python da cui viene eseguito, dove sys.argv [0] ti dà sempre il file che hai eseguito dall'interprete. A seconda delle tue esigenze dovrai scegliere quale si adatta meglio alle tue esigenze.


3
Questa è una prova elaborata che l'implementazione riflette la documentazione. __file__si suppone a "sempre dare il percorso del file corrente", e sys.argv[0]si suppone a "dare sempre il percorso dello script che ha avviato il processo". In ogni caso, l'utilizzo __file__nello script che viene invocato ti dà sempre risultati precisi.
André Caron,

Se hai il riferimento al __file__livello più alto dello script, funzionerà come previsto.
Matthew Schinckel,


-3

Lo farei così:

from os.path import abspath, exists

f_path = abspath("fooabar.txt")

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

Il codice sopra crea un percorso assoluto per il file usando abspath ed equivale a usare normpath(join(os.getcwd(), path))[quello è dal pydocs]. Quindi controlla se quel file esiste effettivamente e quindi utilizza un gestore di contesto per aprirlo in modo da non dover ricordare di chiudere vicino sull'handle del file. IMHO, farlo in questo modo ti farà risparmiare molto dolore a lungo termine.


Questo non risponde alla domanda del poster. dln385 ha affermato specificamente che os.path.abspathnon risolve i percorsi dei file nella stessa cartella dello script se lo script non si trova nella directory corrente.
André Caron,

AH! Supponevo che l'utente stesse eseguendo questo script nella stessa directory del file che voleva leggere, NON nella directory del modulo di qualcosa nel suo PYTHONPATH. Questo mi insegnerà a fare ipotesi ...
dcolish il

abspath non funzionerà in quanto è impossibile per il runtime di python effettuare ricerche nel file system del sistema operativo utilizzando una funzione come questa.
akshat thakar,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.