Чи атрибут модуля __file__ абсолютний чи відносний?


107

У мене проблеми з розумінням __file__. З того, що я розумію, __file__повертає абсолютний шлях, з якого був завантажений модуль.

У мене виникають проблеми з цим: у мене є abc.pyодне твердження print __file__, яке працює від /d/projects/ python abc.pyвіддачі abc.py. працює від /d/повернень projects/abc.py. Будь-які причини, чому?


10
Ось що Гідо має сказати з цього приводу: mail.python.org/pipermail/python-dev/2010-February/097461.html
kindall

Відповіді:


98

З документації :

__file__- це ім'я файлу, з якого був завантажений модуль, якщо він завантажений з файлу. __file__Атрибут відсутня для модулів C, які статично пов'язані в інтерпретатор; для модулів розширень, що динамічно завантажуються із спільної бібліотеки, це ім'я файлу файлу спільної бібліотеки.

Із потоку списку розсилки, пов’язаного @kindall у коментарі до питання:

Я не намагався спростовувати цей конкретний приклад, але причина полягає в тому, що нам не хочеться викликати getpwd () при кожному імпорті, а також не хочемо мати якусь змінну в процесі обробки, щоб кешувати поточний каталог. (getpwd () є відносно повільним і іноді може вийти з ладу прямо, і намагаючись кешувати його, є певний ризик помилитися.)

Замість цього ми робимо код у site.py, який переходить через елементи sys.path і перетворює їх на абсолютні шляхи. Однак цей код працює до того, як "" буде вставлено в передній частині sys.path, так що початкове значення sys.path - "".

За іншим цим sys.pathне враховуйте ''.

Отже, якщо ви знаходитесь поза частиною, sys.pathщо містить модуль, ви отримаєте абсолютний шлях . Якщо ви знаходитесь всередині частини, sys.pathщо містить модуль, ви отримаєте відносний шлях .

Якщо ви завантажите модуль у поточний каталог, а поточний каталог не знаходиться sys.path, ви отримаєте абсолютний шлях.

Якщо ви завантажите модуль у поточний каталог, а поточний каталог є в sys.path, ви отримаєте відносний шлях.


так це означає, що якщо є шлях від '' до модуля, застосовується відносний шлях, якщо не абсолютний шлях буде використовуватися, оскільки решта sys.path є абсолютними ..
goh

4
Якщо ви завантажите модуль у поточний каталог, а поточний каталог не знаходиться sys.path, ви отримаєте абсолютний шлях. Якщо ви завантажуєте модуль в поточному каталозі, і поточний каталог знаходиться в sys.path, ви отримаєте відносний шлях.
agf

Пам'ятайте, що для цієї мети sys.pathне включає ''.
agf

я отримав це, але @agf, якщо я використовую python /foo/abc.py з / home, я вважаю, що частина sys.path, яка містить модуль, є / home / foo і мій поточний каталог / home /, чому друкує файл дає мені відносний шлях?
goh

55

__file__є абсолютним з моменту Python 3.4 , за винятком випадків, коли виконується сценарій безпосередньо, використовуючи відносний шлях:

__file__Атрибути модуля (і пов'язані з ними значення) тепер повинні завжди містити абсолютні шляхи за замовчуванням, за винятком випадків, __main__.__file__коли сценарій виконується безпосередньо з використанням відносного шляху. ( Доповідь Бретта Кеннона в bpo-18416 .)

Не впевнений, чи він вирішує символьні посилання.

Приклад проходження відносного шляху:

$ python script.py

1
Дякую. Це важкий факт, який можна відстежити!
meawoppl

4
Це не вірно для Python 3.4.0 ( Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux). І посилання на моїх випробуваннях не вирішені.
Заморожене полум'я

@FrozenFlame, не соромтеся повідомляти на bugs.python.org, якщо 3.4.1 цього не виправить.
anatoly techtonik

2
Чи os.path.realpath(__file__)є правильним способом вирішення символічних посилань?
кевінарпе


16

Пізній простий приклад:

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

Під Python-2. * Другий виклик неправильно визначає на path.abspath(__file__)основі поточного каталогу:

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

Як зазначає @techtonik, в Python 3.4+ це буде добре, оскільки __file__повертає абсолютний шлях.


... крім __main__модуля, де __file__ може бути відносний шлях.
0xC0000022L

5

За допомогою пошти Guido, наданої @kindall, ми можемо зрозуміти стандартний процес імпорту як намагання знайти модуль у кожного учасника sys.pathта подати файл у результаті цього пошуку (детальніше в модулях PyMOTW і Imports .). Отже, якщо модуль розташований в абсолютному шляху, sys.pathрезультат є абсолютним, але якщо він розташований у відносному шляху, sys.pathто результат є відносним.

Тепер site.pyфайл запуску піклується про доставку лише абсолютного шляху в sys.path, крім початкового '', тому якщо ви не змінюєте його іншим способом, ніж встановлення PYTHONPATH (шлях якого також стає абсолютним, перед префіксацією sys.path), ви завжди отримаєте абсолют шлях, але коли доступ до модуля здійснюється через поточний каталог.

Тепер, якщо ви обдурите sys.path кумедним чином, ви можете отримати що завгодно.

В якості прикладу , якщо у вас є модуль зразка foo.pyв /tmp/с кодом:

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

Якщо ви зайшли / tmp, ви отримаєте:

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

Якщо /home/userви входите, якщо ви додасте /tmpсвоє, PYTHONPATHви отримуєте:

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

Навіть якщо ви додасте ../../tmp, це нормалізується, і результат такий же.

Але якщо замість того, щоб використовувати PYTHONPATHви безпосередньо використовуєте якийсь смішний шлях, ви отримаєте результат такий же смішний, як і причина.

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

У цитованій вище темі Гуїдо пояснює, чому пітон не намагається перетворити всі записи в абсолютні шляхи:

ми не хочемо викликати getpwd () під час кожного імпорту .... getpwd () є відносно повільним і іноді може вийти з ладу прямо,

Тож ваш шлях використовується таким, яким він є .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.