Đường dẫn tương đối trong Python


244

Tôi đang xây dựng một tập lệnh trợ giúp đơn giản cho công việc sẽ sao chép một vài tệp mẫu trong cơ sở mã của chúng tôi vào thư mục hiện tại. Tuy nhiên, tôi không có đường dẫn tuyệt đối đến thư mục nơi các mẫu được lưu trữ. Tôi có một đường dẫn tương đối từ tập lệnh nhưng khi tôi gọi tập lệnh thì nó coi đó là một đường dẫn liên quan đến thư mục làm việc hiện tại. Có cách nào để xác định rằng url tương đối này là từ vị trí của tập lệnh thay thế không?


Câu trả lời:


324

Trong tệp có tập lệnh, bạn muốn làm một cái gì đó như thế này:

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

Điều này sẽ cung cấp cho bạn đường dẫn tuyệt đối đến tệp bạn đang tìm kiếm. Lưu ý rằng nếu bạn đang sử dụng setuptools, có lẽ bạn nên sử dụng API tài nguyên gói của nó thay thế.

CẬP NHẬT : Tôi đang trả lời một bình luận ở đây để tôi có thể dán một mẫu mã. :-)

Tôi có đúng không khi nghĩ rằng __file__không phải lúc nào cũng có sẵn (ví dụ: khi bạn chạy tệp trực tiếp thay vì nhập tệp)?

Tôi giả sử bạn có nghĩa là __main__tập lệnh khi bạn đề cập đến việc chạy tệp trực tiếp. Nếu vậy, điều đó dường như không xảy ra trên hệ thống của tôi (python 2.5.1 trên 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

Tuy nhiên, tôi biết rằng có một số quirks với __file__phần mở rộng C. Ví dụ: tôi có thể làm điều này trên máy Mac của mình:

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

Tuy nhiên, điều này làm tăng một ngoại lệ trên máy Windows của tôi.


1
Tôi có đúng không khi nghĩ rằng tệp không phải lúc nào cũng có sẵn (ví dụ: khi bạn chạy tệp trực tiếp thay vì nhập tệp)?
Stephen Edmonds

@Stephen Edmonds Tôi đang sử dụng nó một tệp mà tôi chạy, thay vì nhập, và nó hoạt động rất tốt.
baudtack

22
Lưu ý bạn nên sử dụng os.path.join ở mọi nơi để có tính di động:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
ford

22
os.path.dirname(__file__)có thể đưa ra một chuỗi trống, os.path.dirname(os.path.abspath(__file__))thay vào đó hãy sử dụng
Dmitry Trofimov 10/03/2015

14
Đó là một điều nhỏ, nhưng xin vui lòng không sử dụng dir như một tên biến vì nó là một nội dung.
David

63

bạn cần os.path.realpath(mẫu bên dưới thêm thư mục mẹ vào đường dẫn của bạn)

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

2
os.path.dirname(__file__)đã cho tôi một chuỗi trống. Điều này làm việc hoàn hảo.
Darragh Enright

3
Điều này dường như cung cấp cho cha mẹ của thư mục tập lệnh được chạy từ đó, không phải vị trí của tập lệnh.
Coquelicot

10
os.path.realpath('..')cung cấp cho bạn thư mục cha của thư mục làm việc hiện tại . Đó thường không phải là những gì bạn muốn.
Martijn Pieters

1
@DarraghEnright: Điều đó chỉ xảy ra trong môi trường đóng gói Python-script-to-exe. Đó là một trong những trường hợp ngoại lệ hiếm hoi khi dựa vào thư mục làm việc hiện tại sẽ là lựa chọn thay thế.
Martijn Pieters

52

Như đã đề cập trong câu trả lời được chấp nhận

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

Tôi chỉ muốn thêm rằng

chuỗi sau không thể bắt đầu bằng dấu gạch chéo ngược, vì không có chuỗi nào nên bao gồm dấu gạch chéo ngược

Nó phải là một cái gì đó như

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

Câu trả lời được chấp nhận có thể gây hiểu nhầm trong một số trường hợp, vui lòng tham khảo liên kết này để biết chi tiết


4
Có sử dụng os.path.joinlà tốt hơn bởi vì nó kết hợp chúng với dấu phân tách dành riêng cho hệ điều hành.
Farshid T

'/relative/path...'không phải là một con đường tương đối. Đó có phải là cố ý?
steveire

Câu trả lời này hiện đã lỗi thời, vì câu trả lời hàng đầu đã được chỉnh sửa để sử dụng một đường dẫn tương đối thích hợp trong os.path.join(). Những gì còn lại là ưu tiên sử dụng các chuỗi riêng biệt cho từng thành phần đường dẫn trên mã hóa phân tách đường dẫn.
Martijn Pieters

@MartijnPieters Có, câu trả lời hàng đầu đã được chỉnh sửa để phù hợp với phần này, nhưng các chuỗi riêng biệt không phải là một sở thích - tách biệt các stings như thế này làm cho nó độc lập với os.
jshaleigh29

26

Bây giờ là năm 2018 và Python đã phát triển __future__từ lâu. Vậy làm thế nào về việc sử dụng tuyệt vời pathlibđến với Python 3.4 để hoàn thành nhiệm vụ thay vì phải vật lộn với os, os.path, glob,shutil vv

Vì vậy, chúng tôi có 3 đường dẫn ở đây (có thể trùng lặp):

  • mod_path: đó là đường dẫn của tập lệnh trợ giúp đơn giản
  • src_path: chứa một vài tệp mẫu đang chờ sao chép.
  • cwd: thư mục hiện tại , đích đến của các tệp mẫu đó.

và vấn đề là: chúng ta không có đường dẫn đầy đủ src_path, chỉ biết đó là đường dẫn tương đối đến mod_path.

Bây giờ hãy giải quyết điều này với điều tuyệt vời 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()

Trong tương lai, nó chỉ đơn giản như vậy. : D


Hơn nữa, chúng ta có thể chọn và kiểm tra và sao chép / di chuyển các tệp mẫu đó bằng 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

Hãy xem xét mã của tôi:

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)

Khi tôi chạy cái này trong windows, tôi nhận được một lỗi: FileNotFoundError: [Errno 2] Không có tệp hoặc thư mục như vậy: '<path>' trong đó <path> có các đoạn đường dẫn chính xác nhưng sử dụng \ cho dấu phân cách.
ngôi sao

11

Xem sys.path Như được khởi tạo khi khởi động chương trình, mục đầu tiên của danh sách này, đường dẫn [0], là thư mục chứa tập lệnh được sử dụng để gọi trình thông dịch Python.

Sử dụng đường dẫn này làm thư mục gốc mà bạn áp dụng đường dẫn tương đối của mình

>>> 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
Điều đó không hẳn đúng. Thông thường sys.path [0] là một chuỗi rỗng hoặc dấu chấm, là đường dẫn tương đối đến thư mục hiện tại. Nếu bạn muốn thư mục hiện tại, sử dụng os.getcwd.
Jason Baker

Các poster ban đầu nhận xét rằng thư mục làm việc hiện tại là vị trí sai để căn cứ đường dẫn tương đối từ đó. Bạn đã đúng khi nói rằng sys.path [0] không phải lúc nào cũng hợp lệ.
Tom Leys

Không, sys.path[0]không phải lúc nào cũng được đặt vào thư mục cha. Mã Python có thể được gọi bằng -choặc -mthông qua một trình thông dịch nhúng, tại đó điểm sys.path[0]được đặt thành một cái gì đó khác nhau hoàn toàn.
Martijn Pieters

6

Thay vì sử dụng

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

như trong câu trả lời được chấp nhận, sẽ mạnh mẽ hơn khi sử dụng:

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

bởi vì sử dụng __file__ sẽ trả về tệp mà mô-đun được tải, nếu nó được tải từ tệp, do đó, nếu tệp có tập lệnh được gọi từ nơi khác, thư mục được trả về sẽ không chính xác.

Những câu trả lời này cung cấp thêm chi tiết: https://stackoverflow.com/a/31867043/5542253https://stackoverflow.com/a/50502/5542253


5
inspect.stack()là một chức năng đắt tiền để gọi. Nó lấy thông tin cho tất cả các khung stack, sau đó bạn loại bỏ và chỉ lấy cái trên cùng cho. Về cơ bản nó gọi inspect.getfile()đối tượng mô-đun, mà chỉ trả về module.__file__. Bạn tốt hơn nhiều chỉ sử dụng __file__.
Martijn Pieters

4

Xin chào, trước tiên bạn nên hiểu chức năng os.path.abspath (path)os.path.relpath (path)

Trong os.path.abspath ngắn (đường dẫn) tạo một đường dẫn tương đối đến đường dẫn tuyệt đối . Và nếu đường dẫn được cung cấp chính nó là một đường dẫn tuyệt đối thì hàm trả về cùng một đường dẫn.

tương tự os.path.relpath (đường dẫn) tạo một đường dẫn tuyệt đối đến đường dẫn tương đối . Và nếu đường dẫn được cung cấp chính nó là một đường dẫn tương đối thì hàm trả về cùng một đường dẫn.

Ví dụ dưới đây có thể cho phép bạn hiểu đúng khái niệm trên :

giả sử tôi có một tập tin input_file_list.txt chứa danh sách các tệp đầu vào được xử lý bởi tập lệnh python của tôi.

D: \ conc \ input1.dic

D: \ conc \ input2.dic

D: \ Copyioconc \ input_file_list.txt

Nếu bạn thấy cấu trúc thư mục ở trên, input_file_list.txt có trong thư mục Copyofconc và các tệp được xử lý bởi tập lệnh python có mặt trong conc thư mục

Nhưng nội dung của tệp input_file_list.txt như dưới đây:

.. \ conc \ input1.dic

.. \ conc \ input2.dic

Và kịch bản python của tôi có mặt trong D: drive.

Và đường dẫn tương đối được cung cấp trong tệp input_file_list.txt có liên quan đến đường dẫn của tệp input_file_list.txt .

Vì vậy, khi kịch bản python sẽ thực thi thư mục làm việc hiện tại (sử dụng os.getcwd () để lấy đường dẫn)

Vì đường dẫn tương đối của tôi có liên quan đến input_file_list.txt , đó là "D: \ Copyofconc" , tôi phải thay đổi thư mục làm việc hiện tại thành "D: \ Copyofconc" .

Vì vậy, tôi phải sử dụng os.chdir ('D: \ Copyofconc') , vì vậy thư mục làm việc hiện tại sẽ là "D: \ Copyofconc" .

Bây giờ để có được các tập tin input1.dicinput2.dic , tôi sẽ đọc các dòng ".. \ conc \ input1.dic" sau đó sẽ sử dụng lệnh

input1_path = os.path.abspath ('.. \ conc \ input1.dic') (để thay đổi đường dẫn tương đối thành đường dẫn tuyệt đối. Ở đây, thư mục làm việc hiện tại là "D: \ Copyofconc", tệp ". \ conc \ input1. dic "sẽ được truy cập liên quan đến" D: \ Copyofconc ")

vì vậy input1_path sẽ là "D: \ conc \ input1.dic"


4

Mã này sẽ trả về đường dẫn tuyệt đối đến tập lệnh chính.

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

Điều này sẽ làm việc ngay cả trong một mô-đun.


Thay vì nhập lại, bạn sẽ sử dụng sys.modules['__main__'].__file__.
Martijn Pieters

3

Một thay thế phù hợp với tôi:

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

0

Những gì làm việc cho tôi là sử dụng sys.path.insert. Sau đó, tôi chỉ định thư mục tôi cần phải đi. Ví dụ tôi chỉ cần lên một thư mục.

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

1
Điều này phụ thuộc vào thư mục làm việc hiện tại, có thể hoàn toàn khác với những gì bạn thực sự muốn.
Martijn Pieters

-2

Tôi không chắc nếu điều này áp dụng cho một số phiên bản cũ hơn, nhưng tôi tin rằng Python 3.3 có hỗ trợ đường dẫn tương đối riêng.

Ví dụ: đoạn mã sau sẽ tạo một tệp văn bản trong cùng thư mục với tập lệnh python:

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

(lưu ý rằng ban đầu không nên có dấu gạch chéo hoặc dấu gạch chéo ngược nếu đó là đường dẫn tương đối)


đúng, vì vậy điều này sẽ hoạt động từ CWD, đây không phải là điều OP yêu cầu. Muốn làm việc từ vị trí script.
Samy Bencherif
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.