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

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

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

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

18 ответов


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

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

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


import inspect

def foo():
   print(inspect.stack()[0][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

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

import inspect

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

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

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


functionNameAsString = sys._getframe().f_code.co_name

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


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

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

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

myself()

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

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()

выводит

заглушки


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

вот мое мнение:

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's caller was:", currentFuncName(1)    


def invokeTest():
    testFunction()


invokeTest()

# end of file

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


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


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

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

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 код выводит

Привет, я-фу, папа

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

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


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

вот подход будущего доказательства.

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

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

поэтому я думаю, что это хорошо играет с будущими версиями 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))

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

myFunctionsHere()

выход:

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

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


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

def my_function(name=None):
    return name

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

>>> get_function_name(my_function)
'my_function'

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

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 украшенного метода у вас есть доступ к себе.__имени__ в качестве текущего имени функции.


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

import sys

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

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

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

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