Relatieve paden in Python

Ik ben een eenvoudig helperscript aan het bouwen voor werk dat een aantal sjabloonbestanden in onze codebasis naar de huidige map kopieert. Ik heb echter niet het absolute pad naar de map waar de sjablonen zijn opgeslagen. Ik heb wel een relatief pad van het script, maar wanneer ik het script aanroep, behandelt het dat als een pad ten opzichte van de huidige werkmap. Is er een manier om aan te geven dat deze relatieve url in plaats daarvan afkomstig is van de locatie van het script?


Antwoord 1, autoriteit 100%

In het bestand dat het script bevat, wil je zoiets als dit doen:

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

Dit geeft je het absolute pad naar het bestand dat je zoekt. Houd er rekening mee dat als u setuptools gebruikt, u waarschijnlijk de pakketbronnen-APIin plaats daarvan.

UPDATE: ik reageer hier op een opmerking zodat ik een codevoorbeeld kan plakken. 🙂

Heb ik gelijk als ik denk dat __file__niet altijd beschikbaar is (bijvoorbeeld wanneer u het bestand rechtstreeks uitvoert in plaats van het te importeren)?

Ik neem aan dat je het __main__-script bedoelt als je vermeldt dat het bestand rechtstreeks moet worden uitgevoerd. Als dat zo is, lijkt dat niet het geval te zijn op mijn systeem (python 2.5.1 op 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

Ik weet echter wel dat er wat eigenaardigheden zijn met __file__op C-extensies. Ik kan dit bijvoorbeeld op mijn Mac doen:

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

Dit veroorzaakt echter een uitzondering op mijn Windows-computer.


Antwoord 2, autoriteit 17%

je hebt os.path.realpathnodig (voorbeeld hieronder voegt de bovenliggende map toe aan je pad)

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

Antwoord 3, autoriteit 15%

Zoals vermeld in het geaccepteerde antwoord

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

Ik wil alleen dat toevoegen

de laatste string kan niet beginnen met de backslash , eigenlijk geen string
moet een backslash bevatten

Het zou zoiets moeten zijn als

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

Het geaccepteerde antwoord kan in sommige gevallen misleidend zijn, raadpleeg dezelink voor details


Antwoord 4, autoriteit 12%

Het is nu 2018 en Python is al lang geleden geëvolueerd naar de __future__. Dus hoe zit het met het gebruik van de geweldige pathlibdie bij Python wordt geleverd 3.4 om de taak te volbrengen in plaats van te worstelen met os, os.path, glob, shutil, enz.

We hebben hier dus 3 paden (mogelijk gedupliceerd):

  • mod_path: dit is het pad van het eenvoudige helperscript
  • src_path: die een paar sjabloonbestandenbevat die wachten om gekopieerd te worden.
  • cwd: huidige map, de bestemming van die sjabloonbestanden.

en het probleem is: we hebben niethet volledige pad van src_path, we weten alleen het relatieve padnaar de mod_path.

Laten we dit nu oplossen met de geweldige 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 de toekomst is het zo simpel. 😀


Bovendien kunnen we die sjabloonbestanden selecteren, controleren en kopiëren/verplaatsen met 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)

Antwoord 5, autoriteit 4%

Denk aan mijn 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)

Antwoord 6, autoriteit 3%

Zie sys.path
Zoals geïnitialiseerd bij het opstarten van het programma, is het eerste item van deze lijst, path[0], de map met het script dat werd gebruikt om de Python-interpreter aan te roepen.

Gebruik dit pad als de hoofdmap van waaruit u uw relatief pad

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

Antwoord 7, autoriteit 2%

In plaats van

. te gebruiken

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

zoals in het geaccepteerde antwoord, zou het robuuster zijn om te gebruiken:

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

omdat het gebruik van __file__ het bestand teruggeeft waaruit de module is geladen, als het uit een bestand is geladen, dus als het bestand met het script van elders wordt aangeroepen, is de geretourneerde map niet correct.

Deze antwoorden geven meer details: https://stackoverflow.com/a/31867043/5542253en https://stackoverflow.com/a/50502/5542253


Antwoord 8

Hallo, allereerst moet u de functies os.path.abspath(path)en os.path.relpath(path)

begrijpen

Kortom os.path.abspath(path)maakt een relatief padnaar absoluut pad. En als het opgegeven pad zelf een absoluut pad is, retourneert de functie hetzelfde pad.

op dezelfde manier maakt os.path.relpath(path)een absoluut padnaar relatief pad. En als het opgegeven pad zelf een relatief pad is, retourneert de functie hetzelfde pad.

Het onderstaande voorbeeld kan u het bovenstaande concept goed laten begrijpen:

stel dat ik een bestand input_file_list.txtheb dat een lijst bevat van invoerbestanden die door mijn python-script moeten worden verwerkt.

D:\conc\input1.dic

D:\conc\input2.dic

D:\Copyioconc\input_file_list.txt

Als je bovenstaande mapstructuur ziet, is input_file_list.txtaanwezig in de map Copyofconcen zijn de bestanden die door het python-script moeten worden verwerkt aanwezig in concmap

Maar de inhoud van het bestand input_file_list.txtis zoals hieronder weergegeven:

..\conc\input1.dic

..\conc\input2.dic

En mijn python-script is aanwezig in de D:schijf.

En het relatieve pad in het bestand input_file_list.txtis relatief aan het pad van het bestand input_file_list.txt.

Dus wanneer python script de huidige werkmap zal uitvoeren (gebruik os.getcwd()om het pad te krijgen)

Aangezien mijn relatieve pad relatief is ten opzichte van input_file_list.txt, dat wil zeggen “D:\Copyofconc”, moet ik de huidige werkmap wijzigen in ” D:\Copyofconc”.

Dus ik moet os.chdir(‘D:\Copyofconc’)gebruiken, dus de huidige werkdirectory zal “D:\Copyofconc”zijn.

Om nu de bestanden input1.dicen input2.dicte krijgen, zal ik de regels “..\conc\input1.dic” lezen en dan het commando gebruiken

input1_path= os.path.abspath(‘..\conc\input1.dic’)(om relatief pad te veranderen in absoluut pad. Hier als huidige werkmap is “D:\Copyofconc” , zal het bestand “.\conc\input1.dic” worden benaderd relatief aan “D:\Copyofconc”)

dus input1_pathzal “D:\conc\input1.dic” zijn


Antwoord 9

Deze code retourneert het absolute pad naar het hoofdscript.

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

Dit werkt zelfs in een module.


Antwoord 10

Een alternatief dat voor mij werkt:

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

Antwoord 11

samenvatting van de belangrijkste commando’s

>>> import os
>>> os.path.join('/home/user/tmp', 'subfolder')
'/home/user/tmp/subfolder'
>>> os.path.normpath('/home/user/tmp/../test/..')
'/home/user'
>>> os.path.relpath('/home/user/tmp', '/home/user')
'tmp'
>>> os.path.isabs('/home/user/tmp')
True
>>> os.path.isabs('/tmp')
True
>>> os.path.isabs('tmp')
False
>>> os.path.isabs('./../tmp')
False
>>> os.path.realpath('/home/user/tmp/../test/..') # follows symbolic links
'/home/user'

Een gedetailleerde beschrijving is te vinden in de docs.
Dit zijn linux-paden. Windows zou analoog moeten werken.


Antwoord 12

Wat voor mij werkte, is het gebruik van sys.path.insert. Toen heb ik de map gespecificeerd die ik moest gaan. Ik hoefde bijvoorbeeld maar één map omhoog te gaan.

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

Antwoord 13

Ik denk dat om met alle systemen te werken “ntpath” gebruikt wordt in plaats van “os.path”. Tegenwoordig werkt het goed met Windows, Linux en Mac OSX.

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

Antwoord 14

Van wat anderen suggereren en van pathlibdocumentatie, een eenvoudige en duidelijke oplossing is de volgende (stel dat het bestand waarnaar we moeten verwijzen: Test/data/users.csv:

# This file location: Tests/src/long/module/subdir/some_script.py
from pathlib import Path
# back to Tests/
PROJECT_ROOT = Path(__file__).parents[4]
# then down to Test/data/users.csv
CSV_USERS_PATH = PROJECT_ROOT / 'data' / 'users.csv'  
with CSV_USERS_PATH.open() as users:
    print(users.read())

Dit lijkt me een beetje vreemd, want als je some_script.pyverplaatst, kan het pad naar de hoofdmap van ons project veranderen (we zouden parents[4]). Aan de andere kant vond ik een oplossing die ik verkies op basis van hetzelfde idee.

Stel dat we de volgende mappenstructuur hebben:

Tests
+-- data
¦  L-- users.csv
L-- src
   +-- long
   ¦  L-- module
   ¦     L-- subdir
   ¦        L-- some_script.py
   +-- main.py
   L-- paths.py

Het bestand paths.pyis verantwoordelijk voor het opslaan van de hoofdlocatie van ons project:

from pathlib import Path
PROJECT_ROOT = Path(__file__).parents[1]

Alle scripts kunnen nu paths.PROJECT_ROOTgebruiken om absolute paden uit de hoofdmap van het project uit te drukken. In src/long/module/subdir/some_script.pyzouden we bijvoorbeeld kunnen hebben:

from paths import PROJECT_ROOT
CSV_USERS_PATH = PROJECT_ROOT / 'data' / 'users.csv'
def hello():
    with CSV_USERS_PATH.open() as f:
        print(f.read())

En alles gaat zoals verwacht:

~/Tests/src/$ python main.py
/Users/cglacet/Tests/data/users.csv
hello, user
~/Tests/$ python src/main.py
/Users/cglacet/Tests/data/users.csv
hello, user

Het main.py-script is simpelweg:

from long.module.subdir import some_script
some_script.hello()

Antwoord 15

Een eenvoudige oplossing zou zijn

import os
os.chdir(os.path.dirname(__file__))

Antwoord 16

Van C:\Users\xyz\myFoldernaar C:\Users\xyz\testdata:

import os
working_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
# C:\Users\xyz\myFolder
print(working_dir)
updated_working_dir = os.path.join(os.path.realpath(working_dir + '/../'), 'testdata')
# C:\Users\xyz\testdata
print(updated_working_dir)

Uitvoer

C:\Users\xyz\myFolder
C:\Users\xyz\testdata

Antwoord 17

Ik weet niet zeker of dit van toepassing is op sommige van de oudere versies, maar ik geloof dat Python 3.3 native relatieve padondersteuning heeft.

De volgende code zou bijvoorbeeld een tekstbestand moeten maken in dezelfde map als het python-script:

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

(merk op dat er geen voorwaartse of backslash aan het begin mag staan ​​als het een relatief pad is)

Other episodes