Як отримати шлях до поточного виконаного файлу в Python?


193

Це може здатися питанням для новачків, але це не так. Деякі поширені підходи працюють не у всіх випадках:

sys.argv [0]

Це означає використовувати path = os.path.abspath(os.path.dirname(sys.argv[0])), але це не працює, якщо ви працюєте з іншого сценарію Python в іншому каталозі, і це може статися в реальному житті.

__file__

Це означає використовувати 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")

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

Якщо у вас є кілька основних сценаріїв у різних каталогах, вам може знадобитися більше однієї копії модуля_локатора.

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


2
Я згадую, що в ОС 10.6 я NameError: global name '__file__' is not definedвикористовую файл, і це не знаходиться всередині IDLE. Подумайте, що __file__визначено лише всередині модулів.
sorin

1
@Sorin Sbarnea: Я оновив свою відповідь тим, як мені це подолати.
Даніель Штуцбах

2
Дякую, але насправді проблема з відсутністю __file__не мала нічого спільного з Unicode. Я не знаю, чому __file__це не визначено, але я шукаю загальне рішення, це спрацює у всіх випадках.
sorin

1
Вибачте, це не можливо у всіх випадках. Наприклад, я намагаюся зробити це в waf.googlecode.com з файлу wscript (python). Ці файли виконуються, але вони не є модулями, і ви не можете їх робити модулями (вони можуть бути будь-яким підкаталогом із дерева джерел).
sorin

1
Хіба це не дасть вам розташування some_path/module_locator.pyзамість цього?
Casebash

68

По-перше, вам потрібно імпортувати з inspectіos

from inspect import getsourcefile
from os.path import abspath

Далі, де б ви не хотіли знайти вихідний файл, який ви просто використовуєте

abspath(getsourcefile(lambda:0))

Найкраща відповідь. Дякую.
Деван Вільямс

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, але ніхто з інших не відповідає.
Полв

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, але це неправда, якщо ставити заяву в "а" lambda, тому вам доведеться вводити щось зі значенням. Там в кілька дійсних 2 характеру речей , які ви могли б поставити, але я думаю , що тільки дійсні поодинокі символьні речі , які ви можете помістити в 0-9 (або одне ім'я змінної символ призначається поза lambda.) Я вважаю , що 0краще за все вказує на нікчемність 0- 9.
ArtOfWarfare

6

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


Привіт, мій прапор було відхилено, тому я зараз розпочав дискусію щодо мета: 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)якимось чином отримує внутрішній вихідний файл об'єкта функції лямбда, тому повертається '<pyshell#nn>'в оболонку Python або повертає файл файлу поточного коду Python.

Використовуючи abspathрезультат, getsourcefile(lambda:0)слід переконатися, що генерований шлях до файлу - це повний шлях до файлу Python.
Це пояснене рішення спочатку ґрунтувалося на коді з відповіді на сторінці Як отримати шлях до поточного виконаного файлу в Python? .


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

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()повертає шлях до каталогу без імені файлу.


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. Зауважте, що багато рішень цієї проблеми не працюють на Macs (оскільки я це чув__file__ її немає в Macs).

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


2

Ви можете використовувати Pathз pathlibмодуля:

from pathlib import Path

# ...

Path(__file__)

Ви можете використовувати call для того, 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])


-1
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))

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