Как я могу получить доступ к текущему исполняющему модулю или имени класса в Python? - PullRequest
36 голосов
/ 02 марта 2009

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

foo.py:

def f():
    print __name__

bar.py:

from foo import f

def b(): f()

Это, очевидно, не работает, так как __name__ - это имя модуля, который содержит функцию. То, к чему я хотел бы получить доступ внутри модуля foo, - это имя текущего исполняющего модуля, который использует foo. Таким образом, в приведенном выше случае это будет bar, но если какой-либо другой импортированный модуль foo, я бы хотел, чтобы foo динамически имел доступ к имени этого модуля.

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

Редактировать: Вот как я пытаюсь использовать это. У меня есть два разных приложения Django, которые оба должны регистрировать ошибки в файл. Допустим, они называются «AppOne» и «AppTwo». У меня также есть место, куда я хотел бы зайти в эти файлы: "/home/hare/app_logs". В каждом приложении в любой данный момент я хотел бы иметь возможность импортировать мой модуль логгера и вызывать функцию log, которая записывает строку журнала в файл. Однако я хотел бы создать каталог под app_logs, который является именем текущего приложения («AppOne» или «AppTwo»), чтобы файлы журналов каждого приложения помещались в соответствующие каталоги журналов.

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

Ответы [ 11 ]

46 голосов
/ 02 марта 2009

Из комментария - не вопрос.

Мне просто любопытно посмотреть, возможно ли то, что я пытаюсь сделать.

Ответ «возможно» всегда «да». Всегда. Если ваш вопрос не касается путешествий во времени, антигравитации или вечного движения.

Поскольку ответом всегда является «да», ваш вопрос некорректен. Реальный вопрос заключается в том, "как можно, чтобы мой модуль регистрации знал имя клиента?" или что-то в этом роде.

Ответ: «Примите это как параметр». Не возиться с проверкой или поиском таинственных глобалов или других уловок.

Просто следуйте шаблону logging.getLogger () и используйте явно именованные регистраторы. Распространенной идиомой является следующий

logger= logging.getLogger( __name__ )

Отлично справляется практически со всеми именами журналов.

30 голосов
/ 18 октября 2011

Это должно работать для ссылки на текущий модуль:

import sys
sys.modules[__name__]
19 голосов
/ 02 марта 2009

«Выполняемый в данный момент модуль» - это явно foo, поскольку именно в нем содержится функция, запущенная в данный момент - я думаю, что лучшим описанием того, что вы хотите, является модуль непосредственного вызова foo (который сам может быть foo, если вы вызываете af () из функции в foo, вызываемой функцией в баре. То, как далеко вы хотите пойти, зависит от того, для чего вы хотите.

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

def f():
    caller = sys._getframe(1)  # Obtain calling frame
    print "Called from module", caller.f_globals['__name__']

[Edit] : На самом деле, использование модуля inspect , как предложено выше, вероятно, является более чистым способом получения кадра стека. Эквивалентный код:

def f():
    caller = inspect.currentframe().f_back
    print "Called from module", caller.f_globals['__name__']

(sys._getframe задокументирован как предназначенный для внутреннего использования - модуль inspect является более надежным API)

11 голосов
/ 02 марта 2009

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

10 голосов
/ 02 марта 2009

__file__ - путь к текущему модулю, в котором выполняется вызов.

4 голосов
/ 20 декабря 2016

Чтобы получить ссылку на модуль "__main__", когда в другом:

import sys
sys.modules['__main__']

Чтобы затем получить путь к файлу модуля, который включает его имя:

sys.modules['__main__'].__file__

Если внутри модуля "__main__", просто используйте: __file__

Чтобы получить только имя файла из пути к файлу:

import os
os.path.basename(file_path)

Чтобы отделить имя файла от его расширения:

file_name.split(".")[0]

Чтобы получить имя экземпляра класса:

instance.__class__.__name__

Чтобы получить название класса:

class.__name__
3 голосов
/ 09 января 2017

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

Только для имени файла используйте __file__.split(os.path.sep)[-1].

Для полного пути используйте os.path.abspath(__file__).

Демо-версия:

/tmp $ cat f.py
from pprint import pprint
import os
import sys

pprint({
    'sys.modules[__name__]': sys.modules[__name__],
    '__file__': __file__,
    '__file__.split(os.path.sep)[-1]': __file__.split(os.path.sep)[-1],
    'os.path.abspath(__file__)': os.path.abspath(__file__),
})

/tmp $ cat i.py
import f

Результаты:

## on *Nix ##

/tmp $ python3 f.py
{'sys.modules[__name__]': <module '__main__' from 'f.py'>,
 '__file__': 'f.py',
 '__file__.split(os.path.sep)[-1]': 'f.py',
 'os.path.abspath(__file__)': '/tmp/f.py'}

/tmp $ python3 i.py
{'sys.modules[__name__]': <module 'f' from '/tmp/f.pyc'>,
 '__file__': '/tmp/f.pyc',
 '__file__.split(os.path.sep)[-1]': 'f.pyc',
 'os.path.abspath(__file__)': '/tmp/f.pyc'}

## on Windows ##

PS C:\tmp> python3.exe f.py
{'sys.modules[__name__]': <module '__main__' from 'f.py'>,
 '__file__': 'f.py',
 '__file__.split(os.path.sep)[-1]': 'f.py',
 'os.path.abspath(__file__)': 'C:\\tools\\cygwin\\tmp\\f.py'}

PS C:\tmp> python3.exe i.py
{'sys.modules[__name__]': <module 'f' from 'C:\\tools\\cygwin\\tmp\\f.py'>,
 '__file__': 'C:\\tools\\cygwin\\tmp\\f.py',
 '__file__.split(os.path.sep)[-1]': 'f.py',
 'os.path.abspath(__file__)': 'C:\\tools\\cygwin\\tmp\\f.py'}

Если вы хотите убрать «.py» с конца, вы можете сделать это легко. (Но не забывайте, что вместо этого вы можете запустить .pyc.)

3 голосов
/ 02 марта 2009

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

2 голосов
/ 02 марта 2009

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

1 голос
/ 03 ноября 2017

Если вы хотите только имя файла:

file_name = __file__.split("/")[len(__file__.split("/"))-1]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...