Come recuperare il percorso di un modulo?
Domanda
Voglio rilevare se il modulo è cambiato. Ora, usare inotify è semplice, devi solo conoscere la directory da cui vuoi ricevere le notifiche.
Come posso recuperare il percorso di un modulo in Python?
Soluzione
import a_module
print(a_module.__file__)
Ti fornirà effettivamente il percorso del file .pyc che è stato caricato, almeno su Mac OS X. Quindi immagino che tu possa fare:
import os
path = os.path.dirname(a_module.__file__)
Puoi anche provare:
path = os.path.abspath(a_module.__file__)
Per ottenere la directory del modulo.
Altri suggerimenti
Esiste il modulo inspect
in Python.
Documentazione ufficiale
Il modulo inspect offre diverse utili funzioni per ottenere informazioni su oggetti live come moduli, classi, metodi, funzioni, traceback, oggetti frame e oggetti code. Per esempio, può aiutarti a esaminare il contenuto di una classe, recuperare la fonte codice di un metodo, estrarre e formattare l'elenco degli argomenti per una funzione, oppure ottieni tutte le informazioni necessarie per visualizzare un traceback dettagliato.
Esempio:
>>> import os
>>> import inspect
>>> inspect.getfile(os)
'/usr/lib64/python2.7/os.pyc'
>>> inspect.getfile(inspect)
'/usr/lib64/python2.7/inspect.pyc'
>>> os.path.dirname(inspect.getfile(inspect))
'/usr/lib64/python2.7'
Come hanno detto le altre risposte, il modo migliore per farlo è con __file__
(di nuovo dimostrato di seguito). Tuttavia, esiste un avvertimento importante, ovvero che __file__
NON esiste se si esegue il modulo da solo (ovvero come __main__
).
Ad esempio, supponi di avere due file (entrambi presenti sul tuo PYTHONPATH):
#/path1/foo.py
import bar
print(bar.__file__)
e
#/path2/bar.py
import os
print(os.getcwd())
print(__file__)
L'esecuzione di foo.py darà l'output:
/path1 # "import bar" causes the line "print(os.getcwd())" to run
/path2/bar.py # then "print(__file__)" runs
/path2/bar.py # then the import statement finishes and "print(bar.__file__)" runs
TUTTAVIA se provi a eseguire bar.py da solo, otterrai:
/path2 # "print(os.getcwd())" still works fine
Traceback (most recent call last): # but __file__ doesn't exist if bar.py is running as main
File "/path2/bar.py", line 3, in <module>
print(__file__)
NameError: name '__file__' is not defined
Spero che questo aiuti. Questo avvertimento mi è costato molto tempo e confusione durante il test delle altre soluzioni presentate.
Proverò ad affrontare alcune variazioni anche su questa domanda:
- trova il percorso dello script chiamato
- trova il percorso dello script attualmente in esecuzione
- trova la directory dello script chiamato
(Alcune di queste domande sono state poste su SO, ma sono state chiuse come duplicate e reindirizzate qui.)
Avvertenze sull'uso di __file__
Per un modulo che hai importato:
import something
something.__file__
restituirà il assoluto percorso del modulo. Tuttavia, dato lo script folowing foo.py:
#foo.py
print '__file__', __file__
Chiamandolo con 'python foo.py' Restituirà semplicemente 'foo.py'. Se aggiungi un shebang:
#!/usr/bin/python
#foo.py
print '__file__', __file__
e chiamalo usando ./foo.py, restituirà './foo.py'. Chiamandolo da una directory diversa, (ad esempio metti foo.py nella barra delle directory), quindi chiamando
python bar/foo.py
o aggiungendo uno shebang ed eseguendo direttamente il file:
bar/foo.py
restituirà 'bar / foo.py' (il percorso relativo).
Ricerca della directory
Ora andando da lì per ottenere la directory, os.path.dirname (__ file__)
può anche essere complicato. Almeno sul mio sistema, restituisce una stringa vuota se la chiami dalla stessa directory del file. es.
# foo.py
import os
print '__file__ is:', __file__
print 'os.path.dirname(__file__) is:', os.path.dirname(__file__)
produrrà:
__file__ is: foo.py
os.path.dirname(__file__) is:
In altre parole, restituisce una stringa vuota, quindi questo non sembra affidabile se si desidera utilizzarlo per l'attuale file (al contrario del file di un modulo importato). Per aggirare questo, puoi avvolgerlo in una chiamata a abspath:
# foo.py
import os
print 'os.path.abspath(__file__) is:', os.path.abspath(__file__)
print 'os.path.dirname(os.path.abspath(__file__)) is:', os.path.dirname(os.path.abspath(__file__))
che genera qualcosa come:
os.path.abspath(__file__) is: /home/user/bar/foo.py
os.path.dirname(os.path.abspath(__file__)) is: /home/user/bar
Nota che abspath () NON risolve i collegamenti simbolici. Se vuoi farlo, usa invece realpath (). Ad esempio, creare un link simbolico file_import_testing_link che punta a file_import_testing.py, con il seguente contenuto:
import os
print 'abspath(__file__)',os.path.abspath(__file__)
print 'realpath(__file__)',os.path.realpath(__file__)
l'esecuzione stamperà percorsi assoluti come:
abspath(__file__) /home/user/file_test_link
realpath(__file__) /home/user/file_test.py
file_import_testing_link - > file_import_testing.py
Uso di inspect
@SummerBreeze menziona l'uso del modulo inspect .
Questo sembra funzionare bene, ed è abbastanza conciso, per i moduli importati:
import os
import inspect
print 'inspect.getfile(os) is:', inspect.getfile(os)
restituisce obbedientemente il percorso assoluto. Tuttavia, per trovare il percorso dello script attualmente in esecuzione, non ho visto un modo per usarlo.
Non capisco perché nessuno ne parli, ma per me la soluzione più semplice sta usando imp.find_module (" modulename ") (documentazione here ):
import imp
imp.find_module("os")
Dà una tupla con il percorso in seconda posizione:
(<open file '/usr/lib/python2.7/os.py', mode 'U' at 0x7f44528d7540>,
'/usr/lib/python2.7/os.py',
('.py', 'U', 1))
Il vantaggio di questo metodo rispetto a "ispeziona" uno è che non è necessario importare il modulo per farlo funzionare ed è possibile utilizzare una stringa in input. Utile quando si controllano moduli chiamati ad esempio in un altro script.
Modifica :
In python3, il modulo importlib
dovrebbe fare:
Doc di importlib.util.find_spec
:
Restituisce le specifiche per il modulo specificato.
Innanzitutto, sys.modules viene controllato per vedere se il modulo è già stato importato. In tal caso, viene restituito sys.modules [nome]. specifica . Se succede impostato su Nessuno, quindi viene generato ValueError. Se il modulo non è presente sys.modules, quindi sys.meta_path viene cercata una specifica adatta con valore del 'percorso' dato ai cercatori. Nessuno viene restituito se nessuna specifica poteva essere trovato.
Se il nome è per sottomodulo (contiene un punto), lo è il modulo genitore importato automaticamente.
Il nome e gli argomenti del pacchetto funzionano allo stesso modo di importlib.import_module (). In altre parole, i nomi dei moduli relativi (con punti iniziali) funzionano.
Questo è stato banale.
Ogni modulo ha una variabile __file__
che mostra il suo percorso relativo da dove ti trovi adesso.
Pertanto, ottenere una directory per la notifica al modulo è semplice come:
os.path.dirname(__file__)
import os
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)
Utilità da riga di comando
Puoi modificarlo in un'utilità della riga di comando,
python-which <package name>
Crea /usr/local/bin/python-which
#!/usr/bin/env python
import importlib
import os
import sys
args = sys.argv[1:]
if len(args) > 0:
module = importlib.import_module(args[0])
print os.path.dirname(module.__file__)
Rendi eseguibile
sudo chmod +x /usr/local/bin/python-which
Quindi ho trascorso un bel po 'di tempo cercando di farlo con py2exe Il problema era ottenere la cartella di base dello script se veniva eseguito come script Python o come eseguibile py2exe. Inoltre, per farlo funzionare indipendentemente dal fatto che fosse eseguito dalla cartella corrente, da un'altra cartella o (questa era la più difficile) dal percorso del sistema.
Alla fine ho usato questo approccio, usando sys.frozen come indicatore di esecuzione in py2exe:
import os,sys
if hasattr(sys,'frozen'): # only when running in py2exe this exists
base = sys.prefix
else: # otherwise this is a regular python script
base = os.path.dirname(os.path.realpath(__file__))
import module
print module.__path__
I pacchetti supportano un altro attributo speciale,
__path__
. Questo è inizializzato per essere un elenco contenente il nome della directory holding il__init__.py
del pacchetto prima dell'esecuzione del codice in quel file. Questa variabile può essere modificata; ciò influisce sulle ricerche future per moduli e pacchetti secondari contenuti nel pacchetto.Sebbene questa funzione non sia spesso necessaria, può essere utilizzata per estendere il insieme di moduli trovati in un pacchetto.
puoi semplicemente importare il tuo modulo quindi premi il suo nome e otterrai il suo percorso completo
>>> import os
>>> os
<module 'os' from 'C:\\Users\\Hassan Ashraf\\AppData\\Local\\Programs\\Python\\Python36-32\\lib\\os.py'>
>>>
Se l'unico avvertimento dell'uso di __file__
è quando corrente, la relativa directory è vuota (cioè, quando viene eseguita come uno script dalla stessa directory in cui si trova lo script), allora una soluzione banale è:
import os.path
mydir = os.path.dirname(__file__) or '.'
full = os.path.abspath(mydir)
print __file__, mydir, full
E il risultato:
$ python teste.py
teste.py . /home/user/work/teste
Il trucco è in o '.'
dopo la chiamata dirname ()
. Imposta dir come .
, che significa directory corrente ed è una directory valida per qualsiasi funzione relativa al percorso.
Pertanto, l'uso di abspath ()
non è veramente necessario. Ma se lo usi comunque, il trucco non è necessario: abspath ()
accetta i percorsi vuoti e lo interpreta correttamente come la directory corrente.
Vorrei contribuire con uno scenario comune (in Python 3) ed esplorare alcuni approcci ad esso.
La funzione integrata open () accetta entrambi i relativi o percorso assoluto come primo argomento. Il percorso relativo viene trattato come relativo alla directory di lavoro corrente , sebbene sia consigliabile passare il percorso assoluto al file.
Detto semplicemente, se si esegue un file di script con il seguente codice, non è garantito che il file example.txt
verrà creato nella stessa directory in cui il file di script si trova:
with open('example.txt', 'w'):
pass
Per correggere questo codice dobbiamo ottenere il percorso dello script e renderlo assoluto. Per garantire che il percorso sia assoluto, utilizziamo semplicemente il os .path.realpath () . Per ottenere il percorso dello script ci sono diverse funzioni comuni che restituiscono vari risultati del percorso:
-
os.getcwd ()
-
os.path.realpath ( 'example.txt')
-
sys.argv [0]
-
__ di file __
Entrambe le funzioni os.getcwd () e < a href = "https://docs.python.org/3/library/os.path.html#os.path.realpath" rel = "nofollow noreferrer"> os.path.realpath () percorso di ritorno risultati basati sulla directory di lavoro corrente . Generalmente non quello che vogliamo. Il primo elemento dell'elenco sys.argv è < em> percorso dello script di root (lo script che esegui) indipendentemente dal fatto che tu chiami l'elenco nello script di root stesso o in uno dei suoi moduli. Potrebbe tornare utile in alcune situazioni. La variabile __file__ contiene il percorso del modulo da cui è stato chiamato .
Il codice seguente crea correttamente un file example.txt
nella stessa directory in cui si trova lo script:
filedir = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(filedir, 'example.txt')
with open(filepath, 'w'):
pass
Dall'interno dei moduli di un pacchetto Python ho dovuto fare riferimento a un file che risiedeva nella stessa directory del pacchetto. Es.
some_dir/
maincli.py
top_package/
__init__.py
level_one_a/
__init__.py
my_lib_a.py
level_two/
__init__.py
hello_world.py
level_one_b/
__init__.py
my_lib_b.py
Quindi in precedenza ho dovuto chiamare maincli.py dal modulo my_lib_a.py sapendo che top_package e maincli.py sono nella stessa directory. Ecco come ottengo il percorso per maincli.py:
import sys
import os
import imp
class ConfigurationException(Exception):
pass
# inside of my_lib_a.py
def get_maincli_path():
maincli_path = os.path.abspath(imp.find_module('maincli')[1])
# top_package = __package__.split('.')[0]
# mod = sys.modules.get(top_package)
# modfile = mod.__file__
# pkg_in_dir = os.path.dirname(os.path.dirname(os.path.abspath(modfile)))
# maincli_path = os.path.join(pkg_in_dir, 'maincli.py')
if not os.path.exists(maincli_path):
err_msg = 'This script expects that "maincli.py" be installed to the '\
'same directory: "{0}"'.format(maincli_path)
raise ConfigurationException(err_msg)
return maincli_path
Sulla base della pubblicazione di PlasmaBinturong ho modificato il codice.
Se desideri farlo in modo dinamico in un "programma" prova questo codice:
Il mio punto è che potresti non conoscere il nome esatto del modulo per "hardcode" esso.
Potrebbe essere selezionato da un elenco o potrebbe non essere attualmente in esecuzione per utilizzare __file__.
(Lo so, non funzionerà in Python 3)
global modpath
modname = 'os' #This can be any module name on the fly
#Create a file called "modname.py"
f=open("modname.py","w")
f.write("import "+modname+"\n")
f.write("modpath = "+modname+"\n")
f.close()
#Call the file with execfile()
execfile('modname.py')
print modpath
<module 'os' from 'C:\Python27\lib\os.pyc'>
Ho provato a sbarazzarmi del " global " problema ma trovato casi in cui non ha funzionato Penso che " execfile () " può essere emulato in Python 3 Poiché questo è in un programma, può essere facilmente inserito in un metodo o modulo per il riutilizzo.
Se desideri conoscere il percorso assoluto del tuo script, puoi utilizzare Path object:
from pathlib import Path
print(Path().absolute())
print(Path().resolve('.'))
print(Path().cwd())
Restituisce un nuovo oggetto path che rappresenta la directory corrente (come restituito da os.getcwd ())
Rendi assoluto il percorso, risolvendo eventuali collegamenti simbolici. Viene restituito un nuovo oggetto path:
Se vuoi recuperare il percorso root del pacchetto da uno qualsiasi dei suoi moduli, il seguente funziona (testato su Python 3.6):
from . import __path__ as ROOT_PATH
print(ROOT_PATH)
Il percorso principale __init__.py
può anche essere referenziato usando invece __file__
.
Spero che questo aiuti!