7

Определение имени функции изнутри самой функции

1

Существует ли способ получить название функции изнутри самой функции?

def foo():
    print("my name is", __myname__)  # <== как мне узнать это во время выполнения?

В приведенном выше примере тело функции foo должно каким-то образом получить имя функции "foo" без жесткого кодирования. Ожидаемый вывод будет таким:

>>> foo()
my name is foo

5 ответ(ов)

6

Ваш код использует модуль inspect для получения информации о стеке вызовов функций. Давайте разберем, как он работает.

В функции foo вы вызываете inspect.stack(), которая возвращает список объектов StackEntry, представляющих текущий вызов стека. Каждый элемент списка содержит информацию о текущем контексте выполнения, включая имя функции, файл и номер строки.

В вашем случае:

  1. inspect.stack()[0][3] возвращает имя функции, которая в данный момент выполняется, то есть foo.
  2. inspect.stack()[1][3] возвращает имя функции или модуля, вызвавшего foo. Если вы вызываете foo из модуля (например, из основного блока программы), это будет <module_caller_of_foo> (или просто имя модуля).

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

foo  # имя текущей функции
<module_caller_of_foo>  # имя вызывающего модуля

Важно учитывать, что если бы вы вызывали foo из другой функции, то имя этой функции было бы выведено вместо <module_caller_of_foo>.

2

Есть несколько способов получить одно и то же значение:

import sys
import inspect

def what_is_my_name():
    print(inspect.stack()[0][0].f_code.co_name)
    print(inspect.stack()[0][3])
    print(inspect.currentframe().f_code.co_name)
    print(sys._getframe().f_code.co_name)

Обратите внимание, что вызовы inspect.stack значительно медленнее, чем альтернативы:

$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
1000 loops, best of 3: 499 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
1000 loops, best of 3: 497 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
10000000 loops, best of 3: 0.1 usec per loop
$ python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
10000000 loops, best of 3: 0.135 usec per loop

Обновление 08/2021 (оригинальный пост был написан для Python 2.7):

Python 3.9.1 (default, Dec 11 2020, 14:32:07)
[GCC 7.3.0] :: Anaconda, Inc. на linux

python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
500 loops, best of 5: 390 usec per loop
python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
500 loops, best of 5: 398 usec per loop
python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
2000000 loops, best of 5: 176 nsec per loop
python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
5000000 loops, best of 5: 62.8 nsec per loop

Таким образом, если вам нужно получить имя текущей функции, рассчитывайте на использование inspect.currentframe() или sys._getframe(), так как они значительно быстрее.

0

Вы можете получить имя текущей функции, используя следующий код:

functionNameAsString = sys._getframe().f_code.co_name

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

0

Это на самом деле является производным из других ответов на вопрос.

Вот мой подход:

import sys

# Для получения имени текущей функции укажите 0 или не передавайте аргумент.
# Для имени вызывающей функции текущей функции укажите 1.
# Для имени вызывающей функции вызывающей функции текущей функции укажите 2 и так далее.
currentFuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name

def testFunction():
    print("Вы находитесь в функции:", currentFuncName())
    print("Эта функция была вызвана из:", currentFuncName(1))    

def invokeTest():
    testFunction()

invokeTest()

# конец файла

Вероятное преимущество данного подхода по сравнению с использованием inspect.stack() заключается в том, что он должен работать в тысячи раз быстрее [см. пост Алекса Мелихова и замеры относительно использования sys._getframe() по сравнению с inspect.stack()].

0

Вы можете использовать следующий полезный инструмент для получения имени текущей функции:

import inspect
myself = lambda: inspect.stack()[1][3]

Пример использования:

myself()

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь