Як отримати шлях модуля?


754

Я хочу виявити, чи змінився модуль. Тепер використовувати 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

Відповіді:


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

Щоб отримати каталог модуля.


54
це відповідає на те, як отримати шлях модуля, який ви імпортуєте, але не про модуль / скрипт, в якому ви перебуваєте (для запущеного сценарію __file__це не повний шлях, він відносний). Для файлу, в якому я перебуваю, мені довелося імпортувати інший модуль із тієї ж каталоги і робити, як показано тут. Хтось знає більш зручний спосіб?
Бен Брайант

5
@hbdgaf майже впевнений, що немає такого вбудованогоself.__file__
Dan Passaro

26
@BenBryant @hbdgaf os.path.dirname(__file__)для мене добре працює і повертає абс-путь до каталогу модуля.
Niccolò

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

У inspectpython є модуль.

Офіційна документація

Модуль перевірки надає кілька корисних функцій, які допомагають отримувати інформацію про живі об'єкти, такі як модулі, класи, методи, функції, відстеження, рамкові об'єкти та об'єкти коду. Наприклад, це може допомогти вам вивчити вміст класу, отримати вихідний код методу, витягнути та відформатувати список аргументів для функції або отримати всю інформацію, необхідну для відображення детального простеження.

Приклад:

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

Наприклад, скажіть, що у вас є два файли (обидва вони знаходяться на вашому ПІТОНПАТ):

#/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 

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


5
У цьому випадку ви можете використовувати sys.argv [0] замість файлу .
Джимофій

4
Це застереження щодо конкретної версії? У 2.6 і 2.7 я успішно покладаюся на файл , який працює у файлі, коли ім'я __ == '__ main '. Єдиний випадок відмови, який я бачив, - це з " файлом друку" python -c . Додам, що інколи файл може бути "<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: 

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

# 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 (). Наприклад, створення файлу symlink 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)

слухняно повертає абсолютний шлях. Однак для пошуку шляху поточного сценарію виконання я не бачив способу його використання.


1
inspect.getfile (os) - це те саме, що os .__ file__ з коду: def getfile (object): "" "Визначте, у якому джерелі чи складеному файлі визначено об'єкт." "" if ismodule (object): if hasattr (об’єкт, ' файл '): return object .__ file__
idanzalz

4
Ви можете використовувати inspect.getfile(inspect.currentframe())для отримання шляху поточного запущеного сценарію.
jbochi

33

Я не розумію, чому про це ніхто не говорить, але для мене найпростішим рішенням є використання imp.find_module ("ім'я модуля") ( тут документація ):

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', що задається шукачам. Жоден не повертається, якщо жодної специфікації не вдалося знайти.

Якщо назва призначена для підмодуля (містить крапку), батьківський модуль автоматично імпортується.

Аргументи імені та пакету працюють так само, як importlib.import_module (). Іншими словами, відносні назви модулів (з провідними точками) працюють.


2
imp НЕ знецінюється в python 2 (поточна версія 2.7.13). імп знецінюється в python 3 з 3.4. замість importlib використовуватиметься в python 3. Мені подобається це рішення, тому що воно працює навіть тоді, коли фактичний імпорт вийде з ладу (наприклад, тому що 64-бітний модуль для 32-
бітового

І дійсно приємно, коли намагається знайти шлях 64-бітного sqlite3 та імпорт не вдається. Ідеально.
rahvin_t

2
importlib.machinery.PathFinder().find_module("os").get_filename()Найкоротшу альтернативу imp.find_moduleя знайшов у Python3 +. Якщо хтось шукає використання importlib.util.find_spec.
Тортується

Цей метод працює без імпорту фактичного модуля, що чудово, тому що я використовую це для з'ясування, яка версія модуля імпортується із спільного комп'ютера.
Gouda

19

Це було банально.

Кожен модуль має __file__змінну, яка показує його відносний шлях від того, де ви зараз перебуваєте.

Тому отримати каталог для модуля, щоб повідомити його, просто:

os.path.dirname(__file__)

14
Майже , але не зовсім правильно - файл є НЕ «щодо того , де ви знаходитесь прямо зараз»; коли це відносно (що буде лише тоді, коли в sys.path є відносні шляхи), це відносно того, де ви були, коли модуль був завантажений .
Чарльз Даффі

17
import os
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)

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

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__замість цього.

Сподіваюся, це допомагає!


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()дзвінка. Він встановлює dir як ., що означає поточний каталог і є дійсним каталогом для будь-якої функції, що стосується шляху.

Таким чином, використання abspath()справді не потрібне. Але якщо ви все-таки використовуєте його, хитрість не потрібна: abspath()приймає порожні контури та правильно інтерпретує їх як поточний каталог.


Краще поміняйте замовлення та просто використовуйте, os.path.dirname(os.path.abspath(__file__))оскільки тоді взагалі не потрібно турбуватися про відносні шляхи. Одна менша можливість для помилок.
szali

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

3

Якщо ви хочете знати абсолютний шлях зі свого сценарію, ви можете використовувати об'єкт Path :

from pathlib import Path

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

метод cwd ()

Повернути новий об'єкт шляху, що представляє поточну директорію (як повернув os.getcwd ())

метод вирішення ()

Зробіть шлях абсолютним, вирішивши будь-які посилання. Повертається новий об'єкт шляху:


1

Зсередини модулів пакету python мені довелося посилатися на файл, який знаходився в тому ж каталозі, що і пакунок. Вих.

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" чудово працює ("Location")

$ pip show detectron2

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

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 ~]#
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.