Атрибут модуля __file__ абсолютный или относительный?

107

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

У меня проблема с этим: у меня есть оператор abc.pywith one print __file__, работающий от /d/projects/ python abc.pyвозвратов abc.py. убегает от /d/возвратов projects/abc.py. По каким причинам?

го
источник
10
Вот что Гвидо говорит по этому поводу: mail.python.org/pipermail/python-dev/2010-Feb
February/
Релевантно: stackoverflow.com/q/9271464/1959808
Иоаннис Филиппидис

Ответы:

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, вы получите относительный путь.

agf
источник
значит ли это, что если существует путь от '' к модулю, будет использоваться относительный путь, если не будет использоваться абсолютный путь, поскольку остаток 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). И в моих испытаниях символические ссылки не разрешаются.
Frozen Flame
@FrozenFlame, не стесняйтесь сообщать на bugs.python.org, если 3.4.1 не исправит это.
анатолий техтоник 03
2
Есть os.path.realpath(__file__)правильный способ разрешить символические ссылки?
kevinarpe 02
3
@kevinarpe, stackoverflow.com/questions/3220755/…
анатолий техтоник 05
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__возвращает абсолютный путь.

SimplyKnownAsG
источник
... кроме __main__модуля, где __file__ может быть относительный путь.
0xC0000022L
5

С помощью почты Guido, предоставленной @kindall, мы можем понять стандартный процесс импорта как попытку найти модуль в каждом члене sys.pathи файле в результате этого поиска (более подробно в PyMOTW Modules and 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

Гвидо объясняет в процитированном выше потоке, почему python не пытается преобразовать все записи в абсолютные пути:

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

Итак, ваш путь используется как есть .

Marcz
источник