Làm cách nào để có được đường dẫn của tệp được thực thi hiện tại trong Python?


193

Điều này có vẻ giống như một câu hỏi của người mới, nhưng nó không phải là. Một số cách tiếp cận phổ biến không hoạt động trong mọi trường hợp:

sys.argv [0]

Điều này có nghĩa là sử dụng path = os.path.abspath(os.path.dirname(sys.argv[0])), nhưng điều này không hoạt động nếu bạn đang chạy từ một tập lệnh Python khác trong thư mục khác và điều này có thể xảy ra trong cuộc sống thực.

__tập tin__

Điều này có nghĩa là sử dụng path = os.path.abspath(os.path.dirname(__file__)), nhưng tôi thấy rằng điều này không hoạt động:

  • py2exekhông có __file__thuộc tính, nhưng có một cách giải quyết
  • Khi bạn chạy từ IDLE execute()mà không có __file__thuộc tính
  • OS X 10.6 nơi tôi nhận được NameError: global name '__file__' is not defined

Các câu hỏi liên quan với câu trả lời không đầy đủ:

Tôi đang tìm kiếm một giải pháp chung , một giải pháp sẽ hoạt động trong tất cả các trường hợp sử dụng ở trên.

Cập nhật

Đây là kết quả của một testcase:

Đầu ra của python a.py (trên Windows)

a.py: __file__= a.py
a.py: os.getcwd()= C:\zzz

b.py: sys.argv[0]= a.py
b.py: __file__= a.py
b.py: os.getcwd()= C:\zzz

a.py

#! /usr/bin/env python
import os, sys

print "a.py: sys.argv[0]=", sys.argv[0]
print "a.py: __file__=", __file__
print "a.py: os.getcwd()=", os.getcwd()
print

execfile("subdir/b.py")

subir / b.py

#! /usr/bin/env python
import os, sys

print "b.py: sys.argv[0]=", sys.argv[0]
print "b.py: __file__=", __file__
print "b.py: os.getcwd()=", os.getcwd()
print

cây

C:.
|   a.py
\---subdir
        b.py

Câu trả lời:


80

Bạn không thể trực tiếp xác định vị trí của tập lệnh chính đang được thực thi. Rốt cuộc, đôi khi kịch bản không đến từ một tập tin nào cả. Ví dụ, nó có thể đến từ trình thông dịch tương tác hoặc mã được tạo động chỉ được lưu trữ trong bộ nhớ.

Tuy nhiên, bạn có thể xác định vị trí của một mô-đun một cách đáng tin cậy, vì các mô-đun luôn được tải từ một tệp. Nếu bạn tạo một mô-đun với mã sau đây và đặt nó vào cùng thư mục với tập lệnh chính của bạn, thì tập lệnh chính có thể nhập mô-đun và sử dụng mô-đun đó để định vị chính nó.

some_path / module_locator.py:

def we_are_frozen():
    # All of the modules are built-in to the interpreter, e.g., by py2exe
    return hasattr(sys, "frozen")

def module_path():
    encoding = sys.getfilesystemencoding()
    if we_are_frozen():
        return os.path.dirname(unicode(sys.executable, encoding))
    return os.path.dirname(unicode(__file__, encoding))

some_path / main.py:

import module_locator
my_path = module_locator.module_path()

Nếu bạn có một số tập lệnh chính trong các thư mục khác nhau, bạn có thể cần nhiều hơn một bản sao của module_locator.

Tất nhiên, nếu tập lệnh chính của bạn được tải bởi một số công cụ khác không cho phép bạn nhập các mô-đun được đặt cùng với tập lệnh của mình, thì bạn đã hết may mắn. Trong trường hợp như vậy, thông tin bạn sau đó đơn giản là không tồn tại ở bất cứ đâu trong chương trình của bạn. Đặt cược tốt nhất của bạn sẽ là gửi một lỗi với các tác giả của công cụ.


2
Tôi đề cập rằng trên OS 10.6 tôi nhận được NameError: global name '__file__' is not definedbằng cách sử dụng tệp và đây không phải là trong IDLE. Hãy nghĩ rằng __file__chỉ được định nghĩa bên trong các mô-đun.
sorin

1
@Sorin Sbarnea: Tôi đã cập nhật câu trả lời của mình với cách tôi khắc phục điều đó.
Daniel Stutzbach

2
Cảm ơn, nhưng trên thực tế, vấn đề mất tích __file__không liên quan gì đến Unicode. Tôi không biết tại sao __file__không được xác định nhưng tôi đang tìm một giải pháp chung chung, nó sẽ hoạt động trong mọi trường hợp.
sorin

1
Xin lỗi điều này là không thể trong mọi trường hợp. Ví dụ: tôi cố gắng làm điều này trong waf.googlecode.com từ bên trong tệp wscript (python). Các tệp này được thực thi nhưng chúng không phải là mô-đun và bạn không thể tạo chúng thành mô-đun (chúng có thể là bất kỳ thư mục con nào từ cây nguồn).
sorin

1
some_path/module_locator.pyThay vào đó sẽ cung cấp cho bạn vị trí thay thế?
Casebash

68

Trước tiên, bạn cần nhập từ inspectos

from inspect import getsourcefile
from os.path import abspath

Tiếp theo, bất cứ nơi nào bạn muốn tìm tệp nguồn từ bạn chỉ cần sử dụng

abspath(getsourcefile(lambda:0))

Câu trả lời tốt nhất. Cảm ơn bạn.
Devan Williams

4
Câu trả lời chính xác. Cảm ơn. Nó dường như cũng là câu trả lời ngắn gọn và di động nhất (chạy trên các hệ điều hành khác nhau) và không gặp phải vấn đề nào như NameError: global name '__file__' is not defined(một giải pháp khác gây ra điều này).
Edward

Một khả năng khác cũng ngắn không kém : lambda:_. Nó làm việc cho tôi - không chắc nó sẽ luôn hoạt động. lambda:0có thể chạy nhanh hơn với một lượng nhỏ vô cùng (hoặc có thể không quá nhỏ ... có thể là tải ngay lập tức hoặc nhanh hơn 0so với tải toàn cầu _?) Tranh cãi về việc nó sạch hơn hay dễ đọc hơn hay thậm chí thông minh hơn / tối nghĩa hơn.
ArtOfWarfare

Như với hầu hết mọi đề xuất tôi đã thử, điều này chỉ trả về cwd cho tôi, không phải tệp thư mục tôi đang chạy (gỡ lỗi).
James

1
@James - Mã này đi vào tệp bạn đang chạy ... đang chạy getsourcefile(lambda:0)sẽ vô nghĩa và chỉ quay lại Nonenếu bạn thử chạy nó tại một dấu nhắc tương tác (vì lambdasẽ không có trong bất kỳ tệp nguồn nào.) Nếu bạn muốn biết nơi một chức năng hoặc đối tượng khác đến từ trong một môi trường tương tác, có thể abspath(getsourcefile(thatFunctionOrObject))sẽ hữu ích hơn cho bạn?
ArtOfWarfare

16

giải pháp này là mạnh mẽ ngay cả trong thực thi

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))

2
Đây phải là câu trả lời chính xác. Điều này hoạt động ngay cả trong entry_point: console_script, nhưng không có câu trả lời khác.
Polv

15

Tôi đã gặp phải một vấn đề tương tự và tôi nghĩ điều này có thể giải quyết vấn đề:

def module_path(local_function):
   ''' returns the module path without the use of __file__.  Requires a function defined
   locally in the module.
   from http://stackoverflow.com/questions/729583/getting-file-path-of-imported-module'''
   return os.path.abspath(inspect.getsourcefile(local_function))

Nó hoạt động cho các kịch bản thông thường và ở chế độ rảnh. Tất cả những gì tôi có thể nói là thử nó cho người khác!

Cách sử dụng điển hình của tôi:

from toolbox import module_path
def main():
   pass # Do stuff

global __modpath__
__modpath__ = module_path(main)

Bây giờ tôi sử dụng __modpath__ thay vì __file__.


2
Theo hướng dẫn về phong cách mã hóa PEP8 , người ta không bao giờ nên tạo tên với dấu gạch dưới hàng đầu và dấu kép - vì vậy __modpath__nên đổi tên. Bạn cũng có thể không cần globaltuyên bố. Nếu không thì +1!
martineau

4
Trên thực tế, bạn có thể xác định chức năng cục bộ ngay trong cuộc gọi đến module_path(). tức là module_path(lambda _: None)không phụ thuộc vào nội dung khác của tập lệnh.
martineau

@martineau: Tôi đã lấy gợi ý của bạn lambda _: Nonevà đã sử dụng nó trong gần hai năm qua, nhưng bây giờ tôi phát hiện ra rằng tôi có thể ngưng tụ nó lại lambda:0. Có bất kỳ lý do cụ thể nào mà bạn đề xuất hình thức bạn đã làm, với một đối số bị bỏ qua _, thay vì không có đối số nào không? Có một cái gì đó vượt trội về Nonetiền tố với một không gian chứ không chỉ là 0? Cả hai đều khó hiểu như nhau, tôi nghĩ, chỉ một cái dài 8 ký tự trong khi cái kia dài 14 ký tự.
ArtOfWarfare

@ArtOfWarfare: Sự khác biệt giữa hai là lambda _:một hàm lấy một đối số và lambda:là một đối số không lấy bất kỳ. Nó không quan trọng vì chức năng không bao giờ được gọi. Tương tự như vậy, nó không quan trọng giá trị trả về được sử dụng. Tôi đoán tôi đã chọn Nonebởi vì tại thời điểm đó dường như nó chỉ ra rằng đó là một chức năng không làm gì, không bao giờ được gọi là tốt hơn. Không gian phía trước nó là tùy chọn và một lần nữa ở đó chỉ để cải thiện khả năng đọc (luôn cố gắng tuân theo PEP8 là hình thành thói quen).
martineau

@martineau: Rõ ràng đó là một sự lạm dụng lambdamặc dù, sử dụng nó cho việc gì đó không bao giờ được thực hiện. Nếu bạn đã theo dõi PEP8, tôi nghĩ rằng nội dung phù hợp với nó sẽ passkhông None, nhưng không hợp lệ để đặt một tuyên bố trong một lambda, vì vậy bạn phải đặt một cái gì đó có giá trị. Có một vài thứ 2 ký tự hợp lệ bạn có thể đặt vào, nhưng tôi nghĩ rằng những ký tự đơn lẻ hợp lệ duy nhất bạn có thể đặt là 0-9 (hoặc một tên biến ký tự duy nhất được gán bên ngoài lambda.) Tôi hình dung 0rõ nhất là không có gì của 0- 9.
ArtOfWarfare

6

Câu trả lời ngắn gọn là không có cách nào đảm bảo để có được thông tin bạn muốn , tuy nhiên có những phương pháp phỏng đoán hoạt động gần như luôn luôn trong thực tế. Bạn có thể nhìn vào Làm thế nào để tôi tìm vị trí của tệp thực thi trong C? . Nó thảo luận về vấn đề theo quan điểm C, nhưng các giải pháp được đề xuất dễ dàng được sao chép thành Python.


Xin chào, cờ của tôi đã bị từ chối vì vậy bây giờ tôi đã bắt đầu một cuộc thảo luận về meta: meta.stackoverflow.com/questions/277272/
Kẻ

Có những câu trả lời làm việc đơn giản đã có trên trang này tuy nhiên. Giải pháp của tôi hoạt động tốt: stackoverflow.com/a/33531619/3787376 .
Edward

5

Xem câu trả lời của tôi cho câu hỏi Nhập mô-đun từ thư mục mẹ để biết thông tin liên quan, bao gồm cả lý do tại sao câu trả lời của tôi không sử dụng __file__biến không đáng tin cậy . Giải pháp đơn giản này phải tương thích chéo với các hệ điều hành khác nhau dưới dạng các mô-đun osinspectlà một phần của Python.

Trước tiên, bạn cần nhập các bộ phận của mô-đun kiểm traos .

from inspect import getsourcefile
from os.path import abspath

Tiếp theo, sử dụng dòng sau bất cứ nơi nào khác trong mã Python của bạn:

abspath(getsourcefile(lambda:0))

Làm thế nào nó hoạt động:

Từ mô-đun tích hợp os(mô tả bên dưới), abspathcông cụ được nhập.

Các thói quen của hệ điều hành cho Mac, NT hoặc Posix tùy thuộc vào hệ thống chúng tôi đang sử dụng.

Sau đó getsourcefile(mô tả bên dưới) được nhập từ mô-đun tích hợp inspect.

Nhận thông tin hữu ích từ các đối tượng Python sống.

  • abspath(path) trả về phiên bản tuyệt đối / đầy đủ của đường dẫn tệp
  • getsourcefile(lambda:0)bằng cách nào đó có được tệp nguồn bên trong của đối tượng hàm lambda, do đó trả về '<pyshell#nn>'trong trình bao Python hoặc trả về đường dẫn tệp của mã Python hiện đang được thực thi.

Sử dụng abspathtrên kết quả getsourcefile(lambda:0)phải đảm bảo rằng đường dẫn tệp được tạo là đường dẫn tệp đầy đủ của tệp Python.
Giải pháp giải thích này ban đầu được dựa trên mã từ câu trả lời tại Làm cách nào để tôi nhận được đường dẫn của tệp được thực thi hiện tại trong Python? .


vâng, tôi vừa đưa ra cùng một giải pháp ... tốt hơn nhiều so với câu trả lời chỉ nói rằng nó không thể được thực hiện một cách đáng tin cậy ... trừ khi câu hỏi không hỏi tôi nghĩ đó là gì ...
Người chơi Grady

5

Bạn đã gọi đơn giản:

path = os.path.abspath(os.path.dirname(sys.argv[0]))

thay vì:

path = os.path.dirname(os.path.abspath(sys.argv[0]))

abspath()cung cấp cho bạn đường dẫn tuyệt đối của sys.argv[0](tên tệp mà mã của bạn nằm trong) và dirname()trả về đường dẫn thư mục mà không có tên tệp.


3

Điều này sẽ thực hiện thủ thuật theo cách đa nền tảng (miễn là bạn không sử dụng trình thông dịch hoặc một cái gì đó):

import os, sys
non_symbolic=os.path.realpath(sys.argv[0])
program_filepath=os.path.join(sys.path[0], os.path.basename(non_symbolic))

sys.path[0]là thư mục chứa tập lệnh gọi của bạn (nơi đầu tiên nó tìm các mô-đun được sử dụng bởi tập lệnh đó). Chúng ta có thể lấy tên của tệp ra khỏi cuối sys.argv[0](đó là những gì tôi đã làm với os.path.basename). os.path.joinchỉ cần gắn chúng lại với nhau theo cách đa nền tảng. os.path.realpathchỉ cần đảm bảo rằng nếu chúng tôi nhận được bất kỳ liên kết tượng trưng nào với các tên khác với chính tập lệnh mà chúng tôi vẫn nhận được tên thật của tập lệnh.

Tôi không có máy Mac; Vì vậy, tôi đã không thử nghiệm điều này trên một. Xin vui lòng cho tôi biết nếu nó hoạt động, vì nó có vẻ như nó nên. Tôi đã thử nghiệm điều này trong Linux (Xubfox) với Python 3.4. Lưu ý rằng nhiều giải pháp cho vấn đề này không hoạt động trên máy Mac (vì tôi đã nghe nói rằng__file__ không có trên máy Mac).

Lưu ý rằng nếu tập lệnh của bạn là một liên kết tượng trưng, ​​nó sẽ cung cấp cho bạn đường dẫn của tệp mà nó liên kết đến (chứ không phải đường dẫn của liên kết tượng trưng).


2

Bạn có thể sử dụng Pathtừ pathlibmô-đun:

from pathlib import Path

# ...

Path(__file__)

Bạn có thể sử dụng lệnh gọi parentđể đi xa hơn trong đường dẫn:

Path(__file__).parent

Câu trả lời này sử dụng __file__biến có thể không đáng tin cậy (không phải luôn luôn là đường dẫn tệp đầy đủ, không hoạt động trên mọi hệ điều hành, v.v.) như người dùng StackOverflow thường đề cập. Thay đổi câu trả lời để không bao gồm nó sẽ gây ra ít vấn đề hơn và tương thích chéo hơn. Để biết thêm thông tin, xem stackoverflow.com/a/33532002/3787376 .
Edward

@ mrroot5 Ok, vậy hãy xóa bình luận của bạn đi.
Gavriel Cohen

1

Nếu mã đến từ một tệp, bạn có thể lấy tên đầy đủ của nó

sys._getframe().f_code.co_filename

Bạn cũng có thể lấy tên hàm như f_code.co_name


0

Chỉ cần thêm vào như sau:

from sys import *
path_to_current_file = sys.argv[0]
print(path_to_current_file)

Hoặc là:

from sys import *
print(sys.argv[0])

0

Giải pháp của tôi là:

import os
print(os.path.dirname(os.path.abspath(__file__)))

-1
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))

Điều này không có ý nghĩa. Thứ nhất, '__file__'không nên là một chuỗi, thứ hai, nếu bạn đã làm __file__, điều này sẽ chỉ hoạt động đối với tệp mà dòng mã này nằm trong, chứ không phải là tệp được thực thi?
Andreas Storvik Strauman
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.