Отримайте __name__ модуля функції виклику в Python


96

Припустимо, 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__
Шрідхар Ратнакумар

Відповіді:


123

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

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

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

Докладніше дивіться документи:

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

Крім того, Дуг Хеллманн має гарний перелік модуля перевірки у своїй серії PyMOTW:

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

EDIT: Ось якийсь код, який робить те, що ви хочете, я думаю:

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.
Шрідхар Ратнакумар

6
Будьте в курсі, що це буде дивно взаємодіяти з гачками імпорту, не працюватиме на ironpython, і може поводитися дивно на jython. Найкраще, якщо ви можете уникнути подібної магії.
Гліф

2
Також зауважте, що збереження посилання на кадр стека може запобігти коректній роботі Python GC. Дивіться попередження тут: docs.python.org/library/inspect.html#the-interpreter-stack
Kamil Kisiel

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

Також зауважте, що ця логіка не спрацьовує належним чином, коли ви компілюєте код python в exe за допомогою pyinstaller.
panofish

19

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

>>> 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()(btw, який повертає відображення кадру для кожного потоку).
hooblei

Дякую, хулі, я ще не перевіряв його, але здається дуже корисним у багатопотокових ситуаціях.
Луї 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.