如何取得相對於某個 .py 檔的絕對路徑

Java 的 Class 提供有一個 getResource(name) 方法,可以用相對某 .class 檔的位置來解讀 name 所表示的相對路徑。

在 Python 裡也有類似的需求,為了程式的可攜性,需要以相對於 .py 檔的方式來描述某個外部檔的位置。假設下面的目錄結構:

/tmp$ tree a
a/
|-- a.txt <-- 內容為 "content of a.txt"
`-- b/
    |-- c/
    |-- mod.py
    `-- othermod.py

2 directories, 2 files

/tmp/a/b/mod.py 會將 /tmp/a.txt 的內容印容印出。它的內容如下:

import os
from os.path import dirname, abspath, join

# 取得 mod.py 所在的路徑 (相對於 current working directory)
mod_dir = dirname(__file__)
# 不直接寫成 '../a.txt' 是為了不要跟 OS 相依
a_pathname = abspath(join(mod_dir, '..', 'a.txt'))

print 'CWD:', os.getcwd()
print open(a_pathname, 'r').read()

不論從哪個位置執行 mod.py (會導致 current working directory 起變化),都能正常地印出 /tmp/a/a.txt 的內容:

/tmp$ python a/b/mod.py
CWD: /tmp
content of a.txt
/tmp$ cd a/b/c
/tmp/a/b/c$ python ../mod.py
CWD: /tmp/a/b/c
content of a.txt

當然也可以藉由其他 module 來定位:

import os, othermod
from os.path import dirname, abspath, join

mod_dir = dirname(othermod.__file__) # 取得 othermod.py 所在的路徑
a_pathname = abspath(join(mod_dir, '..', 'a.txt'))

print 'CWD:', os.getcwd()
print open(a_pathname, 'r').read()

根據上面的實驗結果,我們可以寫一個 abspath(relpath, mod) 來模擬 Java 中 Class.getResource(name) 的行為,也可以做為 os.path.abspath(path) 的擴充。

import os

def abspath(relpath, mod):
    # 呼叫端不用擔心不同 OS 的分隔字元
    relpath = relpath.replace('/', os.sep).replace('', os.sep)
    if mod is None: return os.path.abspath(relpath)

    base_dir = os.path.dirname(mod.__file__)
    pathname = os.path.join(base_dir, relpath)
    return os.path.abspath(pathname)
Tip 可以用 sys.modules[__name__] 來取得 current module.

這裡提到的技巧,也很適合用在執行期動態配置 sys.path

參考資料:

廣告