Как мне получить путь к текущему исполняемому файлу в Python?

193

Это может показаться вопросом новичка, но это не так. Некоторые общие подходы работают не во всех случаях:

sys.argv [0]

Это означает использование path = os.path.abspath(os.path.dirname(sys.argv[0])), но это не работает, если вы запускаете из другого скрипта Python в другом каталоге, и это может случиться в реальной жизни.

__файл__

Это означает использование path = os.path.abspath(os.path.dirname(__file__)), но я обнаружил, что это не работает:

  • py2exeне имеет __file__атрибута, но есть обходной путь
  • Когда вы запускаете из IDLE с execute()нет __file__атрибута
  • OS X 10.6, где я получаю NameError: global name '__file__' is not defined

Связанные вопросы с неполными ответами:

Я ищу общее решение , которое будет работать во всех вышеупомянутых случаях использования.

Обновить

Вот результат теста:

Вывод python a.py (в 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")

подкаталог / 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:.
|   a.py
\---subdir
        b.py
Сорин
источник

Ответы:

80

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

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

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

Если у вас есть несколько основных сценариев в разных каталогах, вам может потребоваться более одной копии module_locator.

Конечно, если ваш основной сценарий загружен каким-либо другим инструментом, который не позволяет импортировать модули, которые находятся рядом с вашим сценарием, то вам не повезло. В таких случаях информация, которую вы ищете, просто не существует нигде в вашей программе. Лучше всего подать сообщение об ошибке авторам инструмента.

Даниэль Штутцбах
источник
2
Я упоминаю, что на OS 10.6 я NameError: global name '__file__' is not definedиспользую файл, и это не внутри IDLE. Подумайте, что __file__определяется только внутри модулей.
Сорин
1
@ Сорин Sbarnea: я обновил свой ответ, как я обойти это.
Даниэль Штутцбах
2
Спасибо, но на самом деле проблема с отсутствием не __file__имеет ничего общего с Unicode. Я не знаю, почему __file__это не определено, но я ищу общее решение, которое будет работать во всех случаях.
Сорин
1
Извините, это возможно не во всех случаях. Например, я пытаюсь сделать это в waf.googlecode.com из файла wscript (python). Эти файлы выполняются, но они не являются модулями, и вы не можете сделать их модулями (они могут быть любыми подкаталогами из исходного дерева).
Сорин
1
Разве это не даст вам местоположение some_path/module_locator.pyвместо?
Casebash
68

Во-первых, вам нужно импортировать из inspectиos

from inspect import getsourcefile
from os.path import abspath

Далее, где вы хотите найти исходный файл, вы просто используете

abspath(getsourcefile(lambda:0))
ArtOfWarfare
источник
Лучший ответ. Спасибо.
Деван Уильямс
4
Отличный ответ. Спасибо. Он также кажется самым коротким и наиболее переносимым (работает на разных ОС) и не сталкивается с такими проблемами, как NameError: global name '__file__' is not defined(другое решение вызвало это).
Эдвард
Еще одна возможность , которая в равной степени короче: lambda:_. Это сработало для меня - не уверен, что всегда будет работать. lambda:0вероятно , работает быстрее некоторой несоизмеримо малым количеством (или , возможно , не так уж мало ... может быть нагрузка немедленно или что - то еще быстрее 0против нагрузки глобальной для _?) Спорно ли это чище и проще для чтения или даже более умным / затемнить.
ArtOfWarfare
Как и почти с каждым предложенным мною предложением, для меня просто возвращается cwd, а не файл каталога, с которым я работаю (отладка).
Джеймс
1
@James - этот код входит в файл, который вы запускаете ... запуск getsourcefile(lambda:0)будет бессмысленным и просто вернется, Noneесли вы попытаетесь запустить его в интерактивном режиме (так как lambdaего не будет в любом исходном файле). Если вы хотите знать, откуда другая функция или объект приходит в интерактивной среде, может быть, abspath(getsourcefile(thatFunctionOrObject))будет более полезным для вас?
ArtOfWarfare
16

это решение является надежным даже в исполняемых файлах

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))
Хосе Креспо Барриос
источник
2
Это должен быть правильный ответ. Это работает даже в entry_point: console_script, но ни один из других ответов.
Polv
15

Я столкнулся с подобной проблемой, и я думаю, что это может решить проблему:

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

Это работает для обычных скриптов и в режиме ожидания. Все, что я могу сказать, это попробовать это для других!

Мое типичное использование:

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

global __modpath__
__modpath__ = module_path(main)

Теперь я использую __modpath__ вместо __file__.

Гаррет Берг
источник
2
Согласно руководству по стилю кодирования PEP8 , никогда не следует создавать имена с двойным начальным и конечным подчеркиванием, поэтому __modpath__их следует переименовывать. Вам также, вероятно, не нужно globalутверждение. В противном случае +1!
Мартино
4
На самом деле вы можете определить локальную функцию прямо в вызове module_path(). то есть module_path(lambda _: None)который не зависит от другого содержания сценария, в котором он находится.
martineau
@martineau: Я принял ваше предложение lambda _: Noneи использовал его в течение почти двух последних лет, но только сейчас я обнаружил, что могу сократить его до просто lambda:0. Есть ли какая-то конкретная причина, по которой вы предложили форму, которую вы указали, с игнорируемым аргументом _, а не без аргумента вообще? Есть ли что-то превосходное в Noneпрефиксе с пробелом, а не просто 0? Они оба одинаково загадочны, я думаю, только один имеет длину 8 символов, а другой - 14 символов.
ArtOfWarfare
@ArtOfWarfare: Разница между ними заключается в том, что lambda _:эта функция принимает один аргумент, а lambda:другая - не принимает. Это не имеет значения, так как функция никогда не вызывается. Также не имеет значения, какое возвращаемое значение используется. Я думаю, что я выбрал, Noneпотому что в то время, казалось, это указывало на то, что это была функция «ничего не делать, никогда не вызываться» лучше. Пространство перед ним не является обязательным, и опять же там только для улучшения читабельности (всегда пытаться следовать PEP8 формирует привычку).
Мартино
@martineau: Это очевидно злоупотребление lambdaтем, чтобы использовать его для чего-то, что никогда не предназначалось. Если бы вы следовали PEP8, я думаю, что правильного содержания для него было бы pass, нет None, но неуместно помещать заявление в a lambda, так что вы должны вставить что-то со значением. Есть несколько допустимых двухсимвольных значений, которые вы можете вставить, но я думаю, что единственно допустимые односимвольные значения, которые вы можете вставить, это 0-9 (или однозначное имя переменной, назначенное вне lambda.). Я считаю, что 0лучше всего указывать на пустоту 0- 9.
ArtOfWarfare
6

Короткий ответ: не существует гарантированного способа получения необходимой информации , однако есть эвристические методы, которые практически всегда работают на практике. Вы можете посмотреть, как найти расположение исполняемого файла в C? , В нем обсуждается проблема с точки зрения Си, но предложенные решения легко транскрибируются в Python.

Дейл Хагглунд
источник
Здравствуйте, мой флаг был отклонен, поэтому я начал обсуждение meta: meta.stackoverflow.com/questions/277272/…
ArtOfWarfare
Однако на этой странице уже есть простые рабочие ответы. Мое решение работает хорошо: stackoverflow.com/a/33531619/3787376 .
Эдвард
5

См. Мой ответ на вопрос « Импортирование модулей из родительской папки» для получения соответствующей информации, в том числе почему мой ответ не использует ненадежную __file__переменную. Это простое решение должно быть совместимо с различными операционными системами в качестве модулей osи inspectвходить в состав Python.

Во-первых, вам нужно импортировать части модулей inspect и os .

from inspect import getsourcefile
from os.path import abspath

Затем используйте следующую строку в любом другом месте, где это необходимо в вашем коде Python:

abspath(getsourcefile(lambda:0))

Как это устроено:

Из встроенного модуля os(описание ниже) abspathинструмент импортируется.

Подпрограммы ОС для Mac, NT или Posix в зависимости от того, на какой системе мы работаем.

Затем getsourcefile(описание ниже) импортируется из встроенного модуля inspect.

Получите полезную информацию от живых объектов Python.

  • abspath(path) возвращает абсолютную / полную версию пути к файлу
  • getsourcefile(lambda:0)каким-то образом получает внутренний исходный файл объекта функции lambda, поэтому возвращается '<pyshell#nn>'в оболочку Python или возвращает путь к файлу кода Python, выполняемого в данный момент.

Использование abspathв результате getsourcefile(lambda:0)должно убедиться, что сгенерированный путь к файлу является полным путем к файлу Python.
Это объясненное решение изначально было основано на коде из ответа в разделе Как получить путь к текущему исполняемому файлу в Python? ,

Эдвард
источник
да , я только что пришел с тем же раствором ... способ лучше , чем ответы , которые просто говорят , что это не может быть надежно сделано ... нет , если вопрос не спрашивает , что я думаю , что это ...
Грейди игрок
5

Вы просто позвонили:

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

вместо того:

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

abspath()дает вам абсолютный путь sys.argv[0](имя файла, в котором находится ваш код) и dirname()возвращает путь к каталогу без имени файла.

DGB
источник
4

Это должно сделать кросс-платформенное решение (если вы не используете интерпретатор или что-то в этом роде):

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]каталог, в котором находится ваш вызывающий скрипт (в первую очередь он ищет модули, которые будут использоваться этим скриптом). Мы можем взять имя файла самого конца sys.argv[0](что я и сделал os.path.basename). os.path.joinпросто склеивает их кросс-платформенным способом. os.path.realpathпросто удостоверимся, что если мы получим какие-либо символические ссылки с именами, отличными от самого скрипта, мы получим настоящее имя скрипта.

У меня нет Mac; Итак, я не проверял это на одном. Пожалуйста, дайте мне знать, если это работает, как кажется, должно. Я проверил это в Linux (Xubuntu) с Python 3.4. Обратите внимание, что многие решения этой проблемы не работают на компьютерах Mac (поскольку я слышал, что их __file__нет на компьютерах Mac).

Обратите внимание, что если ваш скрипт является символической ссылкой, он даст вам путь к файлу, на который он ссылается (а не путь к символической ссылке).

Brōtsyorfuzthrāx
источник
2

Вы можете использовать Pathиз pathlibмодуля:

from pathlib import Path

# ...

Path(__file__)

Вы можете использовать вызов, чтобы parentпойти дальше по пути:

Path(__file__).parent
Гавриил Коэн
источник
В этом ответе используется __file__переменная, которая может быть ненадежной (не всегда полный путь к файлу, не работает в каждой операционной системе и т. Д.), Как часто упоминают пользователи StackOverflow. Изменение ответа, чтобы не включать его, вызовет меньше проблем и будет более совместимым. Для получения дополнительной информации см. Stackoverflow.com/a/33532002/3787376 .
Эдвард
@ mrroot5 Хорошо, удалите свой комментарий, пожалуйста.
Гавриил Коэн
1

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

sys._getframe().f_code.co_filename

Вы также можете получить имя функции как f_code.co_name

Алекс Кон
источник
0

Просто добавьте следующее:

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

Или:

from sys import *
print(sys.argv[0])
Х. Камран
источник
0

Мое решение:

import os
print(os.path.dirname(os.path.abspath(__file__)))
ThienSuBS
источник
-1
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))
Mohamed.Abdo
источник
Это не имеет смысла. Во-первых, '__file__'не должно быть строки, во-вторых, если вы это сделали __file__, это будет работать только для файла, в котором находится эта строка кода, а не для файла, который выполняется?
Андреас