Cách đáng tin cậy mở một tệp trong cùng thư mục với tập lệnh Python


157

Tôi đã từng mở các tệp trong cùng thư mục với tập lệnh Python hiện đang chạy bằng cách sử dụng một lệnh như

open("Some file.txt", "r")

Tuy nhiên, tôi phát hiện ra rằng khi tập lệnh được chạy trong Windows bằng cách nhấp đúp vào tập lệnh, nó sẽ cố mở tệp từ thư mục sai.

Kể từ đó, tôi đã sử dụng một lệnh của mẫu

open(os.path.join(sys.path[0], "Some file.txt"), "r")

Bất cứ khi nào tôi muốn mở một tập tin. Điều này hoạt động cho việc sử dụng cụ thể của tôi, nhưng tôi không chắc sys.path[0]có thể thất bại trong một số trường hợp sử dụng khác.

Vì vậy, câu hỏi của tôi là: cách tốt nhất và đáng tin cậy nhất để mở một tệp trong cùng thư mục với tập lệnh Python hiện đang chạy là gì?

Đây là những gì tôi đã có thể tìm ra cho đến nay:

  • os.getcwd()os.path.abspath('')trả về "thư mục làm việc hiện tại", không phải thư mục script.

  • os.path.dirname(sys.argv[0])os.path.dirname(__file__)trả về đường dẫn được sử dụng để gọi tập lệnh, có thể là tương đối hoặc thậm chí trống (nếu tập lệnh nằm trong cwd). Ngoài ra, __file__không tồn tại khi tập lệnh được chạy trong IDLE hoặc PythonWin.

  • sys.path[0]os.path.abspath(os.path.dirname(sys.argv[0]))dường như trả về thư mục script. Tôi không chắc có sự khác biệt nào giữa hai thứ này không.

Biên tập:

Tôi chỉ nhận ra rằng những gì tôi muốn làm sẽ được mô tả tốt hơn là "mở một tệp trong cùng thư mục với mô-đun chứa". Nói cách khác, nếu tôi nhập một mô-đun tôi đã viết trong thư mục khác và mô-đun đó mở một tệp, tôi muốn nó tìm tệp trong thư mục của mô-đun. Tôi không nghĩ bất cứ điều gì tôi tìm thấy đều có thể làm điều đó ...

Câu trả lời:


199

Tôi luôn luôn sử dụng:

__location__ = os.path.realpath(
    os.path.join(os.getcwd(), os.path.dirname(__file__)))

Cuộc join()gọi chuẩn bị thư mục làm việc hiện tại, nhưng tài liệu nói rằng nếu một số đường dẫn là tuyệt đối, tất cả các đường dẫn còn lại của nó sẽ bị hủy. Do đó, getcwd()được loại bỏ khi dirname(__file__)trả về một đường dẫn tuyệt đối.

Ngoài ra, realpathcuộc gọi giải quyết các liên kết tượng trưng nếu có được tìm thấy. Điều này tránh được các rắc rối khi triển khai với setuptools trên các hệ thống Linux (các tập lệnh được liên kết với /usr/bin/- ít nhất là trên Debian).

Bạn có thể sử dụng cách sau để mở tệp trong cùng một thư mục:

f = open(os.path.join(__location__, 'bundled-resource.jpg'));
# ...

Tôi sử dụng điều này để gói tài nguyên với một số ứng dụng Django trên cả Windows và Linux và nó hoạt động như một cơ duyên!


4
Nếu __file__không thể được sử dụng, sau đó sử dụng sys.argv[0]thay vì dirname(__file__). Phần còn lại nên làm việc như mong đợi. Tôi thích sử dụng __file__vì trong mã thư viện, sys.argv[0]có thể không trỏ đến mã của bạn, đặc biệt nếu được nhập qua một số tập lệnh của bên thứ 3.
André Caron

1
Vấn đề với điều này là nó sẽ thay đổi nếu tệp bạn đang chạy là từ bộ ngắt trực tiếp hoặc nếu nó được nhập. Xem câu trả lời của tôi để biết sự khác biệt giữa tệp và sys.argv [0]
Zimm3r

Vì vậy, có đúng không khi nói rằng biến thể được mô tả trong câu trả lời của Zimm3r được xử lý bằng cách sử dụng realpath( join( getcwd(), dirname(__file__) ))như được mô tả ở đây?
pianoJames

44

Để trích dẫn từ tài liệu Python:

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. Nếu thư mục tập lệnh không khả dụng (ví dụ: nếu trình thông dịch được gọi tương tác hoặc nếu tập lệnh được đọc từ đầu vào tiêu chuẩn), đường dẫn [0] là chuỗi trống, điều hướng Python đến các mô-đun tìm kiếm trong thư mục hiện tại trước tiên. Lưu ý rằng thư mục script được chèn trước khi các mục được chèn là kết quả của PYTHONPATH.

sys.path [0] là những gì bạn đang tìm kiếm.


10
Và cho đường dẫn đầy đủ của tập tin : os.path.join(sys.path[0], 'some file.txt'). Điều đó sẽ xử lý không gian và dấu gạch chéo chính xác trên tất cả các hệ thống.
Jacktose

Câu trả lời này cho câu hỏi đầu tiên, không phải câu hỏi sau EDIT.
mcoolive

22

Ok đây là những gì tôi làm

sys.argv luôn là những gì bạn nhập vào terminal hoặc sử dụng làm đường dẫn tệp khi thực thi nó với python.exe hoặc pythonw.exe

Ví dụ: bạn có thể chạy tệp text.txt theo nhiều cách, mỗi cách cung cấp cho bạn một câu trả lời khác nhau, chúng luôn cung cấp cho bạn đường dẫn mà python đã nhập.

    C:\Documents and Settings\Admin>python test.py
    sys.argv[0]: test.py
    C:\Documents and Settings\Admin>python "C:\Documents and Settings\Admin\test.py"
    sys.argv[0]: C:\Documents and Settings\Admin\test.py

Ok để biết bạn có thể lấy tên tệp, rất nhiều, bây giờ để có được thư mục ứng dụng bạn có thể biết sử dụng os.path, cụ thể là abspath và dirname

    import sys, os
    print os.path.dirname(os.path.abspath(sys.argv[0]))

Điều đó sẽ tạo ra điều này:

   C:\Documents and Settings\Admin\

nó sẽ luôn xuất ra điều này bất kể bạn nhập python test.py hay python "C: \ Documents and Settings \ Admin \ test.py"

Vấn đề với việc sử dụng __file__ Hãy xem xét hai tệp này test.txt

import sys
import os

def paths():
        print "__file__: %s" % __file__
        print "sys.argv: %s" % sys.argv[0]

        a_f = os.path.abspath(__file__)
        a_s = os.path.abspath(sys.argv[0])

        print "abs __file__: %s" % a_f
        print "abs sys.argv: %s" % a_s

if __name__ == "__main__":
    paths()

import_test.py

import test
import sys

test.paths()

print "--------"
print __file__
print sys.argv[0]

Đầu ra của "python test.py"

C:\Documents and Settings\Admin>python test.py
__file__: test.py
sys.argv: test.py
abs __file__: C:\Documents and Settings\Admin\test.py
abs sys.argv: C:\Documents and Settings\Admin\test.py

Đầu ra của "python test_import.py"

C:\Documents and Settings\Admin>python test_import.py
__file__: C:\Documents and Settings\Admin\test.pyc
sys.argv: test_import.py
abs __file__: C:\Documents and Settings\Admin\test.pyc
abs sys.argv: C:\Documents and Settings\Admin\test_import.py
--------
test_import.py
test_import.py

Vì vậy, như bạn có thể thấy tệp luôn cung cấp cho bạn tệp python mà nó đang được chạy từ đó, trong đó sys.argv [0] cung cấp cho bạn tệp mà bạn đã chạy từ trình thông dịch luôn. Tùy thuộc vào nhu cầu của bạn, bạn sẽ cần phải chọn cái nào phù hợp nhất với nhu cầu của bạn.


3
Đây là một bằng chứng công phu rằng việc thực hiện phản ánh các tài liệu. __file__được cho là "luôn cung cấp cho bạn đường dẫn đến tệp hiện tại" và sys.argv[0]được cho là "luôn luôn cung cấp đường dẫn của tập lệnh khởi tạo tiến trình". Trong mọi trường hợp, sử dụng __file__trong tập lệnh được gọi luôn cho bạn kết quả chính xác.
André Caron

Nếu bạn có tài liệu tham khảo __file__ở cấp cao nhất của tập lệnh, nó sẽ hoạt động như mong đợi.
Matthew Schinckel

-1

Tôi đã có thể sử dụng mã được cung cấp bởi dcolish thành công vì tôi gặp vấn đề tương tự với việc đọc một tệp văn bản cụ thể. Tệp không nằm trong cùng cwd với tệp Python.


1
Vui lòng không thêm "cảm ơn" làm câu trả lời. Một khi bạn có đủ danh tiếng , bạn sẽ có thể bỏ phiếu cho các câu hỏi và câu trả lời mà bạn thấy hữu ích. - Từ đánh giá
Roberto Caboni

-3

Tôi sẽ làm theo cách này:

from os.path import abspath, exists

f_path = abspath("fooabar.txt")

if exists(f_path):
    with open(f_path) as f:
        print f.read()

Đoạn mã trên xây dựng một đường dẫn tuyệt đối đến tệp bằng cách sử dụng abspath và tương đương với việc sử dụng normpath(join(os.getcwd(), path))[đó là từ pydocs]. Sau đó, nó sẽ kiểm tra xem tệp đó có thực sự tồn tại hay không và sau đó sử dụng trình quản lý ngữ cảnh để mở tệp để bạn không phải nhớ gọi gần trên phần xử lý tệp. IMHO, làm theo cách này sẽ giúp bạn tiết kiệm rất nhiều đau đớn trong thời gian dài.


Điều này không trả lời câu hỏi của người đăng. dln385 nói cụ thể rằng os.path.abspathkhông giải quyết các đường dẫn đến các tệp trong cùng thư mục với tập lệnh nếu tập lệnh không nằm trong thư mục hiện tại.
André Caron

AH! Tôi giả sử người dùng đang chạy tập lệnh này trong cùng một thư mục với tệp họ muốn đọc, KHÔNG phải trong thư mục mô-đun của một cái gì đó trong PYTHONPATH của họ. Điều đó sẽ dạy tôi đưa ra giả định ...
dcolish

abspath sẽ không hoạt động vì không thể chạy python để tìm kiếm trên hệ thống tệp hệ điều hành bằng cách sử dụng chức năng như thế này.
akshat thakar
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.