Chemins relatifs en Python

245

Je construis un script d'aide simple pour le travail qui copiera quelques fichiers de modèle dans notre base de code dans le répertoire actuel. Cependant, je n'ai pas le chemin absolu vers le répertoire où les modèles sont stockés. J'ai un chemin relatif à partir du script mais quand j'appelle le script, il le traite comme un chemin relatif au répertoire de travail actuel. Existe-t-il un moyen de spécifier que cette URL relative provient plutôt de l'emplacement du script?

baudtack
la source

Réponses:

325

Dans le fichier contenant le script, vous voulez faire quelque chose comme ceci:

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

Cela vous donnera le chemin absolu vers le fichier que vous recherchez. Notez que si vous utilisez setuptools, vous devriez probablement utiliser son API de ressources de package à la place.

MISE À JOUR : je réponds à un commentaire ici afin que je puisse coller un exemple de code. :-)

Ai-je raison de penser que ce __file__n'est pas toujours disponible (par exemple lorsque vous exécutez le fichier directement plutôt que de l'importer)?

Je suppose que vous voulez dire le __main__script lorsque vous mentionnez l'exécution directe du fichier. Si c'est le cas, cela ne semble pas être le cas sur mon système (python 2.5.1 sous OS X 10.5.7):

#foo.py
import os
print os.getcwd()
print __file__

#in the interactive interpreter
>>> import foo
/Users/jason
foo.py

#and finally, at the shell:
~ % python foo.py
/Users/jason
foo.py

Cependant, je sais qu'il y a des bizarreries avec des __file__extensions sur C. Par exemple, je peux le faire sur mon Mac:

>>> import collections #note that collections is a C extension in Python 2.5
>>> collections.__file__
'/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-
dynload/collections.so'

Cependant, cela soulève une exception sur ma machine Windows.

Jason Baker
la source
1
Ai-je raison de penser que le fichier n'est pas toujours disponible (par exemple lorsque vous exécutez le fichier directement plutôt que de l'importer)?
Stephen Edmonds
@Stephen Edmonds Je l'utilise un fichier que je lance, plutôt que d'importer, et cela fonctionne très bien.
baudtack
22
Notez que vous devez utiliser os.path.join partout pour la portabilité:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
ford
22
os.path.dirname(__file__)peut donner une chaîne vide, utilisez à la os.path.dirname(os.path.abspath(__file__))place
Dmitry Trofimov
14
C'est une chose mineure, mais S'IL VOUS PLAÎT n'utilisez pas dir comme nom de variable car il s'agit d'une fonction intégrée.
David
63

dont vous avez besoin os.path.realpath(l'exemple ci-dessous ajoute le répertoire parent à votre chemin)

import sys,os
sys.path.append(os.path.realpath('..'))
user989762
la source
2
os.path.dirname(__file__)m'a donné une chaîne vide. Cela a parfaitement fonctionné.
Darragh Enright
3
Cela semble donner au parent du répertoire à partir duquel le script est exécuté, et non à l'emplacement du script.
Coquelicot
10
os.path.realpath('..')vous donne le répertoire parent du répertoire de travail actuel . Ce n'est généralement pas ce que vous voulez.
Martijn Pieters
1
@DarraghEnright: Cela ne se produit que dans un environnement de packaging Python-script-to-exe. C'est l'une des rares exceptions où le recours au répertoire de travail actuel serait l'alternative.
Martijn Pieters
52

Comme mentionné dans la réponse acceptée

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, '/relative/path/to/file/you/want')

Je veux juste ajouter ça

la dernière chaîne ne peut pas commencer par la barre oblique inverse, en fait aucune chaîne ne doit inclure une barre oblique inverse

Cela devrait être quelque chose comme

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, 'relative','path','to','file','you','want')

La réponse acceptée peut être trompeuse dans certains cas, veuillez vous référer à ce lien pour plus de détails

Ahmed
la source
4
Oui, l'utilisation os.path.joinest meilleure car elle les joint au séparateur spécifique au système d'exploitation.
Farshid T
'/relative/path...'n'est pas un chemin relatif. Est-ce intentionnel?
steveire
Cette réponse est désormais obsolète, car la première réponse a été modifiée pour utiliser un chemin relatif approprié dans os.path.join(). Ce qui reste est la préférence d'utiliser des chaînes distinctes pour chaque élément de chemin plutôt que de coder en dur le séparateur de chemin.
Martijn Pieters
@MartijnPieters Oui, la première réponse a été modifiée pour correspondre en partie à cela, mais les chaînes distinctes ne sont pas une préférence - la séparation des piqûres comme celle-ci la rend indépendante du système d'exploitation.
jshrimp29
26

C'est 2018 maintenant, et Python a déjà évolué __future__il y a longtemps. Alors que diriez - vous en utilisant l'incroyable à pathlibvenir avec Python 3.4 pour accomplir la tâche au lieu de lutter avec os, os.path, glob, shutil, etc.

Nous avons donc 3 chemins ici (éventuellement dupliqués):

  • mod_path: qui est le chemin du script d'aide simple
  • src_path: qui contient quelques fichiers modèles en attente de copie.
  • cwd: répertoire courant , destination de ces fichiers modèles.

et le problème est: nous n'avons pas le chemin complet de src_path, savons seulement que c'est le chemin relatif vers le mod_path.

Maintenant, résolvons cela avec l'incroyable pathlib:

# Hope you don't be imprisoned by legacy Python code :)
from pathlib import Path

# `cwd`: current directory is straightforward
cwd = Path.cwd()

# `mod_path`: According to the accepted answer and combine with future power
# if we are in the `helper_script.py`
mod_path = Path(__file__).parent
# OR if we are `import helper_script`
mod_path = Path(helper_script.__file__).parent

# `src_path`: with the future power, it's just so straightforward
relative_path_1 = 'same/parent/with/helper/script/'
relative_path_2 = '../../or/any/level/up/'
src_path_1 = (mod_path / relative_path_1).resolve()
src_path_2 = (mod_path / relative_path_2).resolve()

À l'avenir, c'est aussi simple que cela. :RÉ


De plus, nous pouvons sélectionner et vérifier et copier / déplacer ces fichiers modèles avec pathlib:

if src_path != cwd:
    # When we have different types of files in the `src_path`
    for template_path in src_path.glob('*.ini'):
        fname = template_path.name
        target = cwd / fname
        if not target.exists():
            # This is the COPY action
            with target.open(mode='wb') as fd:
                fd.write(template_path.read_bytes())
            # If we want MOVE action, we could use:
            # template_path.replace(target)
YaOzI
la source
14

Considérez mon code:

import os


def readFile(filename):
    filehandle = open(filename)
    print filehandle.read()
    filehandle.close()



fileDir = os.path.dirname(os.path.realpath('__file__'))
print fileDir

#For accessing the file in the same folder
filename = "same.txt"
readFile(filename)

#For accessing the file in a folder contained in the current folder
filename = os.path.join(fileDir, 'Folder1.1/same.txt')
readFile(filename)

#For accessing the file in the parent folder of the current folder
filename = os.path.join(fileDir, '../same.txt')
readFile(filename)

#For accessing the file inside a sibling folder.
filename = os.path.join(fileDir, '../Folder2/same.txt')
filename = os.path.abspath(os.path.realpath(filename))
print filename
readFile(filename)
Fahad Haleem
la source
Lorsque j'exécute ceci dans Windows, je reçois une erreur: FileNotFoundError: [Errno 2] Aucun fichier ou répertoire de ce type: '<path>' où <path> a les bons segments de chemin mais utilise \\ pour les séparateurs.
lonstar
11

Voir sys.path Initialisé au démarrage du programme, le premier élément de cette liste, path [0], est le répertoire contenant le script qui a été utilisé pour appeler l'interpréteur Python.

Utilisez ce chemin comme dossier racine à partir duquel vous appliquez votre chemin relatif

>>> import sys
>>> import os.path
>>> sys.path[0]
'C:\\Python25\\Lib\\idlelib'
>>> os.path.relpath(sys.path[0], "path_to_libs") # if you have python 2.6
>>> os.path.join(sys.path[0], "path_to_libs")
'C:\\Python25\\Lib\\idlelib\\path_to_libs'
Tom Leys
la source
3
Ce n'est pas nécessairement vrai. Habituellement, sys.path [0] est une chaîne vide ou un point, qui est un chemin relatif vers le répertoire courant. Si vous voulez le répertoire courant, utilisez os.getcwd.
Jason Baker,
L'affiche originale a commenté que le répertoire de travail actuel n'est pas le bon endroit pour baser le chemin relatif. Vous avez raison de dire que sys.path [0] n'est pas toujours valide.
Tom Leys
Non, sys.path[0]n'est pas toujours défini sur le répertoire parent. Le code Python peut être invoqué avec -cou -mou via un interpréteur intégré, auquel point il sys.path[0]est défini sur quelque chose de complètement différent.
Martijn Pieters
6

À la place d'utiliser

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

comme dans la réponse acceptée, il serait plus robuste d'utiliser:

import inspect
import os
dirname = os.path.dirname(os.path.abspath(inspect.stack()[0][1]))
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

car l'utilisation de __file__ renverra le fichier à partir duquel le module a été chargé, s'il a été chargé à partir d'un fichier, donc si le fichier avec le script est appelé d'ailleurs, le répertoire retourné ne sera pas correct.

Ces réponses donnent plus de détails: https://stackoverflow.com/a/31867043/5542253 et https://stackoverflow.com/a/50502/5542253

smiley
la source
5
inspect.stack()est une fonction coûteuse à appeler. Il récupère les informations pour tous les cadres de pile, que vous jetez ensuite et obtenez uniquement celui du haut. Il appelle essentiellement inspect.getfile()l'objet module, qui revient simplement module.__file__. Vous êtes bien mieux d'utiliser simplement __file__.
Martijn Pieters
4

Bonjour tout d'abord, vous devez comprendre les fonctions os.path.abspath (chemin) et os.path.relpath (chemin)

En bref os.path.abspath (path) crée un chemin relatif vers un chemin absolu . Et si le chemin fourni est lui-même un chemin absolu, la fonction renvoie le même chemin.

de même, os.path.relpath (chemin) crée un chemin absolu vers un chemin relatif . Et si le chemin fourni est lui-même un chemin relatif, la fonction renvoie le même chemin.

L'exemple ci-dessous peut vous permettre de comprendre correctement le concept ci-dessus :

supposons que j'ai un fichier input_file_list.txt qui contient la liste des fichiers d'entrée à traiter par mon script python.

D: \ conc \ input1.dic

D: \ conc \ input2.dic

D: \ Copyioconc \ input_file_list.txt

Si vous voyez la structure du dossier ci-dessus, input_file_list.txt est présent dans le dossier Copyofconc et les fichiers à traiter par le script python sont présents dans le dossier conc

Mais le contenu du fichier input_file_list.txt est le suivant:

.. \ conc \ input1.dic

.. \ conc \ input2.dic

Et mon script python est présent dans D: drive.

Et le chemin relatif fourni dans le fichier input_file_list.txt est relatif au chemin du fichier input_file_list.txt .

Ainsi, lorsque le script python doit exécuter le répertoire de travail actuel (utilisez os.getcwd () pour obtenir le chemin)

Comme mon chemin relatif est relatif à input_file_list.txt , c'est-à-dire "D: \ Copyofconc" , je dois changer le répertoire de travail actuel en "D: \ Copyofconc" .

Je dois donc utiliser os.chdir ('D: \ Copyofconc') , donc le répertoire de travail actuel sera "D: \ Copyofconc" .

Maintenant, pour obtenir les fichiers input1.dic et input2.dic , je vais lire les lignes ".. \ conc \ input1.dic" puis utiliser la commande

input1_path = os.path.abspath ('.. \ conc \ input1.dic') (pour changer le chemin relatif en chemin absolu. Ici, le répertoire de travail actuel est "D: \ Copyofconc", le fichier ". \ conc \ input1. dic "sera accessible par rapport à" D: \ Copyofconc ")

donc input1_path doit être "D: \ conc \ input1.dic"

Chandrajyoti Das
la source
4

Ce code renverra le chemin absolu vers le script principal.

import os
def whereAmI():
    return os.path.dirname(os.path.realpath(__import__("__main__").__file__))

Cela fonctionnera même dans un module.

BookOwl
la source
Au lieu de réimporter, vous utiliseriez sys.modules['__main__'].__file__.
Martijn Pieters
3

Une alternative qui fonctionne pour moi:

this_dir = os.path.dirname(__file__) 
filename = os.path.realpath("{0}/relative/file.path".format(this_dir))
J0hnG4lt
la source
0

Ce qui a fonctionné pour moi, c'est d'utiliser sys.path.insert. Ensuite, j'ai spécifié le répertoire dont j'avais besoin. Par exemple, j'avais juste besoin de remonter d'un répertoire.

import sys
sys.path.insert(0, '../')
Chat blanc
la source
1
Cela dépend du répertoire de travail actuel, qui pourrait être radicalement différent de ce que vous voulez réellement.
Martijn Pieters
-2

Je ne sais pas si cela s'applique à certaines des anciennes versions, mais je crois que Python 3.3 a un support de chemin relatif natif.

Par exemple, le code suivant doit créer un fichier texte dans le même dossier que le script python:

open("text_file_name.txt", "w+t")

(notez qu'il ne devrait pas y avoir de barre oblique ni barre oblique inversée au début s'il s'agit d'un chemin relatif)

Samy Bencherif
la source
à droite, donc cela fonctionnera à partir du CWD qui n'est pas ce que le PO demande. Le désir de travailler à partir de l'emplacement des scripts.
Samy Bencherif