簡體   English   中英

Python中的相對路徑

[英]Relative paths in Python

我正在為工作構建一個簡單的幫助腳本,它將我們代碼庫中的幾個模板文件復制到當前目錄。 但是,我沒有存儲模板的目錄的絕對路徑。 我確實有來自腳本的相對路徑,但是當我調用腳本時,它會將其視為相對於當前工作目錄的路徑。 有沒有辦法指定這個相對 url 來自腳本的位置?

"

在包含腳本的文件中,您希望執行以下操作:

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

這將為您提供您要查找的文件的絕對路徑。 請注意,如果您使用 setuptools,您可能應該改用它的包資源 API

更新:我在這里回復評論,所以我可以粘貼代碼示例。 :-)

我認為__file__並不總是可用(例如,當您直接運行文件而不是導入文件時)是否正確?

當您提到直接運行文件時,我假設您指的是__main__腳本。 如果是這樣,在我的系統上似乎不是這種情況(OS X 10.5.7 上的 python 2.5.1):

#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

但是,我知道 C 擴展上的__file__有一些怪癖。 例如,我可以在我的 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'

但是,這會在我的 Windows 機器上引發異常。

您需要os.path.realpath (下面的示例將父目錄添加到您的路徑中)

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

如已接受的答案中所述

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

我只想補充一點

后一個字符串不能以反斜杠開頭,事實上任何字符串都不應該包含反斜杠

它應該是這樣的

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

在某些情況下,接受的答案可能會產生誤導,請參閱鏈接了解詳細信息

現在是 2018 年,Python 早就進化到了__future__ 那么如何使用 Python 3.4 pathlib的驚人的pathlib來完成任務,而不是在osos.pathglobshutil等中掙扎。

所以我們這里有 3 條路徑(可能是重復的):

  • mod_path : 這是簡單幫助腳本的路徑
  • src_path :其中包含幾個等待復制的模板文件
  • cwd當前目錄,這些模板文件的目的地。

而問題是:我們沒有的完整路徑src_path ,只知道它的相對路徑mod_path

現在讓我們用驚人的pathlib解決這個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()

未來,就這么簡單。 :D


此外,我們可以使用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)

考慮我的代碼:

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)

請參見sys.path作為程序啟動時初始化的,此列表的第一項 path[0] 是包含用於調用 Python 解釋器的腳本的目錄。

將此路徑用作應用相對路徑的根文件夾

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

而不是使用

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

與接受的答案一樣,使用它會更健壯:

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

因為使用 __file__ 會返回加載模塊的文件,如果是從文件加載的,所以如果從其他地方調用帶有腳本的文件,返回的目錄將不正確。

這些答案提供了更多詳細信息: https : //stackoverflow.com/a/31867043/5542253https://stackoverflow.com/a/50502/5542253

嗨,首先你應該了解函數os.path.abspath(path)os.path.relpath(path)

簡而言之os.path.abspath(path)使相對路徑絕對路徑 如果提供的路徑本身是絕對路徑,則該函數返回相同的路徑。

類似地, os.path.relpath(path)相對路徑絕對路徑 如果提供的路徑本身是一個相對路徑,則該函數返回相同的路徑。

下面的例子可以讓你正確理解上述概念

假設我有一個文件input_file_list.txt ,其中包含要由我的 python 腳本處理的輸入文件列表。

D:\\conc\\input1.dic

D:\\conc\\input2.dic

D:\\Copyioconc\\input_file_list.txt

如果你看到上面的文件夾結構, input_file_list.txt存在於Copyofconc文件夾中,python 腳本要處理的文件存在於conc文件夾中

但文件input_file_list.txt的內容如下所示:

..\\conc\\input1.dic

..\\conc\\input2.dic

我的 python 腳本存在於D:驅動器中。

input_file_list.txt文件中提供的相對路徑是相對於input_file_list.txt文件的路徑。

所以當python腳本執行當前工作目錄時(使用os.getcwd()獲取路徑)

由於我的相對路徑是相對於input_file_list.txt ,即"D:\\Copyofconc" ,我必須將當前工作目錄更改為"D:\\Copyofconc"

所以我必須使用os.chdir('D:\\Copyofconc') ,所以當前的工作目錄應該是"D:\\Copyofconc"

現在要獲取文件input1.dicinput2.dic ,我將讀取“..\\conc\\input1.dic”行然后使用命令

input1_path= os.path.abspath('..\\conc\\input1.dic') (將相對路徑改為絕對路徑。這里作為當前工作目錄為“D:\\Copyofconc”,文件“.\\conc\\input1. dic" 應相對於 "D:\\Copyofconc" 訪問)

所以input1_path應該是“D:\\conc\\input1.dic”

此代碼將返回主腳本的絕對路徑。

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

這甚至可以在模塊中工作。

對我有用的替代方法:

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

最重要的命令總結

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

文檔中有詳細說明。 這些是linux路徑。 Windows 應該以類似的方式工作。

從其他人的建議和pathlib文檔中,一個簡單而清晰的解決方案如下(假設我們需要參考的文件: 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())

現在這對我來說看起來有點奇怪,因為如果你移動some_script.py ,我們項目根目錄的路徑可能會改變(我們需要修改parents[4] )。 另一方面,我基於相同的想法找到了我更喜歡的解決方案。

假設我們有以下目錄結構:

Tests
├── data
│  └── users.csv
└── src
   ├── long
   │  └── module
   │     └── subdir
   │        └── some_script.py
   ├── main.py
   └── paths.py

paths.py文件將負責存儲我們項目的根位置:

from pathlib import Path

PROJECT_ROOT = Path(__file__).parents[1]

所有腳本現在都可以使用paths.PROJECT_ROOT來表示從項目根目錄開始的絕對路徑。 例如在src/long/module/subdir/some_script.py我們可以有:

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

一切都按預期進行:

~/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

main.py腳本就是:

from long.module.subdir import some_script

some_script.hello()

對我sys.path.insert是使用sys.path.insert 然后我指定了我需要去的目錄。 例如,我只需要上一個目錄。

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

我認為與所有系統一起使用“ntpath”而不是“os.path”。 今天,它適用於 Windows、Linux 和 Mac OSX。

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

一個簡單的解決方案是

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

C:\\Users\\xyz\\myFolderC:\\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)

輸出

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

示例,在 python 3.9.5 中測試

您當前的目錄:'c:\\project1\\code'

並且您想要訪問以下文件夾:“c:\\project1\\dataset\\train”。

然后您可以使用以下地址訪問該文件夾:'../dataset/train/'

我認為這可能對某人有用!

這是我的總結:

首先,定義名為relpath的工具函數,它將當前文件的相對路徑轉換為 ​​cwd 的相對路徑

import os
relpath = lambda p: os.path.normpath(os.path.join(os.path.dirname(__file__), p))

然后我們用它來包裝相對於當前文件的路徑

path1 = relpath('../src/main.py')

你也可以調用sys.path.append()來導入相對於當前文件位置的文件

sys.path.append(relpath('..')) # so that you can import from upper dir

完整示例代碼: https : //gist.github.com/luochen1990/9b1ffa30f5c4a721dab5991e040e3eb1

假設當前存檔名為“Helper”,上層目錄名為“Workshop”,模板文件在\\Workshop\\Templates,那么Python中的相對路徑為“..\\Templates”。

這是一種將相對路徑添加到系統路徑集的簡單方法。 例如,對於目標目錄比工作目錄高一級(因此, '/../' )的常見情況:

import os
import sys
workingDir = os.getcwd()
targetDir = os.path.join(os.path.relpath(workingDir + '/../'),'target_directory')
sys.path.insert(0,targetDir)

此解決方案經過測試:

Python 3.9.6 | conda-forge 打包| (默認,2021 年 7 月 11 日,03:37:25)[MSC v.1916 64 位 (AMD64)]

我不確定這是否適用於某些舊版本,但我相信 Python 3.3 具有本機相對路徑支持。

例如,以下代碼應在與 python 腳本相同的文件夾中創建一個文本文件:

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

(請注意,如果是相對路徑,則開頭不應有正斜杠或反斜杠)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM