Percorsi relativi in ​​Python


245

Sto creando un semplice script di supporto per il lavoro che copierà un paio di file modello nella nostra base di codice nella directory corrente. Non ho, tuttavia, il percorso assoluto per la directory in cui sono memorizzati i modelli. Ho un percorso relativo dallo script ma quando chiamo lo script lo considera come un percorso relativo alla directory di lavoro corrente. C'è un modo per specificare che questo URL relativo proviene invece dalla posizione dello script?


Risposte:


325

Nel file che ha lo script, vuoi fare qualcosa del genere:

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

Questo ti darà il percorso assoluto per il file che stai cercando. Nota che se stai usando setuptools, probabilmente dovresti usare invece la sua API delle risorse del pacchetto .

AGGIORNAMENTO : sto rispondendo a un commento qui in modo da poter incollare un esempio di codice. :-)

Sono corretto nel pensare che __file__non sia sempre disponibile (ad esempio quando si esegue direttamente il file anziché importarlo)?

Suppongo che intendi lo __main__script quando dici di eseguire direttamente il file. In tal caso, ciò non sembra essere il caso sul mio sistema (python 2.5.1 su 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

Tuttavia, so che ci sono alcune stranezze con le __file__estensioni C. Ad esempio, posso farlo sul mio 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'

Tuttavia, ciò solleva un'eccezione sul mio computer Windows.


1
Sono corretto nel pensare che il file non sia sempre disponibile (ad es. Quando si esegue il file direttamente anziché importarlo)?
Stephen Edmonds,

@Stephen Edmonds Lo sto usando un file che eseguo, piuttosto che importarlo, e funziona benissimo.
baudtack,

22
Nota che dovresti usare os.path.join ovunque per la portabilità:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
ford

22
os.path.dirname(__file__)può dare una stringa vuota, usare os.path.dirname(os.path.abspath(__file__))invece
Dmitry Trofimov

14
È una cosa minore, ma PER FAVORE non usare dir come nome di variabile poiché è incorporato.
David,

63

è necessario os.path.realpath(l'esempio seguente aggiunge la directory principale al percorso)

import sys,os
sys.path.append(os.path.realpath('..'))

2
os.path.dirname(__file__)mi ha dato una stringa vuota. Questo ha funzionato perfettamente.
Darragh Enright,

3
Questo sembra fornire al genitore della directory da cui viene eseguito lo script, non alla posizione dello script.
Coquelicot,

10
os.path.realpath('..')ti dà la directory principale della directory di lavoro corrente . Di solito non è quello che vuoi.
Martijn Pieters

1
@DarraghEnright: succede solo in un ambiente di packaging Python-script-to-exe. Questa è una delle rare eccezioni in cui fare affidamento sull'attuale dir di lavoro sarebbe l'alternativa.
Martijn Pieters

52

Come indicato nella risposta accettata

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

Voglio solo aggiungerlo

quest'ultima stringa non può iniziare con la barra rovesciata, infatti nessuna stringa deve includere una barra rovesciata

Dovrebbe essere qualcosa del genere

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

La risposta accettata può essere fuorviante in alcuni casi, fare riferimento a questo link per i dettagli


4
Sì, l'uso os.path.joinè migliore perché li unisce al separatore specifico del sistema operativo.
Farshid T

'/relative/path...'non è un percorso relativo. È intenzionale?
Steve

Questa risposta è ora obsoleta, poiché la risposta principale è stata modificata per utilizzare un percorso relativo corretto in os.path.join(). Ciò che rimane è la preferenza di utilizzare stringhe separate per ciascun elemento del percorso rispetto alla codifica del separatore del percorso.
Martijn Pieters

@MartijnPieters Sì, la risposta migliore è stata modificata per corrispondere in parte a questa, ma le stringhe separate non sono una preferenza: separare le punture in questo modo la rende indipendente dal sistema operativo.
jshrimp29,

26

Ora è il 2018 e Python si è già evoluto __future__molto tempo fa. Così come su con il sorprendente pathlibvenire con Python 3.4 per realizzare il compito invece di lottare con os, os.path, glob, shutil, etc.

Quindi abbiamo 3 percorsi qui (possibilmente duplicati):

  • mod_path: qual è il percorso del semplice script helper
  • src_path: che contiene un paio di file modello in attesa di essere copiati.
  • cwd: directory corrente , la destinazione di quei file modello.

e il problema è: non abbiamo il percorso completo di src_path, sappiamo solo che è il percorso relativo al mod_path.

Ora risolviamo questo con l'incredibile 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()

In futuro, è così semplice. : D


Inoltre, possiamo selezionare e controllare e copiare / spostare quei file modello con 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)

14

Considera il mio codice:

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)

Quando eseguo questo in Windows, ricevo un errore: FileNotFoundError: [Errno 2] Nessun file o directory: '<percorso>' dove <percorso> ha i segmenti di percorso corretti ma usa \\ per i separatori.
Lonstar,

11

Vedi sys.path Come inizializzato all'avvio del programma, il primo elemento di questo elenco, percorso [0], è la directory contenente lo script che è stato usato per invocare l'interprete Python.

Utilizzare questo percorso come cartella principale da cui applicare il percorso relativo

>>> 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'

3
Questo non è necessariamente vero. Di solito sys.path [0] è una stringa vuota o un punto, che è un percorso relativo alla directory corrente. Se si desidera la directory corrente, utilizzare os.getcwd.
Jason Baker,

Il poster originale ha commentato che l'attuale directory di lavoro è il posto sbagliato da cui basare il percorso relativo. Hai ragione nel dire che sys.path [0] non è sempre valido.
Tom Leys,

No, sys.path[0]non è sempre impostato sulla directory principale. Il codice Python può essere invocato con -co -mtramite un interprete incorporato, a quel punto sys.path[0]è impostato su qualcosa di completamente diverso.
Martijn Pieters

6

Invece di usare

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

come nella risposta accettata, sarebbe più robusto usare:

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')

perché l'uso di __file__ restituirà il file da cui è stato caricato il modulo, se è stato caricato da un file, quindi se il file con lo script viene chiamato da altrove, la directory restituita non sarà corretta.

Queste risposte forniscono maggiori dettagli: https://stackoverflow.com/a/31867043/5542253 e https://stackoverflow.com/a/50502/5542253


5
inspect.stack()è una funzione costosa da chiamare. Recupera le informazioni per tutti i frame dello stack, che poi scarti e ottieni solo quello in cima. Richiama sostanzialmente inspect.getfile()l'oggetto del modulo, che ritorna module.__file__. Stai molto meglio di usare solo __file__.
Martijn Pieters

4

Ciao prima di tutto dovresti capire le funzioni os.path.abspath (path) e os.path.relpath (path)

In breve os.path.abspath (path) crea un percorso relativo al percorso assoluto . E se il percorso fornito è esso stesso un percorso assoluto, la funzione restituisce lo stesso percorso.

allo stesso modo os.path.relpath (percorso) crea un percorso assoluto al percorso relativo . E se il percorso fornito è esso stesso un percorso relativo, la funzione restituisce lo stesso percorso.

L'esempio seguente può farti comprendere correttamente il concetto sopra :

supponiamo di avere un file input_file_list.txt che contiene un elenco di file di input che devono essere elaborati dal mio script Python.

D: \ conc \ input1.dic

D: \ conc \ input2.dic

D: \ Copyioconc \ input_file_list.txt

Se vedi la struttura delle cartelle sopra, input_file_list.txt è presente nella cartella Copyofconc e i file che devono essere elaborati dallo script python sono presenti nella cartella conc

Ma il contenuto del file input_file_list.txt è come mostrato di seguito:

.. \ conc \ input1.dic

.. \ conc \ input2.dic

E il mio script Python è presente in D: drive.

E il percorso relativo fornito nel file input_file_list.txt è relativo al percorso del file input_file_list.txt .

Quindi, quando lo script python deve eseguire la directory di lavoro corrente (utilizzare os.getcwd () per ottenere il percorso)

Poiché il mio percorso relativo è relativo a input_file_list.txt , ovvero "D: \ Copyofconc" , devo modificare la directory di lavoro corrente in "D: \ Copyofconc" .

Quindi devo usare os.chdir ('D: \ Copyofconc') , quindi l'attuale directory di lavoro deve essere "D: \ Copyofconc" .

Ora per ottenere i file input1.dic e input2.dic , leggerò le righe ".. \ conc \ input1.dic" quindi userò il comando

input1_path = os.path.abspath ('.. \ conc \ input1.dic') (per cambiare il percorso relativo in percorso assoluto. Qui come directory di lavoro corrente è "D: \ Copyofconc", il file ". \ conc \ input1. dic "si accede relativamente a" D: \ Copyofconc ")

pertanto input1_path deve essere "D: \ conc \ input1.dic"


4

Questo codice restituirà il percorso assoluto allo script principale.

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

Funzionerà anche in un modulo.


Invece di reimportare, useresti sys.modules['__main__'].__file__.
Martijn Pieters

3

Un'alternativa che funziona per me:

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

0

Quello che ha funzionato per me sta usando sys.path.insert. Quindi ho specificato la directory che dovevo andare. Ad esempio, dovevo solo andare su una directory.

import sys
sys.path.insert(0, '../')

1
Questo si basa sulla directory di lavoro corrente, che potrebbe essere radicalmente diversa da quella che si desidera effettivamente.
Martijn Pieters

-2

Non sono sicuro se questo si applica ad alcune delle versioni precedenti, ma credo che Python 3.3 abbia il supporto del percorso relativo nativo.

Ad esempio, il codice seguente dovrebbe creare un file di testo nella stessa cartella dello script Python:

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

(nota che all'inizio non dovrebbe esserci una barra inversa o una barra rovesciata se si tratta di un percorso relativo)


giusto, quindi questo funzionerà dal CWD che non è quello che l'OP richiede. La voglia di lavorare dalla posizione degli script.
Samy Bencherif,
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.