Определить имя функции из этой функции (без использования трассировки)

В Python, не используя модуль traceback, существует ли способ определить имя функции из этой функции?

Скажем, у меня есть модуль foo с функциональной панелью. Когда вы выполняете foo.bar(), есть ли способ, чтобы бар знал имя бара? Или еще лучше, foo.bar name?

#foo.py  
def bar():
    print "my name is", __myname__ # <== how do I calculate this at runtime?

Ответ 1

Python не имеет функции доступа к функции или ее имени внутри самой функции. Он был предложен, но отклонен. Если вы не хотите играть со стеком самостоятельно, вы должны либо использовать "bar" либо bar.__name__ зависимости от контекста.

Данное уведомление об отказе:

Этот PEP отклоняется. Непонятно, как это должно быть реализовано или какая должна быть точная семантика в крайних случаях, и недостаточно данных важных случаев использования. ответ был в лучшем случае теплым.

Ответ 2

import inspect

def foo():
   print(inspect.stack()[0][3])

Ответ 3

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

from __future__ import print_function
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

Ответ 4

Вы можете получить имя, которое было определено с помощью того подхода, который показывает @Andreas Jung, но это может быть не имя, с которым вызывалась функция:

import inspect

def Foo():
   print inspect.stack()[0][3]

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

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

Ответ 5

functionNameAsString = sys._getframe().f_code.co_name

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

Ответ 6

Я думаю, что inspect это лучший способ сделать это. Например:

import inspect
def bar():
    print("My name is", inspect.stack()[0][3])

Ответ 7

Я держу эту удобную утилиту рядом:

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

Использование:

myself()

Ответ 8

Я нашел оболочку, которая будет писать имя функции

from functools import wraps

def tmp_wrap(func):
    @wraps(func)
    def tmp(*args, **kwargs):
        print func.__name__
        return func(*args, **kwargs)
    return tmp

@tmp_wrap
def my_funky_name():
    print "STUB"

my_funky_name()

Откроется

my_funky_name

STUB

Ответ 9

Это фактически вытекает из других ответов на вопрос.

Вот мой прием:

import sys

# for current func name, specify 0 or no argument.
# for name of caller of current func, specify 1.
# for name of caller of caller of current func, specify 2. etc.
currentFuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name


def testFunction():
    print "You are in function:", currentFuncName()
    print "This function caller was:", currentFuncName(1)    


def invokeTest():
    testFunction()


invokeTest()

# end of file

Вероятное преимущество этой версии в использовании метода inspect.stack() заключается в том, что она должна быть в тысячи раз быстрее (см. сообщение Alex Melihoff и тайминги относительно использования sys._getframe() по сравнению с использованием inspect.stack()]. ​​

Ответ 10

print(inspect.stack()[0].function), похоже, тоже работает (Python 3.5).

Ответ 11

import inspect

def whoami():
    return inspect.stack()[1][3]

def whosdaddy():
    return inspect.stack()[2][3]

def foo():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())
    bar()

def bar():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())

foo()
bar()

В IDE код выводит

Привет, я foo, папа

Привет, я бар, папа foo

Привет, я баре, папа

Ответ 12

Вот перспективный подход.

Объединение предложений @CamHart и @Yuval с принятым ответом @RoshOxymoron имеет преимущество:

  • _hidden и потенциально устаревшие методы
  • индексирование в стек (который может быть переупорядочен в будущих питонах)

Итак, я думаю, что это хорошо сочетается с будущими версиями python (проверено на 2.7.3 и 3.3.2):

from __future__ import print_function
import inspect

def bar():
    print("my name is '{}'".format(inspect.currentframe().f_code.co_name))

Ответ 13

import sys

def func_name():
    """
    :return: name of caller
    """
    return sys._getframe(1).f_code.co_name

class A(object):
    def __init__(self):
        pass
    def test_class_func_name(self):
        print(func_name())

def test_func_name():
    print(func_name())

Тест:

a = A()
a.test_class_func_name()
test_func_name()

Вывод:

test_class_func_name
test_func_name

Ответ 14

Вы можете использовать декоратор:

def my_function(name=None):
    return name

def get_function_name(function):
    return function(name=function.__name__)

>>> get_function_name(my_function)
'my_function'

Ответ 15

Я сделал то, что сказал CamHart:

import sys
def myFunctionsHere():
    print(sys._getframe().f_code.co_name)

myFunctionsHere()

Вывод:

C:\Python\Python36\python.exe C:/Python/GetFunctionsNames/TestFunctionsNames.py myFunctionsHere

Процесс завершен кодом выхода 0

Ответ 16

Я использую свой подход, используемый для вызова супер с безопасностью внутри сценария множественного наследования (я положил весь код)

def safe_super(_class, _inst):
    """safe super call"""
    try:
        return getattr(super(_class, _inst), _inst.__fname__)
    except:
        return (lambda *x,**kx: None)


def with_name(function):
    def wrap(self, *args, **kwargs):
        self.__fname__ = function.__name__
        return function(self, *args, **kwargs)
return wrap

использование образца:

class A(object):

    def __init__():
        super(A, self).__init__()

    @with_name
    def test(self):
        print 'called from A\n'
        safe_super(A, self)()

class B(object):

    def __init__():
        super(B, self).__init__()

    @with_name
    def test(self):
        print 'called from B\n'
        safe_super(B, self)()

class C(A, B):

    def __init__():
        super(C, self).__init__()

    @with_name
    def test(self):
        print 'called from C\n'
        safe_super(C, self)()

тестирование:

a = C()
a.test()

выход:

called from C
called from A
called from B

Внутри каждого метода украшенного @with_name у вас есть доступ к self.__ fname__ в качестве текущего имени функции.

Ответ 17

Используйте это (на основе ответа #Ron Davis):

import sys

def thisFunctionName():
    """Returns a string with the name of the function it called from"""
    return sys._getframe(1).f_code.co_name

Ответ 18

Недавно я попытался использовать приведенные выше ответы для доступа к docstring функции из контекста этой функции, но поскольку вышеупомянутые вопросы возвращали только строку имени, она не работала.

К счастью, я нашел простое решение. Если мне нравится, вы хотите обратиться к функции, а не просто получить строку, представляющую имя, вы можете применить eval() к строке имени функции.

import sys
def foo():
    """foo docstring"""
    print(eval(sys._getframe().f_code.co_name).__doc__)

Ответ 19

Я предлагаю не полагаться на элементы стека. Если кто-то использует ваш код в разных контекстах (например, интерпретатор Python), ваш стек изменится и нарушит ваш индекс ([0] [3]).

Я предлагаю вам что-то подобное:

class MyClass:

    def __init__(self):
        self.function_name = None

    def _Handler(self, **kwargs):
        print('Calling function {} with parameters {}'.format(self.function_name, kwargs))
        self.function_name = None

    def __getattr__(self, attr):
        self.function_name = attr
        return self._Handler


mc = MyClass()
mc.test(FirstParam='my', SecondParam='test')
mc.foobar(OtherParam='foobar')

Ответ 20

Я не уверен, почему люди делают это сложным:

import sys 
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))