Ścieżki względne w Pythonie


245

Buduję prosty skrypt pomocniczy do pracy, który skopiuje kilka plików szablonów z naszej bazy kodu do bieżącego katalogu. Nie mam jednak bezwzględnej ścieżki do katalogu, w którym przechowywane są szablony. Mam ścieżkę względną ze skryptu, ale gdy wywołuję skrypt, traktuje to jako ścieżkę względem bieżącego katalogu roboczego. Czy istnieje sposób, aby określić, że ten względny adres URL pochodzi z lokalizacji skryptu?


Odpowiedzi:


325

W pliku ze skryptem chcesz zrobić coś takiego:

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

To da ci bezwzględną ścieżkę do pliku, którego szukasz. Pamiętaj, że jeśli używasz setuptools, prawdopodobnie powinieneś zamiast tego użyć interfejsu API zasobów pakietu .

AKTUALIZACJA : Odpowiadam na komentarz tutaj, więc mogę wkleić próbkę kodu. :-)

Czy mam rację, myśląc, że __file__nie zawsze jest dostępny (np. Kiedy uruchamiasz plik bezpośrednio zamiast go importować)?

Zakładam, że masz na myśli __main__skrypt, kiedy wspominasz o bezpośrednim uruchomieniu pliku. Jeśli tak, wydaje się, że tak nie jest w moim systemie (python 2.5.1 w 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

Wiem jednak, że istnieją pewne dziwactwa z __file__rozszerzeniami C. Na przykład mogę to zrobić na komputerze 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'

Rodzi to jednak wyjątek na moim komputerze z systemem Windows.


1
Czy mam rację, sądząc, że plik nie zawsze jest dostępny (np. Kiedy uruchamiasz go bezpośrednio, zamiast go importować)?
Stephen Edmonds

@Stephen Edmonds Używam tego pliku, który uruchamiam, a nie importuję, i działa świetnie.
baudtack

22
Zauważ, że powinieneś używać os.path.join wszędzie dla przenośności:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
ford

22
os.path.dirname(__file__)może podać pusty ciąg, użyj os.path.dirname(os.path.abspath(__file__))zamiast tego
Dmitry Trofimov

14
To drobna sprawa, ale PROSZĘ nie używaj dir jako nazwy zmiennej, ponieważ jest to wbudowane.
David

63

potrzebujesz os.path.realpath(przykład poniżej dodaje katalog nadrzędny do ścieżki)

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

2
os.path.dirname(__file__)dał mi pusty ciąg. To działało idealnie.
Darragh Enright

3
Wydaje się, że daje to rodzicowi katalogu, z którego skrypt jest uruchamiany, a nie lokalizacji skryptu.
Coquelicot,

10
os.path.realpath('..')daje katalog macierzysty bieżącego działającego katalogu . Zwykle nie tego chcesz.
Martijn Pieters

1
@DarraghEnright: Dzieje się tak tylko w środowisku pakowania w języku Python-script-to-exe. To jeden z rzadkich wyjątków, w którym alternatywą byłoby poleganie na bieżącym katalogu roboczym.
Martijn Pieters

52

Jak wspomniano w zaakceptowanej odpowiedzi

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

Chcę tylko to dodać

ten ostatni ciąg nie może zaczynać się od odwrotnego ukośnika, w rzeczywistości żaden ciąg nie powinien zawierać odwrotnego ukośnika

Tak powinno być

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

W niektórych przypadkach zaakceptowana odpowiedź może wprowadzać w błąd. Szczegółowe informacje można znaleźć w tym linku


4
Tak, użycie os.path.joinjest lepsze, ponieważ łączy je z separatorem specyficznym dla systemu operacyjnego.
Farshid T

'/relative/path...'nie jest ścieżką względną. Czy to celowe?
steveire

Ta odpowiedź jest teraz nieaktualna, ponieważ pierwsza odpowiedź została zredagowana, aby użyć właściwej ścieżki względnej w os.path.join(). Pozostaje preferencja użycia oddzielnych ciągów dla każdego elementu ścieżki zamiast zakodowania separatora ścieżki na stałe.
Martijn Pieters

@MartijnPieters Tak, najlepsza odpowiedź została zredagowana, aby częściowo ją dopasować, ale oddzielne ciągi nie są preferencjami - oddzielenie takich żądań powoduje, że jest niezależny od systemu operacyjnego.
jshrimp29

26

Teraz jest 2018, a Python ewoluował już __future__dawno temu. Tak jak o użyciu niesamowite pathlibpochodzących z Pythona 3.4, aby wykonać zadanie, a nie walczy z os, os.path, glob, shutil, itd.

Mamy tutaj 3 ścieżki (być może zduplikowane):

  • mod_path: która jest ścieżką prostego skryptu pomocniczego
  • src_path: który zawiera kilka plików szablonów oczekujących na skopiowanie.
  • cwd: bieżący katalog , miejsce docelowe tych plików szablonów.

problem polega na tym , że nie mamy pełnej ścieżki src_path, wiemy tylko, że jest to ścieżka względna do mod_path.

Teraz rozwiążmy to za pomocą niesamowitego 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()

W przyszłości będzie to takie proste. :RE


Ponadto możemy wybierać i sprawdzać oraz kopiować / przenosić te pliki szablonów za pomocą 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

Rozważ mój kod:

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)

Gdy uruchamiam to w systemie Windows, otrzymuję błąd: FileNotFoundError: [Errno 2] Brak takiego pliku lub katalogu: „<ścieżka>”, gdzie <ścieżka> ma prawidłowe segmenty ścieżki, ale używa \\ do separatorów.
lonstar

11

Zobacz sys.path Zainicjowany podczas uruchamiania programu, pierwszy element tej listy, ścieżka [0], to katalog zawierający skrypt użyty do wywołania interpretera Pythona.

Użyj tej ścieżki jako folderu głównego, z którego zastosujesz ścieżkę względną

>>> 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
To niekoniecznie prawda. Zwykle sys.path [0] to pusty ciąg lub kropka, która jest względną ścieżką do bieżącego katalogu. Jeśli chcesz bieżący katalog, użyj os.getcwd.
Jason Baker

Oryginalny plakat skomentował, że bieżący katalog roboczy jest niewłaściwym miejscem do oparcia ścieżki względnej. Masz rację mówiąc, że sys.path [0] nie zawsze jest poprawny.
Tom Leys

Nie, sys.path[0]nie zawsze jest ustawiony na katalog nadrzędny. Kod Python może być wywołany z -club -mlub za pośrednictwem wbudowanego tłumacza, w którym momencie sys.path[0]jest ustawiony na coś innego całkowicie.
Martijn Pieters

6

Zamiast używać

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

tak jak w zaakceptowanej odpowiedzi, bardziej niezawodne byłoby użycie:

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

ponieważ użycie __plik_ zwróci plik, z którego moduł został załadowany, jeśli został załadowany z pliku, więc jeśli plik ze skryptem zostanie wywołany z innego miejsca, zwrócony katalog nie będzie poprawny.

Odpowiedzi te zawierają więcej szczegółów: https://stackoverflow.com/a/31867043/5542253 i https://stackoverflow.com/a/50502/5542253


5
inspect.stack()jest kosztowną funkcją do wywołania. Pobiera informacje dla wszystkich ramek stosu, które następnie odrzucasz i otrzymujesz tylko najwyższą. Zasadniczo wywołuje inspect.getfile()obiekt modułu, który po prostu zwraca module.__file__. Lepiej jest po prostu używać __file__.
Martijn Pieters

4

Cześć, przede wszystkim powinieneś zrozumieć funkcje os.path.abspath (ścieżka) i os.path.relpath (ścieżka)

W skrócie os.path.abspath (ścieżka) tworzy ścieżkę względną do ścieżki bezwzględnej . A jeśli podana ścieżka jest sama ścieżką bezwzględną, wówczas funkcja zwraca tę samą ścieżkę.

podobnie os.path.relpath (ścieżka) tworzy ścieżkę bezwzględną do ścieżki względnej . A jeśli podana ścieżka sama jest ścieżką względną, wówczas funkcja zwraca tę samą ścieżkę.

Poniższy przykład pozwala poprawnie zrozumieć powyższą koncepcję :

Załóżmy, że mam plik lista_plików_wejściowych.txt, który zawiera listę plików wejściowych do przetworzenia przez mój skrypt python.

D: \ conc \ input1.dic

D: \ conc \ input2.dic

D: \ Copyioconc \ lista_plików_wejściowych.txt

Jeśli widzisz powyższą strukturę folderów, lista_plików_plików.txt znajduje się w folderze Copyofconc, a pliki do przetworzenia przez skrypt python znajdują się w folderze conc

Ale zawartość pliku input_file_list.txt jest taka, jak pokazano poniżej:

.. \ conc \ input1.dic

.. \ conc \ input2.dic

A mój skrypt Pythona jest obecny w D: drive.

A ścieżka względna podana w pliku plik_wejściowy.txt jest względna do ścieżki pliku plik_wejściowy.txt .

Więc kiedy skrypt Pythona powinien wykonać bieżący katalog roboczy (użyj os.getcwd () aby uzyskać ścieżkę)

Ponieważ moja ścieżka względna jest względna do listy_pliku_wejściowego.txt , czyli „D: \ Copyofconc” , muszę zmienić bieżący katalog roboczy na „D: \ Copyofconc” .

Więc muszę użyć os.chdir („D: \ Copyofconc”) , więc bieżącym katalogiem roboczym będzie „D: \ Copyofconc” .

Teraz, aby pobrać pliki input1.dic i input2.dic , przeczytam wiersze „.. \ conc \ input1.dic”, a następnie użyję polecenia

input1_path = os.path.abspath ('.. \ conc \ input1.dic') (aby zmienić ścieżkę względną na ścieżkę bezwzględną. Tutaj jako bieżący katalog roboczy jest „D: \ Copyofconc”, plik „. \ conc \ input1. dic ”należy uzyskać w odniesieniu do„ D: \ Copyofconc ”)

więc input1_path będzie mieć postać „D: \ conc \ input1.dic”


4

Ten kod zwróci bezwzględną ścieżkę do głównego skryptu.

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

Działa to nawet w module.


Zamiast ponownie importować, możesz użyć sys.modules['__main__'].__file__.
Martijn Pieters

3

Alternatywa, która działa dla mnie:

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

0

To, co zadziałało, to używanie sys.path.insert. Następnie określiłem katalog, który powinienem przejść. Na przykład po prostu musiałem przejść o jeden katalog wyżej.

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

1
Zależy to od bieżącego katalogu roboczego, który może radykalnie różnić się od tego, czego naprawdę chcesz.
Martijn Pieters

-2

Nie jestem pewien, czy dotyczy to niektórych starszych wersji, ale uważam, że Python 3.3 ma natywną obsługę ścieżek względnych.

Na przykład poniższy kod powinien utworzyć plik tekstowy w tym samym folderze co skrypt Pythona:

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

(pamiętaj, że na początku nie powinno być ukośnika w przód ani w lewo, jeśli jest to ścieżka względna)


tak, to zadziała z CWD, o co nie prosi OP. Chcę pracować z lokalizacji skryptów.
Samy Bencherif,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.