Thuộc tính __file__ của mô-đun là tuyệt đối hay tương đối?


107

Tôi không hiểu __file__. Từ những gì tôi hiểu, __file__trả về đường dẫn tuyệt đối mà từ đó mô-đun đã được tải.

Tôi đang gặp sự cố khi tạo điều này: Tôi có abc.pycâu lệnh với một print __file__, chạy từ /d/projects/ python abc.pytrả về abc.py. chạy từ /d/lợi nhuận projects/abc.py. Bất kỳ lý do tại sao?


10
Đây là những gì Guido phải nói về điều này: mail.python.org/pipermail/python-dev/2010-Feb 2/097461.html
kindall

Câu trả lời:


98

Từ tài liệu :

__file__là tên đường dẫn của tệp mà từ đó mô-đun được tải, nếu nó được tải từ một tệp. Các __file__thuộc tính là không có mặt cho module C được liên kết tĩnh thành người phiên dịch; đối với các mô-đun mở rộng được tải động từ thư viện được chia sẻ, nó là tên đường dẫn của tệp thư viện được chia sẻ.

Từ chuỗi danh sách gửi thư được liên kết bởi @kindall trong một nhận xét cho câu hỏi:

Tôi đã không thử lặp lại ví dụ cụ thể này, nhưng lý do là chúng tôi không muốn phải gọi getpwd () trên mỗi lần nhập cũng như chúng tôi không muốn có một số loại biến trong quá trình để lưu vào bộ đệm thư mục hiện tại. (getpwd () tương đối chậm và đôi khi có thể bị lỗi hoàn toàn và việc cố gắng lưu vào bộ nhớ cache sẽ có nguy cơ sai nhất định.)

Thay vào đó, những gì chúng tôi làm là viết mã trong site.py để lướt qua các phần tử của sys.path và biến chúng thành các đường dẫn tuyệt đối. Tuy nhiên, mã này chạy trước khi '' được chèn vào phía trước của sys.path, do đó giá trị ban đầu của sys.path là ''.

Đối với phần còn lại của điều này, hãy xem xét sys.pathkhông bao gồm ''.

Vì vậy, nếu bạn ở bên ngoài phần sys.pathchứa mô-đun, bạn sẽ nhận được một đường dẫn tuyệt đối . Nếu bạn đang ở bên trong phần sys.pathchứa mô-đun, bạn sẽ nhận được một đường dẫn tương đối .

Nếu bạn tải một mô-đun trong thư mục hiện tại và thư mục hiện tại không ở trong sys.path, bạn sẽ nhận được một đường dẫn tuyệt đối.

Nếu bạn tải một mô-đun trong thư mục hiện tại và thư mục hiện tại nằm trong đó sys.path, bạn sẽ nhận được một đường dẫn tương đối.


vì thế không có nghĩa rằng nếu có một con đường từ '' thành các mô-đun, một đường dẫn tương đối sẽ được sử dụng, nếu không phải là một đường dẫn tuyệt đối sẽ được sử dụng kể từ khi phần còn lại của sys.path là tuyệt đối ..
goh

4
Nếu bạn tải một mô-đun trong thư mục hiện tại và thư mục hiện tại không ở trong sys.path, bạn sẽ nhận được một đường dẫn tuyệt đối. Nếu bạn tải một mô-đun trong thư mục hiện tại và thư mục hiện tại nằm trong đó sys.path, bạn sẽ nhận được một đường dẫn tương đối.
agf

Hãy nhớ rằng, cho mục đích này, sys.pathkhông bao gồm ''.
agf

hiểu rồi, nhưng @agf, nếu tôi sử dụng python /foo/abc.py from / home, tôi cho rằng một phần của sys.path chứa mô-đun là / home / foo và thư mục hiện tại của tôi là / home /, tại sao lại in tập tin cho tôi một đường dẫn tương đối?
goh

55

__file__là tuyệt đối kể từ Python 3.4 , ngoại trừ khi thực thi một tập lệnh trực tiếp bằng đường dẫn tương đối:

Các __file__thuộc tính mô-đun (và các giá trị liên quan) giờ đây phải luôn chứa các đường dẫn tuyệt đối theo mặc định, với ngoại lệ duy nhất là __main__.__file__khi tập lệnh được thực thi trực tiếp bằng đường dẫn tương đối. (Do Brett Cannon đóng góp trong bpo-18416 .)

Tuy nhiên, không chắc liệu nó có giải quyết được các liên kết tượng trưng hay không.

Ví dụ về truyền một đường dẫn tương đối:

$ python script.py

1
Cảm ơn. Đây là một sự thật khó theo dõi!
urwoppl

4
Điều này không đúng với Python 3.4.0 ( Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux). Và các liên kết tượng trưng không được giải quyết trong các thử nghiệm của tôi.
Frozen Flame

@FrozenFlame, vui lòng báo cáo cho bug.python.org nếu 3.4.1 không sửa được.
anatoly techtonik

2
Cách os.path.realpath(__file__)chính xác để giải quyết các liên kết tượng trưng?
kevinarpe


16

Ví dụ đơn giản muộn:

from os import path, getcwd, chdir

def print_my_path():
    print('cwd:     {}'.format(getcwd()))
    print('__file__:{}'.format(__file__))
    print('abspath: {}'.format(path.abspath(__file__)))

print_my_path()

chdir('..')

print_my_path()

Trong Python-2. *, Lệnh gọi thứ hai xác định không chính xác path.abspath(__file__)dựa trên thư mục hiện tại:

cwd:     C:\codes\py
__file__:cwd_mayhem.py
abspath: C:\codes\py\cwd_mayhem.py
cwd:     C:\codes
__file__:cwd_mayhem.py
abspath: C:\codes\cwd_mayhem.py

Theo lưu ý của @techtonik, trong Python 3.4+, điều này sẽ hoạt động tốt vì __file__trả về một đường dẫn tuyệt đối.


... ngoại trừ __main__mô-đun, nơi __file__ có thể là một đường dẫn tương đối.
0xC0000022L

5

Với sự trợ giúp của thư Guido do @kindall cung cấp, chúng tôi có thể hiểu quy trình nhập tiêu chuẩn khi cố gắng tìm mô-đun trong từng thành viên sys.pathvà tệp là kết quả của việc tra cứu này (thêm chi tiết trong Mô-đun và Nhập PyMOTW .). Vì vậy, nếu mô-đun được đặt trong một đường dẫn tuyệt đối trong sys.pathkết quả là tuyệt đối, nhưng nếu nó nằm trong một đường dẫn tương đối trong sys.pathkết quả là tương đối.

Bây giờ site.pytệp khởi động sẽ chỉ cung cấp đường dẫn tuyệt đối trong sys.path, ngoại trừ đường dẫn đầu tiên '', vì vậy nếu bạn không thay đổi nó bằng cách khác ngoài việc đặt PYTHONPATH (đường dẫn của nó cũng được đặt là tuyệt đối, trước tiền tố sys.path), bạn sẽ luôn nhận được đường dẫn tuyệt đối nhưng khi mô-đun được truy cập thông qua thư mục hiện tại.

Bây giờ nếu bạn lừa sys.path một cách hài hước, bạn có thể lấy được bất cứ thứ gì.

Một ví dụ nếu bạn có một mô-đun mẫu foo.pytrong /tmp/với mã:

import sys
print(sys.path)
print (__file__)

Nếu bạn vào / tmp, bạn sẽ nhận được:

>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
./foo.py

Khi vào /home/user, nếu bạn thêm /tmpbạn PYTHONPATHsẽ nhận được:

>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
/tmp/foo.py

Ngay cả khi bạn thêm ../../tmp, nó sẽ được chuẩn hóa và kết quả là như nhau.

Nhưng nếu thay vì sử dụng, PYTHONPATHbạn sử dụng trực tiếp một số đường dẫn vui nhộn, bạn sẽ nhận được kết quả là nguyên nhân gây cười.

>>> import sys
>>> sys.path.append('../../tmp')
>>> import foo
['', '/usr/lib/python3.3', .... , '../../tmp']
../../tmp/foo.py

Guido giải thích trong chuỗi trích dẫn ở trên, tại sao python không cố gắng chuyển đổi tất cả các mục nhập theo đường dẫn tuyệt đối:

chúng tôi không muốn phải gọi getpwd () trên mỗi lần nhập .... getpwd () tương đối chậm và đôi khi có thể bị lỗi hoàn toàn,

Vì vậy, đường dẫn của bạn được sử dụng như nó vốn có .

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.