Получить __name__ модуля вызывающей функции в Python


98

Предположим, myapp/foo.pyсодержит:

def info(msg):
    caller_name = ????
    print '[%s] %s' % (caller_name, msg)

И myapp/bar.pyсодержит:

import foo
foo.info('Hello') # => [myapp.bar] Hello

В этом случае я хочу caller_nameустановить __name__атрибут модуля вызывающих функций (которым является myapp.foo). Как это может быть сделано?


Предположим, что какой-то другой сценарий точки входа вызывает bar.py .. и, следовательно, caller_nameне может быть__main__
Шридхар Ратнакумар

Ответы:


125

Проверьте модуль проверки:

inspect.stack() вернет информацию о стеке.

Внутри функции inspect.stack()[1]вернет стек вашего вызывающего. Оттуда вы можете получить дополнительную информацию об имени вызывающей функции, модуле и т. Д.

Подробнее см. В документации:

http://docs.python.org/library/inspect.html

Кроме того, Дуг Хеллманн написал хорошую статью о модуле inspect в своей серии PyMOTW:

http://pymotw.com/2/inspect/index.html#module-inspect

РЕДАКТИРОВАТЬ: Вот какой-то код, который делает то, что вы хотите, я думаю:

import inspect 

def info(msg):
    frm = inspect.stack()[1]
    mod = inspect.getmodule(frm[0])
    print '[%s] %s' % (mod.__name__, msg)

1
Итак, как получить __name__атрибут этого модуля с помощью inspectмодуля? Например, как мне вернуться myapp.foo(не myapp/foo.py) в мой пример выше? Я уже пробовал использовать модуль проверки перед публикацией в SO.
Шридхар Ратнакумар 08

6
Имейте в виду, что это будет странным образом взаимодействовать с обработчиками импорта, не будет работать на ironpython и может вести себя неожиданным образом на jython. Лучше всего, если вы сможете избежать подобной магии.
Glyph

2
Также обратите внимание, что сохранение ссылки на кадр стека может помешать правильной работе сборщика мусора Python. См. Предупреждение здесь: docs.python.org/library/inspect.html#the-interpreter-stack
Камил Кисиэль,

6
Обратите внимание, что если функция вызывающего абонента оформлена (@ ...), вам потребуется доступ inspect.stack()[2]для реального вызывающего абонента.
Амир Али Акбари

Также обратите внимание, что эта логика не работает должным образом, когда вы компилируете свой код python в exe с помощью pyinstaller.
panofish

19

Столкнувшись с аналогичной проблемой, я обнаружил, что sys._current_frames () из модуля sys содержит интересную информацию, которая может помочь вам без необходимости импорта inspect, по крайней мере, в определенных случаях использования.

>>> sys._current_frames()
{4052: <frame object at 0x03200C98>}

Затем вы можете "подняться" с помощью f_back:

>>> f = sys._current_frames().values()[0]
>>> # for python3: f = list(sys._current_frames().values())[0]

>>> print f.f_back.f_globals['__file__']
'/base/data/home/apps/apricot/1.6456165165151/caller.py'

>>> print f.f_back.f_globals['__name__']
'__main__'

В качестве имени файла вы также можете использовать f.f_back.f_code.co_filename, как было предложено Марком Родди выше. Я не уверен в ограничениях и предостережениях этого метода (скорее всего, проблема с несколькими потоками), но я намерен использовать его в своем случае.


2
примечание: код inspect.stack ОТКАЗЫВАЕТСЯ после компиляции в exe с использованием pyinstaller, но при использовании sys._current_frames РАБОТАЕТ ОТЛИЧНО ... так что это предпочтительный метод для меня.
panofish

7
Я думаю, что проще получить предыдущий кадр sys._getframe(1)вместо вызова sys._current_frames()(кстати, который возвращает отображение кадра для каждого потока).
hooblei

Спасибо, hooblei, я еще не тестировал, но кажется очень полезным для многопоточных ситуаций.
Louis LC

Я предпочитаю использовать inspect.currentframe()вместо sys._current_frames().values()[0].
Аран-Фей

3

Я не рекомендую этого делать, но вы можете достичь своей цели следующим методом:

def caller_name():
    frame=inspect.currentframe()
    frame=frame.f_back.f_back
    code=frame.f_code
    return code.co_filename

Затем обновите существующий метод следующим образом:

def info(msg):
    caller = caller_name()
    print '[%s] %s' % (caller, msg)

7
Имя файла не то же самое, что__name__
Шридхар Ратнакумар
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.