Определить имя функции из этой функции (без использования трассировки)
В 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))