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

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

У меня возникла проблема с этим: у меня есть abc.py с одним утверждением print __file__, начиная с /d/projects/ python abc.py возвращает abc.py. от /d/ возвращает projects/abc.py. Любые причины, по которым?

Ответы

Ответ 1

Из документации:

__file__ - путь к файлу, из которого был загружен модуль, если он был загружен из файла. __file__ отсутствует для модулей C, которые статически связаны с интерпретатором; для модулей расширения, загружаемых динамически из общей библиотеки, это путь к файлу общей библиотеки.

Из ветки списка рассылки, связанной @kindall в комментарии к вопросу:

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

Вместо этого мы делаем код в site.py, который просматривает элементы sys.path и превращает их в абсолютные пути. Однако этот код выполняется до того, как '' вставляется в начало sys.path, поэтому начальное значение sys.path равно ''.

В остальном, рассмотрите sys.path чтобы не включать ''.

Итак, если вы находитесь за пределами части sys.path, содержащей модуль, вы получите абсолютный путь. Если вы находитесь внутри части sys.path, содержащей модуль, вы получите относительный путь.

Если вы загрузите модуль в текущем каталоге, а текущий каталог отсутствует в sys.path, вы получите абсолютный путь.

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

Ответ 2

__file__ с Python 3.4, __file__ является абсолютным, за исключением случаев, когда скрипт выполняется напрямую, используя относительный путь:

__file__ модуля __file__ (и связанные с ними значения) теперь должны всегда содержать абсолютные пути по умолчанию, за единственным исключением __main__.__file__ когда сценарий выполняется напрямую с использованием относительного пути. (Предоставлено Бреттом Кэнноном в bpo-18416.)

Не уверен, что он разрешает символические ссылки, хотя.

Пример прохождения относительного пути:

$ python script.py

Ответ 3

Поздний простой пример:

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__ возвращает абсолютный путь.

Ответ 4

С помощью почты Guido, предоставляемой @kindall, мы можем понять стандартный процесс импорта, пытаясь найти модуль в каждом члене sys.path, и файл в результате этого поиска (подробнее в Модули и импорт PyMOTW.). Поэтому, если модуль находится в абсолютном пути в sys.path, результат будет абсолютным, но если он находится в относительном пути в sys.path, результат будет относительным.

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

Теперь, если вы trick 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

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

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

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

Ответ 5

if   '\\' in __file__: location='\\'.join(__file__.split('\\')[:-1])    #+'\\'  #For Fun
elif '/' in __file__: location='/'.join(__file__.split('/')[:-1])       #+'/'
else: print('\\/\\/HAT THE FUCK!!')