Come recuperare il percorso di un modulo?


757

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?


1
Dai un'occhiata a modulefinder: docs.python.org/library/modulefinder.html

9
Se stai ancora cercando su questo sito, ti preghiamo di aggiornare la risposta corretta a questo . È molto più pulito della soluzione proposta e funziona anche nei casi in cui __file__non è impostato.
erikbwork,

3
@erikb85: non è solo più pulito; inspectLa soluzione basata su execfile()caso funziona anche per caso quando __file__produce silenziosamente un nome sbagliato.
jfs,


import pathlib, module; pathlib.Path(module.__file__).resolve().parentQuesto è indipendente dalla piattaforma
MortenB

Risposte:


907
import a_module
print(a_module.__file__)

In realtà ti darà il percorso del file .pyc che è stato caricato, almeno su Mac OS X. Quindi immagino che tu possa fare:

import os
path = os.path.abspath(a_module.__file__)

Puoi anche provare:

path = os.path.dirname(a_module.__file__)

Per ottenere la directory del modulo.


54
questo risponde come ottenere il percorso del modulo importato, ma non del modulo / script in cui ci si trova (per lo script in esecuzione, __file__non è un percorso completo, è relativo). Per il file in cui mi trovavo ho dovuto importare un altro modulo dalla stessa directory e fare come mostrato qui. Qualcuno conosce un modo più conveniente?
Ben Bryant,

5
@hbdgaf è abbastanza sicuro che non esiste un built-inself.__file__
Dan Passaro il

26
@BenBryant @hbdgaf os.path.dirname(__file__)funziona bene per me e restituisce il percorso abs della directory del modulo.
Niccolò,

9
Ho provato a fare questo e ottenere il traceback: AttributeError: 'module' object has no attribute '__file__'
Dorian Dore

5
@DorianDore Stavo scherzando un po 'con i moduli e sono arrivato a una soluzione path = module.__path__.__dict__["_path"][0], ma non sono sicuro che sia portatile o che non differisca tra le versioni di Python. Funziona per me, a differenza di questa risposta che mi dà lo stesso errore e la inspectrisposta aumenta TypeError: <module 'module' (namespace)> is a built-in module...
Jezor

279

C'è un inspectmodulo in Python.

Documentazione ufficiale

Il modulo inspect offre diverse utili funzioni per ottenere informazioni su oggetti live come moduli, classi, metodi, funzioni, traceback, frame frame e oggetti code. Ad esempio, può aiutarti a esaminare il contenuto di una classe, recuperare il codice sorgente di un metodo, estrarre e formattare l'elenco degli argomenti per una funzione o ottenere 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'

7
Puoi usare anche inspectper ottenere il nome del file corrente; vedi stackoverflow.com/a/50905/320036
z0r

5
Ho cercato su Google questa domanda molte volte e questa è la risposta più ragionevole che abbia mai visto! Aggiorna le informazioni suinspect.currentframe()
erikbwork il

l' inspect.getfile()approccio non funziona con il modulo _io, ma funziona con il modulo io.
smwikipedia,

70

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 sta eseguendo 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 sia di aiuto. Questo avvertimento mi è costato molto tempo e confusione durante il test delle altre soluzioni presentate.


5
In questo caso, è possibile utilizzare sys.argv [0] al posto del file .
Jimothy,

4
È un avvertimento specifico per la versione? In 2.6 e 2.7, faccio affidamento sul file , che funziona quando il nome __ == '__ main '. L'unico caso di errore che ho visto è con "python -c ' file di stampa '". Aggiungerò che a volte il file può essere '<stdin>', il che accade quando IDE come emacs eseguono il buffer corrente.
Paul Du Bois,

1
Nota che i caratteri "__" iniziali e finali mettono una parola in grassetto, quindi tienilo a mente quando leggi i commenti precedenti :-P
Paul Du Bois,

1
@PaulDuBois Puoi circondarlo senza tic: ` __file__` diventa__file__
fncomp

1
Come si ottiene NameError? @ Paolo Du Bois: ho provato Python 2,3-3,4 ed __file__è definito comunque ho eseguito il file Python: python a.py, python -ma, ./a.py.
jfs,

43

Proverò ad affrontare alcune variazioni anche su questa domanda:

  1. trovare il percorso dello script chiamato
  2. trovare il percorso dello script attualmente in esecuzione
  3. trovare la directory dello script chiamato

(Alcune di queste domande sono state poste su SO, ma sono state chiuse come duplicati e reindirizzate qui.)

Avvertenze sull'uso __file__

Per un modulo che hai importato:

import something
something.__file__ 

restituirà il percorso assoluto del modulo. Tuttavia, dato lo script folowing foo.py:

#foo.py
print '__file__', __file__

Chiamandolo con 'python foo.py' tornerà 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 uno dei due

python bar/foo.py

o aggiungendo uno shebang ed eseguendo direttamente il file:

bar/foo.py

restituirà 'bar / foo.py' (il percorso relativo ).

Trovare la directory

Ora andare 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. ex.

# 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 ciò non sembra affidabile se si desidera utilizzarlo per il file corrente (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 produce qualcosa di simile a:

os.path.abspath(__file__) is: /home/user/bar/foo.py
os.path.dirname(os.path.abspath(__file__)) is: /home/user/bar

Si noti 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

Usando inspect

@SummerBreeze menzioni utilizzando il ispezionare modulo.

Questo sembra funzionare bene, ed è abbastanza conciso, per i moduli importati:

import os
import inspect
print 'inspect.getfile(os) is:', inspect.getfile(os)

obbedientemente restituisce il percorso assoluto. Tuttavia, per trovare il percorso dello script attualmente in esecuzione, non ho visto un modo per usarlo.


1
inspect.getfile (os) è uguale a os .__ file__ dal codice: def getfile (oggetto): "" "Calcola in quale sorgente o file compilato è stato definito un oggetto." "" if ismodule (object): if hasattr (object, ' file '): return object .__ file__
idanzalz

4
È possibile utilizzare inspect.getfile(inspect.currentframe())per ottenere il percorso dello script attualmente in esecuzione.
jbochi,

33

Non capisco perché nessuno ne parli, ma per me la soluzione più semplice sta usando imp.find_module ("modulename") (documentazione qui ):

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 quello "inspect" è 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 importlibmodulo 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, sys.modules [nome]. specviene restituita la . Se sembra che sia impostato su Nessuno, viene generato ValueError. Se il modulo non è in sys.modules, allora sys.meta_path viene cercata una specifica adatta con il valore di 'percorso' dato ai cercatori. Nessuno viene restituito se non è possibile trovare alcuna specifica.

Se il nome è per sottomodulo (contiene un punto), il modulo padre viene importato automaticamente.

Gli argomenti name e package funzionano allo stesso modo di importlib.import_module (). In altre parole, i nomi dei moduli relativi (con punti iniziali) funzionano.


2
imp non è ammortizzato in python 2 (versione corrente 2.7.13). imp è ammortizzato in Python 3 dal 3.4. invece importlib deve essere usato in Python 3. Mi piace questa soluzione, perché funziona anche quando l'importazione effettiva fallirà (ad es. Perché modulo a 64 bit per un motore a 32 bit)
mdew

E DAVVERO bello quando si cerca di trovare il percorso di sqlite3 a 64 bit e l'importazione non riesce. Perfetto.
rahvin_t,

2
importlib.machinery.PathFinder().find_module("os").get_filename()La più breve alternativa a quella imp.find_moduleche ho trovato in Python3 +. Se qualcuno sta cercando l'uso di importlib.util.find_spec.
Torxato il

Questo metodo funziona senza importare il modulo reale, il che è fantastico poiché lo sto usando per capire quale versione di un modulo viene importata da un computer condiviso.
Gouda

19

Questo è stato banale.

Ogni modulo ha una __file__variabile 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__)

14
Quasi ma non del tutto a posto - il file non è "relativo a dove ti trovi adesso"; quando è relativo (che sarà solo quando ci sono percorsi relativi in ​​sys.path), è relativo a dove eri quando il modulo è stato caricato .
Charles Duffy,

17
import os
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)

1
non funziona sul mio Linux Python 2.6 poiché __file__è solo dir / test.py, abspath coinvolge il cwd per completare il percorso che non è il risultato desiderato, ma se si importa un modulo, si m.__file__ottiene il risultato desiderato.
Ben Bryant,

10
import module
print module.__path__

Pacchetti supportano un attributo più speciale, __path__. Questo è inizializzato per essere un elenco contenente il nome della directory contenente il pacchetto __init__.pyprima dell'esecuzione del codice in quel file. Questa variabile può essere modificata; ciò influisce sulle ricerche future di moduli e pacchetti secondari contenuti nel pacchetto.

Sebbene questa funzione non sia spesso necessaria, può essere utilizzata per estendere il set di moduli presenti in un pacchetto.

fonte


9

Utilità da riga di comando

Puoi modificarlo in un'utilità della riga di comando,

python-which <package name>

inserisci qui la descrizione dell'immagine


Creare /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__)

Renderlo eseguibile

sudo chmod +x /usr/local/bin/python-which

7

Quindi ho trascorso un bel po 'di tempo cercando di farlo con py2exe Il problema era ottenere la cartella base dello script sia che venisse 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__))

7

puoi semplicemente importare il tuo modulo quindi premere 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'>
>>>

4

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)

__init__.pyÈ inoltre possibile fare riferimento al percorso principale utilizzando__file__ invece.

Spero che sia di aiuto!


3

Se l'unico avvertimento dell'utilizzo __file__è quando corrente, la relativa directory è vuota (cioè, quando viene eseguita come 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 è or '.'dopo la dirname()chiamata. Imposta dir come ., che significa directory corrente ed è una directory valida per qualsiasi funzione relativa al percorso.

Pertanto, l'utilizzo abspath()non è veramente necessario. Ma se lo usi comunque, il trucco non è necessario: abspath()accetta percorsi vuoti e lo interpreta correttamente come directory corrente.


Meglio scambiare l'ordine e usarlo solo os.path.dirname(os.path.abspath(__file__))perché non devi preoccuparti dei percorsi relativi. Un'opportunità in meno per i bug.
szali

3

Vorrei contribuire con uno scenario comune (in Python 3) ed esplorare alcuni approcci ad esso.

La funzione integrata open () accetta il percorso relativo o assoluto come primo argomento. Il percorso relativo viene trattato come relativo alla directory di lavoro corrente quindi si consiglia di passare il percorso assoluto al file.

È presto detto, se si esegue un file di script con il seguente codice, è non è garantito che il example.txtfile verrà creato nella stessa directory in cui si trova il file di script:

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 la funzione 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]
  • __file__

Entrambe le funzioni os.getcwd () e os.path.realpath () restituiscono i risultati del percorso in base alla directory di lavoro corrente . Generalmente non quello che vogliamo. Il primo elemento dell'elenco sys.argv è il percorso dello script di root (lo script che si esegue) indipendentemente dal fatto che si 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.txtnella 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

3

Se desideri conoscere il percorso assoluto del tuo script, puoi utilizzare l' oggetto Path :

from pathlib import Path

print(Path().absolute())
print(Path().resolve('.'))
print(Path().cwd())

metodo cwd ()

Restituisce un nuovo oggetto path che rappresenta la directory corrente (come restituito da os.getcwd ())

metodo di risoluzione ()

Rendi assoluto il percorso, risolvendo eventuali collegamenti simbolici. Viene restituito un nuovo oggetto path:


1

Dall'interno dei moduli di un pacchetto Python ho dovuto fare riferimento a un file che risiedeva nella stessa directory del pacchetto. Ex.

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.


1

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 "codificarlo". 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 liberarmi del problema "globale", ma ho trovato casi in cui non funzionava, penso che "execfile ()" possa essere emulato in Python 3 Dato che si trova in un programma, può essere facilmente inserito in un metodo o modulo per riutilizzare.


0

Se lo hai installato usando pip, "pip show" funziona alla grande ("Posizione")

$ pip show detectron2

Name: detectron2
Version: 0.1
Summary: Detectron2 is FAIR next-generation research platform for object detection and segmentation.
Home-page: https://github.com/facebookresearch/detectron2
Author: FAIR
Author-email: None
License: UNKNOWN
Location: /home/ubuntu/anaconda3/envs/pytorch_p36/lib/python3.6/site-packages
Requires: yacs, tabulate, tqdm, pydot, tensorboard, Pillow, termcolor, future, cloudpickle, matplotlib, fvcore

0

Ecco uno script bash rapido nel caso sia utile a chiunque. Voglio solo essere in grado di impostare una variabile di ambiente in modo che io possa pushdal codice.

#!/bin/bash
module=${1:?"I need a module name"}

python << EOI
import $module
import os
print os.path.dirname($module.__file__)
EOI

Esempio di shell:

[root@sri-4625-0004 ~]# export LXML=$(get_python_path.sh lxml)
[root@sri-4625-0004 ~]# echo $LXML
/usr/lib64/python2.7/site-packages/lxml
[root@sri-4625-0004 ~]#
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.