216 votes

Comment obtenir le chemin du fichier actuellement exécuté en Python ?

Y a-t-il une approche universelle en Python pour trouver le chemin du fichier actuellement en cours d'exécution?

Approches infructueuses

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

Cela ne fonctionne pas si vous exécutez à partir d'un autre script Python dans un autre répertoire, par exemple en utilisant execfile en 2.x.

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

J'ai constaté que cela ne fonctionne pas dans les cas suivants:

  • py2exe n'a pas d'attribut __file__, même s'il existe une solution de contournement
  • Lorsque le code est exécuté depuis IDLE en utilisant execute(), auquel cas il n'y a pas d'attribut __file__
  • Sous Mac OS X v10.6 (Snow Leopard), j'obtiens NameError: global name '__file__' is not defined

Cas de test

Arborescence du répertoire

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

Contenu de 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")

Contenu de 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

Sortie de python a.py (sur 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

Lié (mais ces réponses ne sont pas complètes)

85voto

Daniel Stutzbach Points 20026

Vous ne pouvez pas déterminer directement l'emplacement du script principal en cours d'exécution. Après tout, parfois le script ne vient pas du tout d'un fichier. Par exemple, il pourrait provenir de l'interpréteur interactif ou du code généré dynamiquement stocké uniquement en mémoire.

Cependant, vous pouvez déterminer de manière fiable l'emplacement d'un module, car les modules sont toujours chargés à partir d'un fichier. Si vous créez un module avec le code suivant et le placez dans le même répertoire que votre script principal, alors le script principal peut importer le module et l'utiliser pour se localiser.

some_path/module_locator.py:

def we_are_frozen():
    # Tous les modules sont intégrés à l'interpréteur, par exemple, par 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))

some_path/main.py:

import module_locator
my_path = module_locator.module_path()

Si vous avez plusieurs scripts principaux dans différents répertoires, vous pourriez avoir besoin de plus d'une copie de module_locator.

Bien sûr, si votre script principal est chargé par un autre outil qui ne vous permet pas d'importer des modules situés au même endroit que votre script, alors vous êtes malchanceux. Dans des cas comme celui-ci, les informations que vous recherchez tout simplement n'existent nulle part dans votre programme. Votre meilleure option serait de signaler un bogue aux auteurs de l'outil.

2 votes

Je mentionne que sur OS 10.6, j'obtiens NameError: global name '__file__' is not defined en utilisant file et ce n'est pas à l'intérieur de l'IDLE. Pensez que __file__ est défini uniquement à l'intérieur des modules.

1 votes

@Sorin Sbarnea : J'ai mis à jour ma réponse avec la façon dont je contourne cela.

2 votes

Merci, mais en fait le problème avec l'absence de __file__ n'avait rien à voir avec Unicode. Je ne sais pas pourquoi __file__ n'est pas défini, mais je cherche une solution générique qui fonctionnera dans tous les cas.

84voto

ArtOfWarfare Points 2345

Tout d'abord, vous devez importer de inspect et os

from inspect import getsourcefile
from os.path import abspath

Ensuite, à l'endroit où vous voulez trouver le fichier source, vous utilisez simplement

abspath(getsourcefile(lambda:0))

1 votes

Meilleure réponse. Merci.

4 votes

Super réponse. Merci. Il semble également être la réponse la plus courte et la plus portable (fonctionnant sur différents systèmes d'exploitation) et ne rencontre pas de problèmes tels que NameError: global name '__file__' is not defined (une autre solution a provoqué cela).

0 votes

Une autre possibilité qui est tout aussi courte: lambda:_. Ça a fonctionné pour moi - pas sûr que ça fonctionnera toujours. lambda:0 fonctionne probablement plus rapidement d'un petit montant incommensurable (ou peut-être pas si petit... pourrait être un chargement immédiat ou quelque chose encore plus rapide pour 0 par rapport à un chargement global pour _?) Contestable si c'est plus propre ou plus facile à lire ou encore plus astucieux/obscur.

15voto

Garrett Berg Points 884

J'avais rencontré un problème similaire, et je pense que cela pourrait résoudre le problème :

def module_path(local_function):
   ''' retourne le chemin du module sans utiliser __file__.  Nécessite une fonction définie
   localement dans le module.
   depuis http://stackoverflow.com/questions/729583/getting-file-path-of-imported-module'''
   return os.path.abspath(inspect.getsourcefile(local_function))

Cela fonctionne pour les scripts réguliers et dans IDLE. Tout ce que je peux dire, c'est d'essayer pour les autres !

Mon utilisation typique :

from toolbox import module_path
def main():
   pass # Faire des choses

global __modpath__
__modpath__ = module_path(main)

Maintenant j'utilise __modpath__ au lieu de __file__.

3 votes

Selon le guide de style de codage PEP8, on ne devrait jamais créer des noms avec des doubles tirets bas en tête et en queue -- donc __modpath__ devrait être renommé. Vous n'avez probablement pas besoin de l'instruction global. Sinon +1!

4 votes

En fait, vous pouvez définir la fonction locale directement dans l'appel à module_path(). c'est-à-dire module_path(lambda _: None) qui ne dépend pas des autres contenus du script dans lequel il se trouve.

0 votes

@martineau: J'ai suivi votre suggestion de lambda _: None et l'ai utilisée pendant presque deux ans, mais je viens juste de découvrir que je pouvais la condenser à simplement lambda:0. Y a-t-il une raison particulière pour laquelle vous avez suggéré la forme que vous avez, avec un argument ignoré de _, au lieu de ne pas avoir d'argument du tout? Y a-t-il quelque chose de supérieur à None préfixé par un espace plutôt que simplement 0? Ils sont tous les deux aussi cryptiques, je pense, juste l'un fait 8 caractères de long tandis que l'autre en fait 14.

6voto

Dale Hagglund Points 7159

La réponse courte est qu'il n'y a pas de moyen garanti d'obtenir l'information que vous voulez, cependant il existe des heuristiques qui fonctionnent presque toujours en pratique. Vous pouvez consulter Comment trouver l'emplacement de l'exécutable en C?. Il aborde le problème d'un point de vue C, mais les solutions proposées peuvent facilement être transcrites en Python.

0 votes

Bonjour, mon drapeau a été refusé, alors j'ai maintenant commencé une discussion sur méta : meta.stackoverflow.com/questions/277272/…

0 votes

Il existe déjà des réponses simples et fonctionnelles sur cette page cependant. Ma solution fonctionne bien : stackoverflow.com/a/33531619/3787376.

0voto

macm Points 429
import os

# Ma solution pour les fichiers sur disque:
# dans a.py ou n'importe quel module

class SomeFoo(object):

    def where(self):
        rsp = os.path.dirname(__file__)
        print(rsp)

# a.py

bla = SomeFoo()

class StackOverFlow(object):

    def __init__(self):
        global bla
        bla.where()

# ... et maintenant vous pouvez créer une instance ------

answer = StackOverFlow()
# La astuce est d'utiliser global!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X