Cách tham chiếu đường dẫn tài nguyên tương đối khi làm việc với kho lưu trữ mã


187

Chúng tôi đang làm việc với một kho lưu trữ mã được triển khai cho cả Windows và Linux - đôi khi trong các thư mục khác nhau. Làm thế nào một trong các mô-đun bên trong dự án đề cập đến một trong các tài nguyên không phải là Python trong dự án (tệp CSV, v.v.)?

Nếu chúng ta làm một cái gì đó như:

thefile=open('test.csv')

hoặc là:

thefile=open('../somedirectory/test.csv')

Nó sẽ chỉ hoạt động khi tập lệnh được chạy từ một thư mục cụ thể hoặc một tập hợp con của các thư mục.

Những gì tôi muốn làm là một cái gì đó như:

path=getBasePathOfProject()+'/somedirectory/test.csv'
thefile=open(path)

Có thể không?

Câu trả lời:


255

Cố gắng sử dụng tên tệp liên quan đến đường dẫn tệp hiện tại. Ví dụ cho './my_file':

fn = os.path.join(os.path.dirname(__file__), 'my_file')

Trong Python 3.4+, bạn cũng có thể sử dụng pathlib :

fn = pathlib.Path(__file__).parent / 'my_file'

3
Tôi nghĩ giải pháp này sẽ chỉ hoạt động nếu tài nguyên nằm trong cùng thư mục của tệp python hoặc trong thư mục con của nó. Làm thế nào để bạn giải quyết nó khi bạn có cơ cấu cây sau đây: / Project_Root_dir / python_files_dir / Một số chi tiết subdirs đây py_file.py / nguồn / some subdirs đây resource_file.csv
olamundo

1
Xin lỗi, cây tập tin đã bị cắt xén trên thông điệp cuối cùng ... thứ hai thử: bạn có tập tin của bạn tại /Project_Root_dir/python_files_dir/some_subdirs/py_file.py và bạn có tập tin tài nguyên của bạn tại /Project_Root_dir/resources/some_subdirs/resource_file.csv
olamundo

28
Bạn sẽ có thể truy cập thư mục mẹ bằng cách sử dụng phép nối (foo, '..'). Vì vậy, từ / root / python_files / module / myfile, hãy sử dụng os.path.join (os.path.dirname ( __file__), '..', '..', 'resource')
c089

7
os.pardirlà tốt hơn một chút '..', mặc dù cả hai đều tương đương trên cả POSIX và Windows.
davidchambers

4
@cedbeu: Nó tương đương với mọi hệ thống tôi từng gặp và tôi nghĩ mọi con trăn hệ thống đều chạy vào ngày hôm nay (vui lòng sửa cho tôi nếu tôi sai ở đây). Tuy nhiên, nếu bạn mong đợi python sẽ được chuyển đến một hệ thống bằng cách sử dụng trình phân tách đường dẫn khác trong tương lai và muốn mã của bạn sẵn sàng cho nó, os.pardir sẽ dễ mang theo hơn. Tôi đã làm cho mọi trường hợp rằng mọi lập trình viên, ngay cả một người chưa từng đọc bất kỳ con trăn nào cũng biết ý nghĩa của "..", trong khi "os.pardir" là một mức độ thiếu quyết đoán, người ta sẽ phải tra cứu tài liệu một cách cá nhân. d dính vào "..".
c089

40

Nếu bạn đang sử dụng các công cụ thiết lập hoặc phân phối (cài đặt setup.py) thì cách "đúng" để truy cập các tài nguyên được đóng gói này dường như đang sử dụng gói_resource.

Trong trường hợp của bạn, ví dụ sẽ là

import pkg_resources
my_data = pkg_resources.resource_string(__name__, "foo.dat")

Tất nhiên những gì đọc tài nguyên và dữ liệu nhị phân đọc sẽ là giá trị của my_data

Nếu bạn chỉ cần tên tệp, bạn cũng có thể sử dụng

resource_filename(package_or_requirement, resource_name)

Thí dụ:

resource_filename("MyPackage","foo.dat")

Ưu điểm là nó được đảm bảo hoạt động ngay cả khi nó là một bản phân phối lưu trữ như một quả trứng.

Xem http://packages.python.org/distribution/pkg_resource.html#resourcemanager-api


3
Tôi biết đây là một câu trả lời cũ, cách ưa thích của tôi là (/ có lẽ?) Để sử dụng pkg_resource, nhưng với sự biến mất của trứng nén, liệu có hại gì khi chỉ sử dụng __file__như ngày xưa không?
Pykler

1
Đây là một cách tiếp cận vững chắc. Ngay cả khi quy ước trứng sẽ biến mất, setuptools không và nhiều người vẫn đang cài đặt deps chống lại git repos nơi trứng được xây dựng trong thời gian chạy
deepelement

18

Trong Python, các đường dẫn có liên quan đến thư mục làm việc hiện tại , trong hầu hết các trường hợp là thư mục mà bạn chạy chương trình của mình. Thư mục làm việc hiện tại rất có thể không giống với thư mục của tệp mô-đun của bạn, vì vậy sử dụng một đường dẫn liên quan đến tệp mô-đun hiện tại của bạn luôn là một lựa chọn tồi.

Sử dụng đường dẫn tuyệt đối nên là giải pháp tốt nhất:

import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')

15

Tôi thường sử dụng một cái gì đó tương tự như thế này:

import os
DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 'datadir'))

# if you have more paths to set, you might want to shorten this as
here = lambda x: os.path.abspath(os.path.join(os.path.dirname(__file__), x))
DATA_DIR = here('datadir') 

pathjoin = os.path.join
# ...
# later in script
for fn in os.listdir(DATA_DIR):
    f = open(pathjoin(DATA_DIR, fn))
    # ...

Biến

__file__

giữ tên tệp của tập lệnh mà bạn viết mã đó, để bạn có thể tạo các đường dẫn liên quan đến tập lệnh, nhưng vẫn được viết bằng các đường dẫn tuyệt đối. Nó hoạt động khá tốt vì nhiều lý do:

  • đường dẫn là tuyệt đối, nhưng vẫn tương đối
  • dự án vẫn có thể được triển khai trong một thùng chứa tương đối

Nhưng bạn cần xem khả năng tương thích nền tảng - os.pathsep của Windows khác với UNIX.


4
import os
cwd = os.getcwd()
path = os.path.join(cwd, "my_file")
f = open(path)

Bạn cũng cố gắng bình thường hóa việc cwdsử dụng của bạn os.path.abspath(os.getcwd()). Thêm thông tin ở đây .


3
Tuy nhiên, rất ít trường hợp sử dụng cwdlà đường dẫn của mô-đun
cedbeu

nó không hoạt động bên trong một gói, chỉ từ cùng một thư mục (hoặc thư mục làm việc) được thiết lập bởi tập lệnh.
alexandra

Điều này sẽ không hoạt động nếu người dùng chạy chương trình bằng đường dẫn tuyệt đối từ thư mục khác. ví dụ: python3 /usr/someone/test.py
sgrpwr

2

Bạn có thể sử dụng xây dựng trong __file__biến. Nó chứa đường dẫn của tập tin hiện tại. Tôi sẽ triển khai getBaseOfProject trong một mô-đun trong thư mục gốc của dự án của bạn. Ở đó tôi sẽ có được phần đường dẫn __file__và sẽ trả lại điều đó. Phương pháp này sau đó có thể được sử dụng ở mọi nơi trong dự án của bạn.


0

Tôi đã bối rối ở đây một chút. Muốn gói một số tệp tài nguyên vào một tệp bánh xe và truy cập chúng. Có bao bì sử dụng tệp kê khai, nhưng cài đặt pip không cài đặt nó trừ khi nó là một thư mục con. Hy vọng những bức ảnh sceen này sẽ giúp

├── cnn_client
   ├── image_preprocessor.py
   ├── __init__.py
   ├── resources
      ├── mscoco_complete_label_map.pbtxt
      ├── retinanet_complete_label_map.pbtxt
      └── retinanet_label_map.py
   ├── tf_client.py

MANIFEST.in

recursive-include cnn_client/resources *

Tạo một weel bằng cách sử dụng thiết lập tiêu chuẩn. Pip cài đặt tập tin bánh xe. Sau khi cài đặt kiểm tra nếu tài nguyên được cài đặt. họ đang

ls /usr/local/lib/python2.7/dist-packages/cnn_client/resources

mscoco_complete_label_map.pbtxt
retinanet_complete_label_map.pbtxt 
 retinanet_label_map.py  

Trong tfclient.py để truy cập các tệp này. từ

templates_dir = os.path.join(os.path.dirname(__file__), 'resources')
 file_path = os.path.join(templates_dir, \
            'mscoco_complete_label_map.pbtxt')
        s = open(file_path, 'r').read()

Và nó hoạt động.


-5

Tôi đã dành một thời gian dài để tìm ra câu trả lời cho điều này, nhưng cuối cùng tôi đã nhận được nó (và nó thực sự rất đơn giản):

import sys
import os
sys.path.append(os.getcwd() + '/your/subfolder/of/choice')

# now import whatever other modules you want, both the standard ones,
# as the ones supplied in your subfolders

Điều này sẽ nối đường dẫn tương đối của thư mục con của bạn vào các thư mục để python tìm trong Nó khá nhanh và bẩn, nhưng nó hoạt động như một bùa mê :)


6
Điều này sẽ chỉ hoạt động nếu bạn đang chạy chương trình Python từ cùng thư mục với tệp .py được đề cập. Và trong trường hợp đó, bạn vẫn có thể làm được open('your/subfolder/of/choice').
Paul Fisher

4
và OP đã đề cập rằng mã cần phải hoạt động trên cả Windows và Linux. Điều này sẽ không.
dùng183037
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.