Comment récupérer le chemin d'un module?


757

Je veux détecter si le module a changé. Maintenant, utiliser inotify est simple, il vous suffit de connaître le répertoire dont vous souhaitez recevoir les notifications.

Comment récupérer le chemin d'un module en python?



9
Si vous recherchez toujours sur ce site, veuillez mettre à jour la bonne réponse à cela . Il est beaucoup plus propre que la solution proposée et il fonctionne également dans les cas où il __file__n'est pas réglé.
erikbwork

3
@ erikb85: ce n'est pas seulement plus propre; inspect-based solution fonctionne également pour le execfile()cas où __file__produit un mauvais nom en silence.
jfs


import pathlib, module; pathlib.Path(module.__file__).resolve().parentCeci est
indépendant de la

Réponses:


907
import a_module
print(a_module.__file__)

Vous donnera en fait le chemin du fichier .pyc qui a été chargé, au moins sur Mac OS X. Je suppose donc que vous pouvez le faire:

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

Vous pouvez également essayer:

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

Pour obtenir le répertoire du module.


54
cela explique comment obtenir le chemin du module que vous importez, mais pas du module / script dans lequel vous vous trouvez (pour le script que vous exécutez, ce __file__n'est pas un chemin complet, il est relatif). Pour le fichier dans lequel je me trouve, j'ai dû importer un autre module du même répertoire et faire comme indiqué ici. Quelqu'un connaît-il un moyen plus pratique?
Ben Bryant

5
@hbdgaf à peu près sûr qu'il n'y a rien de tel qu'un intégréself.__file__
Dan Passaro

26
@BenBryant @hbdgaf os.path.dirname(__file__)fonctionne très bien pour moi et renvoie le chemin abs du répertoire du module.
Niccolò

9
J'ai essayé de faire cela et d'obtenir le retraçage: AttributeError: 'module' object has no attribute '__file__'
Dorian Dore

5
@ DorianDore Je jouais un peu avec les modules et path = module.__path__.__dict__["_path"][0]je suis parvenu à une solution , mais je ne suis pas sûr non plus qu'il soit portable ou qu'il ne diffère pas entre les versions de python. Ça marche pour moi, contrairement à cette réponse qui me donne la même erreur et la inspectréponse soulève TypeError: <module 'module' (namespace)> is a built-in module...
Jezor

279

Il y a inspect module en python.

Documentation officielle

Le module d'inspection fournit plusieurs fonctions utiles pour vous aider à obtenir des informations sur les objets actifs tels que les modules, les classes, les méthodes, les fonctions, les retraits, les objets de trame et les objets de code. Par exemple, il peut vous aider à examiner le contenu d'une classe, à récupérer le code source d'une méthode, à extraire et à formater la liste d'arguments pour une fonction, ou à obtenir toutes les informations dont vous avez besoin pour afficher un suivi détaillé.

Exemple:

>>> 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
Vous pouvez également utiliser inspectpour obtenir le nom du fichier actuel; voir stackoverflow.com/a/50905/320036
z0r

5
Je googlé cette question à plusieurs reprises et c'est la réponse la plus raisonnable que j'ai jamais vu! Veuillez mettre à jour les informations concernantinspect.currentframe()
erikbwork

l' inspect.getfile()approche ne fonctionne pas avec le module _io, mais fonctionne avec le module io.
smwikipedia

70

Comme l'ont dit les autres réponses, la meilleure façon de le faire est de __file__(démontré à nouveau ci-dessous). Cependant, il y a une mise en garde importante, qui __file__n'existe PAS si vous exécutez le module seul (c'est-à-dire as __main__).

Par exemple, supposons que vous ayez deux fichiers (tous deux sur votre PYTHONPATH):

#/path1/foo.py
import bar
print(bar.__file__)

et

#/path2/bar.py
import os
print(os.getcwd())
print(__file__)

L'exécution de foo.py donnera la sortie:

/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

TOUTEFOIS, si vous essayez d'exécuter bar.py seul, vous obtiendrez:

/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 

J'espère que cela t'aides. Cette mise en garde m'a coûté beaucoup de temps et de confusion lors du test des autres solutions présentées.


5
Dans ce cas, vous pouvez utiliser sys.argv [0] à la place du fichier .
Jimothy

4
Est-ce une mise en garde spécifique à la version? En 2.6 et 2.7, je me fie avec succès au fichier , qui fonctionne avec le fichier lorsque le nom __ == '__ principal '. Le seul cas d'échec que j'ai vu est avec "python -c ' fichier d' impression '". J'ajouterai que parfois le fichier peut être '<stdin>', ce qui se produit lorsque des IDE comme emacs exécutent le tampon actuel.
Paul Du Bois

1
Notez que les caractères "__" de début et de fin mettent un mot en gras, alors gardez cela à l'esprit lors de la lecture des commentaires précédents :-P
Paul Du Bois

1
@PaulDuBois Vous pouvez l'entourer avec des tics en arrière: ` __file__` devient__file__
fncomp

1
Comment obtenez-vous le NameError? Du Bois @ Paul: J'ai essayé Python et 02/03 à 03/04 __file__est défini mais j'exécuter le fichier Python: python a.py, python -ma, ./a.py.
jfs

43

J'essaierai également d'aborder quelques variantes de cette question:

  1. trouver le chemin du script appelé
  2. trouver le chemin du script en cours d'exécution
  3. trouver le répertoire du script appelé

(Certaines de ces questions ont été posées sur SO, mais ont été fermées en tant que doublons et redirigées ici.)

Mises en garde concernant l'utilisation __file__

Pour un module que vous avez importé:

import something
something.__file__ 

renverra le chemin absolu du module. Cependant, étant donné le script foo.py suivant:

#foo.py
print '__file__', __file__

L'appeler avec 'python foo.py' retournera simplement 'foo.py'. Si vous ajoutez un shebang:

#!/usr/bin/python 
#foo.py
print '__file__', __file__

et appelez-le en utilisant ./foo.py, il renverra './foo.py'. L'appeler à partir d'un répertoire différent (par exemple, mettre foo.py dans la barre de répertoire), puis appeler soit

python bar/foo.py

ou en ajoutant un shebang et en exécutant le fichier directement:

bar/foo.py

renverra «bar / foo.py» (le parent chemin ).

Trouver le répertoire

Maintenant, aller à partir de là pour obtenir le répertoire, os.path.dirname(__file__)peut aussi être difficile. Au moins sur mon système, il renvoie une chaîne vide si vous l'appelez depuis le même répertoire que le fichier. ex.

# foo.py
import os
print '__file__ is:', __file__
print 'os.path.dirname(__file__) is:', os.path.dirname(__file__)

affichera:

__file__ is: foo.py
os.path.dirname(__file__) is: 

En d'autres termes, il renvoie une chaîne vide, donc cela ne semble pas fiable si vous souhaitez l'utiliser pour le fichier actuel (par opposition au fichier d'un module importé). Pour contourner cela, vous pouvez l'envelopper dans un appel à 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__))

qui génère quelque chose comme:

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

Notez que abspath () ne résout PAS les liens symboliques. Si vous voulez le faire, utilisez plutôt realpath (). Par exemple, créer un lien symbolique file_import_testing_link pointant vers file_import_testing.py, avec le contenu suivant:

import os
print 'abspath(__file__)',os.path.abspath(__file__)
print 'realpath(__file__)',os.path.realpath(__file__)

l'exécution affichera des chemins absolus quelque chose comme:

abspath(__file__) /home/user/file_test_link
realpath(__file__) /home/user/file_test.py

file_import_testing_link -> file_import_testing.py

Utilisation d'inspecter

@SummerBreeze mentionne l'utilisation de l' inspection module d' .

Cela semble bien fonctionner et est assez concis pour les modules importés:

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

renvoie docilement le chemin absolu. Cependant, pour trouver le chemin du script en cours d'exécution, je n'ai pas vu de moyen de l'utiliser.


1
inspect.getfile (os) est identique à os .__ file__ du code: def getfile (object): "" "Détermine dans quel fichier source ou compilé un objet a été défini." "" if ismodule (object): if hasattr (objet, ' fichier '): renvoyer l'objet .__ fichier__
idanzalz

4
Vous pouvez utiliser inspect.getfile(inspect.currentframe())pour obtenir le chemin du script en cours d'exécution.
jbochi

33

Je ne comprends pas pourquoi personne n'en parle, mais pour moi, la solution la plus simple consiste à utiliser imp.find_module ("modulename") (documentation ici ):

import imp
imp.find_module("os")

Il donne un tuple avec le chemin en deuxième position:

(<open file '/usr/lib/python2.7/os.py', mode 'U' at 0x7f44528d7540>,
'/usr/lib/python2.7/os.py',
('.py', 'U', 1))

L'avantage de cette méthode sur celle "inspecter" est que vous n'avez pas besoin d'importer le module pour le faire fonctionner, et vous pouvez utiliser une chaîne en entrée. Utile lors de la vérification de modules appelés dans un autre script par exemple.

MODIFIER :

En python3, le importlibmodule devrait faire:

Doc de importlib.util.find_spec:

Renvoie la spécification du module spécifié.

Tout d'abord, sys.modules est vérifié pour voir si le module a déjà été importé. Si c'est le cas, alors sys.modules [nom]. spec est retourné. Si cela se trouve être défini sur Aucun, ValueError est déclenché. Si le module n'est pas dans sys.modules, alors sys.meta_path est recherché pour une spécification appropriée avec la valeur de 'path' donnée aux chercheurs. Aucun n'est retourné si aucune spécification n'a pu être trouvée.

Si le nom est pour le sous-module (contient un point), le module parent est automatiquement importé.

Le nom et les arguments du package fonctionnent de la même manière que importlib.import_module (). En d'autres termes, les noms de modules relatifs (avec des points en tête) fonctionnent.


2
imp n'est PAS déprécié en python 2 (version actuelle 2.7.13). imp est amorti en python 3 depuis la 3.4. importlib doit être utilisé à la place en python 3. J'aime cette solution, car elle fonctionne même lorsque l'importation réelle échoue (par exemple parce que le module 64 bits pour un moteur 32 bits)
mdew

Et vraiment sympa lorsque vous essayez de trouver le chemin de sqlite3 64 bits et que l'importation échoue. Parfait.
rahvin_t

2
importlib.machinery.PathFinder().find_module("os").get_filename()Alternative la plus courte à celle imp.find_moduleque j'ai trouvée en Python3 +. Si quelqu'un cherche à utiliser importlib.util.find_spec.
Torxed

Cette méthode fonctionne sans importer le module réel, ce qui est formidable car j'utilise ceci pour déterminer quelle version d'un module est importée à partir d'un ordinateur partagé.
Gouda

19

C'était insignifiant.

Chaque module a une __file__variable qui montre son chemin relatif d'où vous vous trouvez en ce moment.

Par conséquent, obtenir un répertoire pour que le module le notifie est simple:

os.path.dirname(__file__)

14
Presque mais pas tout à fait raison - le fichier n'est pas "relatif à l'endroit où vous vous trouvez en ce moment"; quand il est relatif (ce ne sera que lorsqu'il y a des chemins relatifs dans sys.path), il est relatif à l' endroit où vous étiez lorsque le module a été chargé .
Charles Duffy

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

1
ne fonctionne pas sur mon python 2.6 Linux car il ne __file__s'agit que de dir / test.py, abspath implique que le cwd complète le chemin d'accès qui n'est pas le résultat souhaité, mais si vous importez un module, il m.__file__donne le résultat souhaité.
Ben Bryant

10
import module
print module.__path__

Forfaits un support attribut plus spécial, __path__. Ceci est initialisé pour être une liste contenant le nom du répertoire contenant le package __init__.pyavant l'exécution du code dans ce fichier. Cette variable peut être modifiée; cela affecte les recherches futures des modules et sous-packages contenus dans le package.

Bien que cette fonctionnalité ne soit pas souvent nécessaire, elle peut être utilisée pour étendre l'ensemble des modules contenus dans un package.

La source


9

Utilitaire de ligne de commande

Vous pouvez le modifier pour un utilitaire de ligne de commande,

python-which <package name>

entrez la description de l'image ici


Créer /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__)

Rendez-le exécutable

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

7

J'ai donc passé pas mal de temps à essayer de le faire avec py2exe Le problème était d'obtenir le dossier de base du script, qu'il soit exécuté en tant que script python ou en tant qu'exécutable py2exe. Pour le faire fonctionner, qu'il soit exécuté à partir du dossier actuel, d'un autre dossier ou (c'était le plus difficile) à partir du chemin du système.

Finalement, j'ai utilisé cette approche, en utilisant sys.frozen comme indicateur de l'exécution dans 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

vous pouvez simplement importer votre module puis frapper son nom et vous obtiendrez son chemin complet

>>> import os
>>> os
<module 'os' from 'C:\\Users\\Hassan Ashraf\\AppData\\Local\\Programs\\Python\\Python36-32\\lib\\os.py'>
>>>

4

Si vous souhaitez récupérer le chemin racine du package à partir de l'un de ses modules, les travaux suivants (testés sur Python 3.6):

from . import __path__ as ROOT_PATH
print(ROOT_PATH)

Le principal __init__.py chemin peut également être référencé en utilisant à la __file__place.

J'espère que cela t'aides!


3

Si la seule mise en garde de l'utilisation __file__est à jour, le répertoire relatif est vide (c'est-à-dire lors de l'exécution en tant que script à partir du même répertoire où le script est), alors une solution triviale est:

import os.path
mydir = os.path.dirname(__file__) or '.'
full  = os.path.abspath(mydir)
print __file__, mydir, full

Et le résultat:

$ python teste.py 
teste.py . /home/user/work/teste

L'astuce est or '.'après l' dirname()appel. Il définit le dir comme ., ce qui signifie le répertoire courant et est un répertoire valide pour toute fonction liée au chemin.

Ainsi, l'utilisation abspath()n'est pas vraiment nécessaire. Mais si vous l'utilisez quand même, l'astuce n'est pas nécessaire: abspath()accepte les chemins vides et l'interprète correctement comme le répertoire courant.


Mieux vaut échanger la commande et simplement l'utiliser os.path.dirname(os.path.abspath(__file__))parce qu'alors vous n'avez plus à vous soucier des chemins relatifs. Une opportunité de moins pour les bugs.
szali

3

Je voudrais contribuer avec un scénario commun (en Python 3) et explorer quelques approches.

La fonction intégrée open () accepte le chemin relatif ou absolu comme premier argument. Le chemin relatif est cependant traité par rapport au répertoire de travail actuel , il est donc recommandé de passer le chemin absolu au fichier.

En termes simples, si vous exécutez un fichier de script avec le code suivant, il n'est pas garanti que le example.txtfichier sera créé dans le même répertoire où se trouve le fichier de script:

with open('example.txt', 'w'):
    pass

Pour corriger ce code, nous devons obtenir le chemin d'accès au script et le rendre absolu. Pour garantir que le chemin soit absolu, nous utilisons simplement la fonction os.path.realpath () . Pour obtenir le chemin d'accès au script, il existe plusieurs fonctions courantes qui renvoient divers résultats de chemin d'accès:

  • os.getcwd()
  • os.path.realpath('example.txt')
  • sys.argv[0]
  • __file__

Les deux fonctions os.getcwd () et os.path.realpath () renvoient des résultats de chemin basés sur le répertoire de travail actuel . Généralement pas ce que nous voulons. Le premier élément de la liste sys.argv est le chemin du script racine (le script que vous exécutez), que vous appeliez la liste dans le script racine lui-même ou dans l'un de ses modules. Cela peut être utile dans certaines situations. La variable __file__ contient le chemin du module à partir duquel elle a été appelée.


Le code suivant crée correctement un fichier example.txtdans le même répertoire où se trouve le script:

filedir = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(filedir, 'example.txt')

with open(filepath, 'w'):
    pass

3

Si vous souhaitez connaître le chemin absolu de votre script, vous pouvez utiliser l' objet Path :

from pathlib import Path

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

méthode cwd ()

Renvoie un nouvel objet path représentant le répertoire courant (tel que renvoyé par os.getcwd ())

Méthode resolver ()

Rendre le chemin absolu, résoudre tous les liens symboliques. Un nouvel objet chemin est renvoyé:


1

Depuis les modules d'un package python, j'ai dû me référer à un fichier qui résidait dans le même répertoire que le package. 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

Donc, ci-dessus, j'ai dû appeler maincli.py depuis le module my_lib_a.py sachant que top_package et maincli.py sont dans le même répertoire. Voici comment obtenir le chemin d'accès à 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

Sur la base de la publication par PlasmaBinturong, j'ai modifié le code.


1

Si vous souhaitez le faire dynamiquement dans un "programme" essayez ce code:
Mon point est que vous ne connaissez peut-être pas le nom exact du module pour le "coder en dur". Il peut être sélectionné dans une liste ou ne pas être en cours d'exécution pour utiliser __file__.

(Je sais, cela ne fonctionnera pas en 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'>

J'ai essayé de me débarrasser du problème "global" mais j'ai trouvé des cas où cela ne fonctionnait pas. Je pense que "execfile ()" peut être émulé en Python 3 Comme c'est dans un programme, il peut facilement être mis dans une méthode ou un module pour réutilisation.


0

Si vous l'avez installé à l'aide de pip, "pip show" fonctionne très bien ("Emplacement")

$ 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

Voici un script bash rapide au cas où il serait utile à quiconque. Je veux juste pouvoir définir une variable d'environnement pour que je puisse pushdle code.

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

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

Exemple de 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 ~]#
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.