Come posso ottenere il percorso del file attualmente eseguito in Python?


193

Potrebbe sembrare una domanda da principiante, ma non lo è. Alcuni approcci comuni non funzionano in tutti i casi:

sys.argv [0]

Questo significa usare path = os.path.abspath(os.path.dirname(sys.argv[0])), ma non funziona se stai eseguendo un altro script Python in un'altra directory, e questo può accadere nella vita reale.

__file__

Questo significa usare path = os.path.abspath(os.path.dirname(__file__)), ma ho scoperto che non funziona:

  • py2exenon ha un __file__attributo, ma esiste una soluzione alternativa
  • Quando si esegue da IDLE con execute()non esiste alcun __file__attributo
  • OS X 10.6 dove ottengo NameError: global name '__file__' is not defined

Domande correlate con risposte incomplete:

Sto cercando soluzione generica , che funzioni in tutti i casi d'uso sopra indicati.

Aggiornare

Ecco il risultato di una prova:

Output di python a.py (su Windows)

a.py: __file__= a.py
a.py: os.getcwd()= C:\zzz

b.py: sys.argv[0]= a.py
b.py: __file__= a.py
b.py: os.getcwd()= C:\zzz

a.py

#! /usr/bin/env python
import os, sys

print "a.py: sys.argv[0]=", sys.argv[0]
print "a.py: __file__=", __file__
print "a.py: os.getcwd()=", os.getcwd()
print

execfile("subdir/b.py")

subdir / b.py

#! /usr/bin/env python
import os, sys

print "b.py: sys.argv[0]=", sys.argv[0]
print "b.py: __file__=", __file__
print "b.py: os.getcwd()=", os.getcwd()
print

albero

C:.
|   a.py
\---subdir
        b.py

Risposte:


80

Non è possibile determinare direttamente la posizione dello script principale in esecuzione. Dopotutto, a volte lo script non proveniva affatto da un file. Ad esempio, potrebbe provenire dall'interprete interattivo o dal codice generato dinamicamente e archiviato solo in memoria.

Tuttavia, è possibile determinare in modo affidabile la posizione di un modulo, poiché i moduli vengono sempre caricati da un file. Se si crea un modulo con il seguente codice e lo si inserisce nella stessa directory dello script principale, lo script principale può importare il modulo e utilizzarlo per localizzarsi.

QUALCHE_PERCORSO / module_locator.py:

def we_are_frozen():
    # All of the modules are built-in to the interpreter, e.g., by py2exe
    return hasattr(sys, "frozen")

def module_path():
    encoding = sys.getfilesystemencoding()
    if we_are_frozen():
        return os.path.dirname(unicode(sys.executable, encoding))
    return os.path.dirname(unicode(__file__, encoding))

QUALCHE_PERCORSO / main.py:

import module_locator
my_path = module_locator.module_path()

Se hai diversi script principali in directory diverse, potresti aver bisogno di più di una copia di module_locator.

Ovviamente, se il tuo script principale è caricato da qualche altro strumento che non ti consente di importare moduli che si trovano nello stesso luogo, allora sei sfortunato. In casi del genere, le informazioni che cerchi semplicemente non esistono in nessun punto del tuo programma. La soluzione migliore sarebbe quella di presentare un bug con gli autori dello strumento.


2
Dico che su OS 10.6 ottengo l' NameError: global name '__file__' is not definedutilizzo del file e questo non è all'interno dell'IDLE. Pensa che __file__sia definito solo all'interno dei moduli.
sorin,

1
@Sorin Sbarnea: ho aggiornato la mia risposta su come aggirarlo.
Daniel Stutzbach,

2
Grazie, ma in effetti il ​​problema con la mancanza __file__non aveva nulla a che fare con Unicode. Non so perché __file__non sia definito ma sto cercando una soluzione generica che funzioni tutti i casi.
sorin,

1
Spiacente, questo non è possibile in tutti i casi. Ad esempio, provo a farlo in waf.googlecode.com dall'interno di un file wscript (python). Questi file vengono eseguiti ma non sono moduli e non è possibile crearli come moduli (possono essere qualsiasi sottodirectory dall'albero dei sorgenti).
sorin,

1
Non ti darà some_path/module_locator.pyinvece la posizione di ?
Casebash,

68

Innanzitutto, è necessario importare da inspecteos

from inspect import getsourcefile
from os.path import abspath

Successivamente, ovunque tu voglia trovare il file sorgente da te, basta usare

abspath(getsourcefile(lambda:0))

Migliore risposta. Grazie.
Devan Williams,

4
Bella risposta. Grazie. Sembra anche essere la risposta più breve e portatile (in esecuzione su diversi sistemi operativi) e non riscontra problemi come NameError: global name '__file__' is not defined(un'altra soluzione ha causato questo).
Edward,

Un'altra possibilità che è altrettanto breve: lambda:_. Ha funzionato per me - non sono sicuro che funzionerà sempre. lambda:0probabilmente corre più veloce di una quantità incommensurabilmente piccola (o forse non così piccola ... potrebbe essere un carico immediato o qualcosa di ancora più veloce per 0un carico globale per _?) Discutibile se è più pulito o più facile da leggere o persino più intelligente / oscuro.
ArtOfWarfare il

Come con quasi tutte le proposte che ho provato, questo restituisce semplicemente il CWD per me, non il file di directory che sto eseguendo (debug).
James,

1
@James - Questo codice viene inserito nel file che stai eseguendo ... l'esecuzione non getsourcefile(lambda:0)avrà senso e tornerà semplicemente Nonese provi a eseguirlo a un prompt interattivo (poiché lambdanon si troverà in alcun file sorgente.) Se vuoi sapere da dove proviene un'altra funzione o oggetto in un ambiente interattivo, forse abspath(getsourcefile(thatFunctionOrObject))ti sarà più utile?
ArtOfWarfare il

16

questa soluzione è solida anche negli eseguibili

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))

2
Questa dovrebbe essere la risposta corretta. Funziona anche in entry_point: console_script, ma nessuna delle altre risposte.
Polv,

15

Stavo riscontrando un problema simile e penso che questo potrebbe risolvere il problema:

def module_path(local_function):
   ''' returns the module path without the use of __file__.  Requires a function defined
   locally in the module.
   from http://stackoverflow.com/questions/729583/getting-file-path-of-imported-module'''
   return os.path.abspath(inspect.getsourcefile(local_function))

Funziona con script regolari e inattivo. Tutto quello che posso dire è provarlo per gli altri!

Il mio uso tipico:

from toolbox import module_path
def main():
   pass # Do stuff

global __modpath__
__modpath__ = module_path(main)

Ora uso __modpath__ invece di __file__.


2
Secondo la guida allo stile di codifica PEP8 , non si dovrebbero mai creare nomi con caratteri di sottolineatura iniziali e finali doppi, quindi è __modpath__necessario rinominarli. Probabilmente anche tu non hai bisogno globaldell'affermazione. Altrimenti +1!
martineau,

4
In realtà è possibile definire la funzione locale direttamente nella chiamata a module_path(). cioè module_path(lambda _: None)che non dipende dagli altri contenuti della sceneggiatura in cui si trova.
martineau,

@martineau: ho preso il tuo suggerimento lambda _: Nonee l'ho usato per quasi due anni, ma proprio ora ho scoperto che avrei potuto condensarlo in solo lambda:0. C'è qualche motivo particolare per cui hai suggerito il modulo che hai fatto, con un argomento ignorato _, invece che senza alcun argomento? Esiste qualcosa di superiore nel Noneprefisso con uno spazio anziché solo 0? Sono entrambi ugualmente enigmatici, penso, solo uno è lungo 8 caratteri mentre l'altro è lungo 14 caratteri.
ArtOfWarfare il

@ArtOfWarfare: la differenza tra i due è che lambda _:è una funzione che accetta un argomento ed lambda:è uno che non ne prende nessuno. Non importa poiché la funzione non viene mai chiamata. Allo stesso modo, non importa quale valore di ritorno viene utilizzato. Immagino di aver scelto Noneperché all'epoca sembrava indicare che era una funzione di non fare nulla, mai da chiamare. Lo spazio di fronte è facoltativo e di nuovo lì solo per una migliore leggibilità (sempre cercando di seguire PEP8 è abitudine).
martineau,

@martineau: Ovviamente è un abuso lambda, usarlo per qualcosa che non avrebbe mai dovuto fare. Se dovessi seguire PEP8, penso che il contenuto giusto sarebbe pass, no None, ma non è valido per inserire una dichiarazione in a lambda, quindi devi inserire qualcosa con un valore. Ci sono alcune cose valide da 2 caratteri che potresti inserire, ma penso che le uniche cose valide da un singolo carattere che puoi inserire siano 0-9 (o un nome di variabile a singolo carattere assegnato al di fuori del lambda.) Immagino che 0meglio indica il nulla di 0- 9.
ArtOfWarfare il

6

La risposta breve è che non esiste un modo garantito per ottenere le informazioni desiderate , tuttavia ci sono euristiche che funzionano quasi sempre nella pratica. Potresti guardare Come posso trovare la posizione dell'eseguibile in C? . Discute il problema da un punto di vista C, ma le soluzioni proposte sono facilmente trascritte in Python.


Ciao, la mia bandiera è stata rifiutata, quindi ho iniziato una discussione su meta: meta.stackoverflow.com/questions/277272/…
ArtOfWarfare,

Ci sono tuttavia già semplici risposte operative in questa pagina. La mia soluzione funziona bene: stackoverflow.com/a/33531619/3787376 .
Edward,

5

Vedi la mia risposta alla domanda Importazione di moduli dalla cartella principale per informazioni correlate, incluso il motivo per cui la mia risposta non utilizza la __file__variabile inaffidabile . Questa semplice soluzione dovrebbe essere compatibile con diversi sistemi operativi come i moduli ose far inspectparte di Python.

In primo luogo, è necessario parti dell'importazione del ispezionare e os moduli.

from inspect import getsourcefile
from os.path import abspath

Quindi, usa la seguente riga in qualsiasi altro punto del codice Python:

abspath(getsourcefile(lambda:0))

Come funziona:

Dal modulo integrato os(descrizione sotto), lo abspathstrumento viene importato.

Routine OS per Mac, NT o Posix a seconda del sistema in uso.

Quindi getsourcefile(descrizione in basso) viene importato dal modulo integrato inspect.

Ottieni informazioni utili dagli oggetti live di Python.

  • abspath(path) restituisce la versione assoluta / completa di un percorso di file
  • getsourcefile(lambda:0)in qualche modo ottiene il file sorgente interno dell'oggetto funzione lambda, quindi ritorna '<pyshell#nn>'nella shell Python o restituisce il percorso del file del codice Python attualmente in esecuzione.

L'utilizzo abspathsul risultato di getsourcefile(lambda:0)dovrebbe assicurarsi che il percorso del file generato sia il percorso completo del file Python.
Questa soluzione spiegata era originariamente basata sul codice della risposta in Come posso ottenere il percorso del file attualmente eseguito in Python? .


sì, ho appena trovato la stessa soluzione ... molto meglio delle risposte che dicono semplicemente che non si può fare in modo affidabile ... a meno che la domanda non stia ponendo ciò che penso sia ...
Grady Player

5

Hai semplicemente chiamato:

path = os.path.abspath(os.path.dirname(sys.argv[0]))

invece di:

path = os.path.dirname(os.path.abspath(sys.argv[0]))

abspath()ti dà il percorso assoluto di sys.argv[0](il nome del file in cui si trova il tuo codice) e dirname()restituisce il percorso della directory senza il nome del file.


4

Questo dovrebbe fare il trucco in un modo multipiattaforma (purché non si usi l'interprete o qualcosa del genere):

import os, sys
non_symbolic=os.path.realpath(sys.argv[0])
program_filepath=os.path.join(sys.path[0], os.path.basename(non_symbolic))

sys.path[0]è la directory in cui si trova lo script chiamante (il primo posto in cui cerca i moduli che devono essere utilizzati da quello script). Possiamo prendere il nome del file stesso alla fine di sys.argv[0](che è quello che ho fatto con os.path.basename). os.path.joinli unisce semplicemente in modo multipiattaforma.os.path.realpathsolo assicurati che se otteniamo collegamenti simbolici con nomi diversi dallo script stesso, otteniamo ancora il vero nome dello script.

Non ho un Mac; quindi, non ho provato questo su uno. Per favore fatemi sapere se funziona, come sembra che dovrebbe. Ho provato questo in Linux (Xubuntu) con Python 3.4. Nota che molte soluzioni a questo problema non funzionano sui Mac (poiché ho sentito che __file__non è presente sui Mac).

Nota che se lo script è un link simbolico, ti darà il percorso del file a cui si collega (e non il percorso del link simbolico).


2

È possibile utilizzare Pathdal pathlibmodulo:

from pathlib import Path

# ...

Path(__file__)

Puoi usare call to parentper andare oltre nel percorso:

Path(__file__).parent

Questa risposta usa la __file__variabile che può essere inaffidabile (non è sempre il percorso completo del file, non funziona su tutti i sistemi operativi ecc.) Come spesso menzionato dagli utenti StackOverflow. La modifica della risposta in modo da non includerla causerà meno problemi e sarà più compatibile tra loro. Per ulteriori informazioni, consultare stackoverflow.com/a/33532002/3787376 .
Edward,

@ mrroot5 Ok, quindi cancella il tuo commento per favore.
Gavriel Cohen,

1

Se il codice proviene da un file, è possibile ottenere il nome completo

sys._getframe().f_code.co_filename

È inoltre possibile recuperare il nome della funzione come f_code.co_name


0

Aggiungi semplicemente quanto segue:

from sys import *
path_to_current_file = sys.argv[0]
print(path_to_current_file)

O:

from sys import *
print(sys.argv[0])

0

La mia soluzione è:

import os
print(os.path.dirname(os.path.abspath(__file__)))

-1
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))

Ciò non ha senso. In primo luogo, '__file__'non dovrebbe essere una stringa, in secondo luogo, se lo facessi __file__, questo funzionerebbe solo per il file in cui si trova questa riga di codice e non per il file che viene eseguito?
Andreas Storvik Strauman,
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.