Как получить путь модуля?

757

Я хочу определить, изменился ли модуль. Теперь использовать inotify просто, вам нужно знать каталог, из которого вы хотите получать уведомления.

Как мне получить путь модуля в Python?

бодрый
источник
1
Проверьте modulefinder: docs.python.org/library/modulefinder.html
9
Если вы все еще ищете на этом сайте, пожалуйста , обновите правильный ответ на это . Это намного чище, чем предлагаемое решение, и работает также в тех случаях, когда __file__не установлено.
erikbwork
3
@ erikb85: это не только чище; inspectРешение также работает для execfile()случая, когда __file__молча выдает неправильное имя.
JFS
import pathlib, module; pathlib.Path(module.__file__).resolve().parentЭто зависит от платформы
MortenB

Ответы:

907
import a_module
print(a_module.__file__)

На самом деле даст вам путь к файлу .pyc, который был загружен, по крайней мере, в Mac OS X. Поэтому я думаю, вы можете сделать:

import os
path = os.path.abspath(a_module.__file__)

Вы также можете попробовать:

path = os.path.dirname(a_module.__file__)

Получить каталог модуля.

Orestis
источник
54
это ответит, как получить путь к модулю, который вы импортируете, но не к модулю / скрипту, в котором вы находитесь (для скрипта, который вы запускаете, __file__это не полный путь, а относительный). Для файла, в котором я нахожусь, мне пришлось импортировать другой модуль из того же каталога и сделать, как показано здесь. Кто-нибудь знает более удобный способ?
Бен Брайант
5
@hbdgaf почти уверен, что встроенной функции не существуетself.__file__
Дан Пассаро,
26
@BenBryant @hbdgaf os.path.dirname(__file__)прекрасно работает для меня и возвращает abs путь к каталогу модуля.
Никколо
9
Я попытался сделать это и получить трассировку: AttributeError: 'module' object has no attribute '__file__'
Дориан Дор
5
@DorianDore Я немного поэкспериментировал с модулями и нашел решение path = module.__path__.__dict__["_path"][0], но я не уверен, что он переносимый или он не отличается между версиями Python. Это работает для меня, в отличие от этого ответа, который дает мне ту же ошибку, и inspectответ поднимается TypeError: <module 'module' (namespace)> is a built-in module...
Jezor
279

В inspectпитоне есть модуль.

Официальная документация

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

Пример:

>>> import os
>>> import inspect
>>> inspect.getfile(os)
'/usr/lib64/python2.7/os.pyc'
>>> inspect.getfile(inspect)
'/usr/lib64/python2.7/inspect.pyc'
>>> os.path.dirname(inspect.getfile(inspect))
'/usr/lib64/python2.7'
Томас Томечек
источник
7
Вы также можете использовать, inspectчтобы получить имя текущего файла; см. stackoverflow.com/a/50905/320036
z0r
5
Я много раз гуглял на этот вопрос, и это самый разумный ответ, который я когда-либо видел! Пожалуйста, обновите информацию оinspect.currentframe()
erikbwork
inspect.getfile()подход не работает с модулем _io, но работает с модулем io.
smwikipedia
70

Как уже говорилось в других ответах, лучший способ сделать это с помощью __file__(продемонстрировано снова ниже). Однако есть важное предостережение, которое __file__НЕ существует, если вы запускаете модуль самостоятельно (то есть как __main__).

Например, скажем, у вас есть два файла (оба из которых находятся на вашей PYTHONPATH):

#/path1/foo.py
import bar
print(bar.__file__)

а также

#/path2/bar.py
import os
print(os.getcwd())
print(__file__)

Запуск foo.py даст результат:

/path1        # "import bar" causes the line "print(os.getcwd())" to run
/path2/bar.py # then "print(__file__)" runs
/path2/bar.py # then the import statement finishes and "print(bar.__file__)" runs

ОДНАКО, если вы попытаетесь запустить bar.py самостоятельно, вы получите:

/path2                              # "print(os.getcwd())" still works fine
Traceback (most recent call last):  # but __file__ doesn't exist if bar.py is running as main
  File "/path2/bar.py", line 3, in <module>
    print(__file__)
NameError: name '__file__' is not defined 

Надеюсь это поможет. Это предостережение стоило мне много времени и путаницы при тестировании других представленных решений.

mcstrother
источник
5
В этом случае вы можете использовать sys.argv [0] вместо файла .
Джимоти
4
Это предупреждение о конкретной версии? В 2.6 и 2.7 я успешно полагаюсь на файл , который работает файл, когда имя __ == '__ main '. Единственный случай сбоя, который я видел, связан с "python -c 'print file '". Я добавлю, что иногда файл может быть <stdin>, что происходит, когда IDE, такие как emacs, выполняют текущий буфер.
Пол Дю Буа
1
Обратите внимание, что начальные и конечные символы "__" выделяют слово жирным шрифтом, так что имейте это в виду при чтении предыдущих комментариев :-P
Поль Дю Буа
1
@PaulDuBois Вы можете окружить его с тиками: ` __file__` становиться__file__
fncomp
1
Как вы получаете NameError? @Paul Du Bois: Я пробовал Python 2.3-3.4 и __file__определяется , однако я запускаю файл Python: python a.py, python -ma, ./a.py.
JFS
43

Я попытаюсь решить несколько вариантов и по этому вопросу:

  1. найти путь к вызываемому скрипту
  2. найти путь к исполняемому в данный момент скрипту
  3. найти каталог вызываемого скрипта

(Некоторые из этих вопросов были заданы на SO, но были закрыты как дубликаты и перенаправлены здесь.)

Предостережения об использовании __file__

Для модуля, который вы импортировали:

import something
something.__file__ 

вернет абсолютный путь модуля. Однако, учитывая следующий скрипт foo.py:

#foo.py
print '__file__', __file__

Вызов его с помощью «python foo.py» вернет просто «foo.py». Если вы добавите Шебанг:

#!/usr/bin/python 
#foo.py
print '__file__', __file__

и вызовите его с помощью ./foo.py, он вернет './foo.py'. Вызов его из другого каталога (например, поместите foo.py в строку каталога), затем вызов

python bar/foo.py

или добавив шебанг и выполнив файл напрямую:

bar/foo.py

вернет 'bar / foo.py' ( относительный путь).

Нахождение каталога

Теперь идти оттуда, чтобы получить каталог, os.path.dirname(__file__)также может быть сложно. По крайней мере, в моей системе она возвращает пустую строку, если вы вызываете ее из того же каталога, что и файл. ех.

# foo.py
import os
print '__file__ is:', __file__
print 'os.path.dirname(__file__) is:', os.path.dirname(__file__)

будет выводить:

__file__ is: foo.py
os.path.dirname(__file__) is: 

Другими словами, он возвращает пустую строку, поэтому это не кажется надежным, если вы хотите использовать его для текущего файла (в отличие от файла импортируемого модуля). Чтобы обойти это, вы можете заключить это в вызов abspath:

# foo.py
import os
print 'os.path.abspath(__file__) is:', os.path.abspath(__file__)
print 'os.path.dirname(os.path.abspath(__file__)) is:', os.path.dirname(os.path.abspath(__file__))

который выводит что-то вроде:

os.path.abspath(__file__) is: /home/user/bar/foo.py
os.path.dirname(os.path.abspath(__file__)) is: /home/user/bar

Обратите внимание, что abspath () НЕ разрешает символические ссылки. Если вы хотите сделать это, используйте вместо этого realpath (). Например, создание символической ссылки file_import_testing_link, указывающей на file_import_testing.py, со следующим содержимым:

import os
print 'abspath(__file__)',os.path.abspath(__file__)
print 'realpath(__file__)',os.path.realpath(__file__)

выполнение будет печатать абсолютные пути что-то вроде:

abspath(__file__) /home/user/file_test_link
realpath(__file__) /home/user/file_test.py

file_import_testing_link -> file_import_testing.py

Используя осмотреть

@SummerBreeze упоминает об использовании модуля проверки .

Похоже, это хорошо работает и довольно кратко для импортированных модулей:

import os
import inspect
print 'inspect.getfile(os) is:', inspect.getfile(os)

послушно возвращает абсолютный путь. Однако для нахождения пути выполняемого в данный момент скрипта я не нашел способа его использовать.

jpgeek
источник
1
inspect.getfile (os) аналогичен os .__ file__ из кода: def getfile (object): "" "Определить, в каком исходном или скомпилированном файле был определен объект." "" if ismodule (object): if hasattr (объект, ' файл '): вернуть объект .__ файл__
idanzalz
4
Вы можете использовать, inspect.getfile(inspect.currentframe())чтобы получить путь к текущему запущенному скрипту.
jbochi
33

Я не понимаю, почему никто не говорит об этом, но для меня самое простое решение - использовать imp.find_module ("modulename") (документация здесь ):

import imp
imp.find_module("os")

Это дает кортеж с путем во второй позиции:

(<open file '/usr/lib/python2.7/os.py', mode 'U' at 0x7f44528d7540>,
'/usr/lib/python2.7/os.py',
('.py', 'U', 1))

Преимущество этого метода перед «проверять» заключается в том, что вам не нужно импортировать модуль, чтобы он работал, и вы можете использовать строку для ввода. Полезно, например, при проверке модулей, вызываемых в другом скрипте.

РЕДАКТИРОВАТЬ :

В python3 importlibмодуль должен делать:

Док о importlib.util.find_spec:

Вернуть спецификацию для указанного модуля.

Сначала проверяется sys.modules, чтобы убедиться, что модуль уже импортирован. Если это так, то sys.modules [имя]. спецификация возвращается. Если для этого параметра установлено значение None, то вызывается ValueError. Если модуль отсутствует в sys.modules, то в sys.meta_path ищется подходящая спецификация со значением 'path', заданным для искателей. Ни один не возвращается, если никакая спецификация не могла быть найдена.

Если имя для субмодуля (содержит точку), родительский модуль автоматически импортируется.

Аргументы name и package работают так же, как importlib.import_module (). Другими словами, относительные имена модулей (с начальными точками) работают.

PlasmaBinturong
источник
2
Imp не рекомендуется в Python 2 (текущая версия 2.7.13). Imp амортизируется в Python 3, начиная с 3.4. importlib должен использоваться вместо Python 3. Мне нравится это решение, потому что оно даже работает, когда фактический импорт не удастся (например, потому что 64-
битный
И ДЕЙСТВИТЕЛЬНО приятно, когда пытаешься найти путь к 64-битному sqlite3 и импорт не удался. Отлично.
rahvin_t
2
importlib.machinery.PathFinder().find_module("os").get_filename()Кратчайшая альтернатива, imp.find_moduleкоторую я нашел в Python3 +. Если кто-то ищет использование importlib.util.find_spec.
Торксед
Этот метод работает без импорта самого модуля, и это здорово, так как я использую его, чтобы выяснить, какая версия модуля импортируется с общего компьютера.
Гауда
19

Это было тривиально.

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

Таким образом, получить каталог для модуля, чтобы уведомить его просто:

os.path.dirname(__file__)
бодрый
источник
14
Почти , но не совсем правильно - файл является не «относительно того , где вы находитесь прямо сейчас»; когда это относительно (это будет только тогда, когда в sys.path есть относительные пути), это относительно того, где вы были, когда модуль был загружен .
Чарльз Даффи
17
import os
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)
Vinoth
источник
1
не работает на моем Linux Python 2.6, так как __file__это просто dir / test.py, abspath использует cwd для завершения пути, который не является желаемым результатом, но если вы импортируете модуль, то m.__file__дает желаемый результат.
Бен Брайант
10
import module
print module.__path__

Пакеты поддерживают еще один специальный атрибут __path__. Это инициализируется, чтобы быть списком, содержащим имя каталога, содержащего пакет, __init__.pyпрежде чем код в этом файле будет выполнен. Эта переменная может быть изменена; это влияет на последующий поиск модулей и подпакетов, содержащихся в пакете.

Хотя эта функция не требуется часто, ее можно использовать для расширения набора модулей, найденных в пакете.

Источник

Лукас Гребликас
источник
9

Утилита командной строки

Вы можете настроить его на утилиту командной строки,

python-which <package name>

введите описание изображения здесь


Создайте /usr/local/bin/python-which

#!/usr/bin/env python

import importlib
import os
import sys

args = sys.argv[1:]
if len(args) > 0:
    module = importlib.import_module(args[0])
    print os.path.dirname(module.__file__)

Сделайте это исполняемым

sudo chmod +x /usr/local/bin/python-which
Йосеф Харуш
источник
7

Поэтому я потратил немало времени, пытаясь сделать это с py2exe. Проблема заключалась в том, чтобы получить базовую папку скрипта, независимо от того, выполнялся ли он как скрипт на python или как исполняемый файл py2exe. Также, чтобы он работал, независимо от того, запускался ли он из текущей папки, другой папки или (это было самым сложным) из системного пути.

В конце концов я использовал этот подход, используя sys.frozen в качестве индикатора работы в py2exe:

import os,sys
if hasattr(sys,'frozen'): # only when running in py2exe this exists
    base = sys.prefix
else: # otherwise this is a regular python script
    base = os.path.dirname(os.path.realpath(__file__))
URI
источник
7

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

>>> import os
>>> os
<module 'os' from 'C:\\Users\\Hassan Ashraf\\AppData\\Local\\Programs\\Python\\Python36-32\\lib\\os.py'>
>>>
Хасан Ашраф
источник
4

Если вы хотите получить корневой путь пакета из любого из его модулей, работает следующее (проверено на Python 3.6):

from . import __path__ as ROOT_PATH
print(ROOT_PATH)

На основной __init__.pyпуть также можно ссылаться, используя __file__вместо этого.

Надеюсь это поможет!

fr_andres
источник
3

Если единственным предостережением использования __file__является то, что текущий относительный каталог пуст (то есть, когда он запускается как скрипт из того же каталога, где находится скрипт), тогда тривиальное решение:

import os.path
mydir = os.path.dirname(__file__) or '.'
full  = os.path.abspath(mydir)
print __file__, mydir, full

И результат:

$ python teste.py 
teste.py . /home/user/work/teste

Трюк в or '.'после dirname()звонка. Он устанавливает каталог как ., что означает текущий каталог и является допустимым каталогом для любой связанной с путем функции.

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

MestreLion
источник
Лучше поменяйте порядок и просто используйте, os.path.dirname(os.path.abspath(__file__))потому что тогда вам не нужно беспокоиться об относительных путях. Еще одна возможность для ошибок.
Сали
3

Я хотел бы внести свой вклад в один общий сценарий (в Python 3) и изучить несколько подходов к нему.

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

Проще говоря, если вы запустите файл сценария со следующим кодом, это не гарантирует, что example.txtфайл будет создан в том же каталоге, где находится файл сценария:

with open('example.txt', 'w'):
    pass

Чтобы исправить этот код, нам нужно получить путь к сценарию и сделать его абсолютным. Чтобы обеспечить абсолютный путь, мы просто используем функцию os.path.realpath () . Чтобы получить путь к сценарию, есть несколько общих функций, которые возвращают различные результаты пути:

  • os.getcwd()
  • os.path.realpath('example.txt')
  • sys.argv[0]
  • __file__

Обе функции os.getcwd () и os.path.realpath () возвращают результаты пути на основе текущего рабочего каталога . Вообще не то, что мы хотим. Первым элементом списка sys.argv является путь к корневому сценарию (сценарию, который вы запускаете) независимо от того, вызываете ли вы этот список в самом корневом сценарии или в любом из его модулей. Это может пригодиться в некоторых ситуациях. __FILE__ переменная содержит путь модуля , из которого она была вызвана.


Следующий код правильно создает файл example.txtв том же каталоге, где находится скрипт:

filedir = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(filedir, 'example.txt')

with open(filepath, 'w'):
    pass
Jeyekomon
источник
3

Если вы хотите узнать абсолютный путь из вашего скрипта, вы можете использовать объект Path :

from pathlib import Path

print(Path().absolute())
print(Path().resolve('.'))
print(Path().cwd())

метод cwd ()

Вернуть новый объект пути, представляющий текущий каталог (как возвращено os.getcwd ())

метод resol ()

Сделайте путь абсолютным, разрешив любые символические ссылки. Новый объект пути возвращается:

Влад Безден
источник
1

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

some_dir/
  maincli.py
  top_package/
    __init__.py
    level_one_a/
      __init__.py
      my_lib_a.py
      level_two/
        __init__.py
        hello_world.py
    level_one_b/
      __init__.py
      my_lib_b.py

Так что выше я должен был вызвать maincli.py из модуля my_lib_a.py, зная, что top_package и maincli.py находятся в одном каталоге. Вот как я могу получить путь к maincli.py:

import sys
import os
import imp


class ConfigurationException(Exception):
    pass


# inside of my_lib_a.py
def get_maincli_path():
    maincli_path = os.path.abspath(imp.find_module('maincli')[1])
    # top_package = __package__.split('.')[0]
    # mod = sys.modules.get(top_package)
    # modfile = mod.__file__
    # pkg_in_dir = os.path.dirname(os.path.dirname(os.path.abspath(modfile)))
    # maincli_path = os.path.join(pkg_in_dir, 'maincli.py')

    if not os.path.exists(maincli_path):
        err_msg = 'This script expects that "maincli.py" be installed to the '\
        'same directory: "{0}"'.format(maincli_path)
        raise ConfigurationException(err_msg)

    return maincli_path

Основываясь на публикации PlasmaBinturong, я изменил код.

Аль Конрад
источник
1

Если вы хотите сделать это динамически в «программе», попробуйте этот код:
Суть в том, что вы можете не знать точное имя модуля, чтобы «жестко его кодировать». Он может быть выбран из списка или может не работать в данный момент для использования __file__.

(Я знаю, это не будет работать в Python 3)

global modpath
modname = 'os' #This can be any module name on the fly
#Create a file called "modname.py"
f=open("modname.py","w")
f.write("import "+modname+"\n")
f.write("modpath = "+modname+"\n")
f.close()
#Call the file with execfile()
execfile('modname.py')
print modpath
<module 'os' from 'C:\Python27\lib\os.pyc'>

Я пытался избавиться от «глобальной» проблемы, но нашел случаи, когда она не работала, я думаю, что «execfile ()» можно эмулировать в Python 3, так как это в программе, его можно легко поместить в метод или модуль для повторное использование.

Робин Рэндалл
источник
0

Если вы установили его с помощью pip, «pip show» прекрасно работает («Местоположение»)

$ pip show deteron2

Name: detectron2
Version: 0.1
Summary: Detectron2 is FAIR next-generation research platform for object detection and segmentation.
Home-page: https://github.com/facebookresearch/detectron2
Author: FAIR
Author-email: None
License: UNKNOWN
Location: /home/ubuntu/anaconda3/envs/pytorch_p36/lib/python3.6/site-packages
Requires: yacs, tabulate, tqdm, pydot, tensorboard, Pillow, termcolor, future, cloudpickle, matplotlib, fvcore
Javi
источник
0

Вот быстрый скрипт bash на случай, если он кому-нибудь пригодится. Я просто хочу иметь возможность установить переменную окружения, чтобы я мог pushdкод.

#!/bin/bash
module=${1:?"I need a module name"}

python << EOI
import $module
import os
print os.path.dirname($module.__file__)
EOI

Пример оболочки:

[root@sri-4625-0004 ~]# export LXML=$(get_python_path.sh lxml)
[root@sri-4625-0004 ~]# echo $LXML
/usr/lib64/python2.7/site-packages/lxml
[root@sri-4625-0004 ~]#
землеройка
источник