Как в Python получить имя функции в виде строки без вызова функции?
def my_function():
pass
print get_function_name_as_string(my_function) # my_function is not in quotes
должен выводить "my_function".
Доступна ли такая функция в Python? Если нет, есть идеи о том, как реализовать get_function_name_as_string на Python?






my_function.func_name
Есть и другие забавные свойства функций. Введите dir(func_name), чтобы перечислить их. func_name.func_code.co_code - это скомпилированная функция, хранящаяся в виде строки.
import dis
dis.dis(my_function)
отобразит код в удобочитаемом формате почти. :)
Имена с двойным подчеркиванием традиционно считаются личными, хотя никогда не применяются. Поэтому лучше использовать func_name, даже если они могут быть одинаковыми.
Сэм: имена частные, __names особенные, есть концептуальная разница.
Если кто-то озадачен предыдущим ответом Мэтью, система комментариев интерпретировала некоторые двойные подчеркивания как код для жирного шрифта. Сообщение должно быть экранировано обратной кавычкой: __names__ - частные, __names - специальные.
@MatthewTrevor, у вас есть ссылка для получения дополнительной информации / подробностей?
На самом деле, я думаю, что правильным является то, что _names являются частными (одинарное подчеркивание перед, просто соглашение), __names__ являются особенными (двойные подчеркивания перед и после). Не уверен, имеет ли двойное подчеркивание перед ним какое-либо значение, формально или как соглашение.
@MestreLion Это называется искажение имени.
@ nyuszika7h: правда, я забыл про фальсификацию имен. Поэтому я думаю, что лучший совет: используйте соглашение_name для «обычных» частных участников, поскольку __name может быть изменен в именах, а __name__ может быть «особенным» в зависимости от name, если только они не являются тем, что вы хотите, и вы знаете, что вы делает.
func_name больше не существует в python3, поэтому вам нужно использовать func.__name__, если вам нужна совместимость.
my_function.__name__
Использование __name__ является предпочтительным методом, поскольку он применяется единообразно. В отличие от func_name, он также работает со встроенными функциями:
>>> import time
>>> time.time.func_name
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'builtin_function_or_method' object has no attribute 'func_name'
>>> time.time.__name__
'time'
Также двойное подчеркивание указывает читателю, что это особый атрибут. В качестве бонуса классы и модули также имеют атрибут __name__, поэтому вам нужно запомнить только одно специальное имя.
Потому что в некоторых случаях вы получаете некоторый объект функции в качестве аргумента вашей функции, и вам нужно отображать / хранить / управлять именем этой функции. Возможно, вы создаете документацию, текст справки, историю действий и т. д. Так что нет, вы не всегда жестко кодируете имя функции.
@mgargiel: Я хотел сказать следующее: предположим, у вас есть класс, который определяет 100 методов, где все методы являются только оболочками, которые вызывают частный метод, передавая имя самой оболочки. Примерно так: def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). У вас есть другой вариант: def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). Так что ответ Альберта Вонпуппа кажется мне лучше.
@RichardGomes Один ответ подходит для получения имени внутри самой функции, другой подходит для получения его из ссылки на функцию. Написанный вопрос ОП указывает на последнее.
@RichardGomes На самом деле я пришел к этому вопросу в поисках решения этой проблемы. Проблема, которую я пытаюсь решить, - создать декоратор, который можно использовать для регистрации всех моих звонков.
Функции @RichardGomes - это первоклассные объекты, с ними может быть связано не только имя. Это не обязательно так, что f.__name__ == 'f'.
Я не вижу, чтобы это решение помогло, поскольку большинство людей будут использовать его только в том случае, если им нужно что-то общее, которое вы можете вызывать как метод много раз. Такой метод подойдет, если вы хотите отслеживать порядок выполнения функций. В приведенном выше решении говорится, что моя функция - foo, и вызывается foo._name__, а затем устанавливается имя переменной. С таким же успехом вы можете просто установить строку со значением foo. Вызов _getframe (), описанный ниже как другое решение, работает внутри нескольких функций внутри одного класса! именно то, что мне нужно.
@EamonnKenny Я написал декоратор, который принимает на вход универсальную функцию. В некоторых случаях возникает ошибка, если метод не поддерживается. Мое исключение сообщает имя неподдерживаемого метода в сообщении об исключении. __name__ решает эту проблему.
@RichardGomes Мой вариант использования - отладка функции функционального программирования. У меня есть функция, которая принимает функцию в качестве аргумента, и я хочу зарегистрировать имя переданной функции.
Чтобы получить имя текущей функции или метода изнутри, рассмотрите:
import inspect
this_function_name = inspect.currentframe().f_code.co_name
sys._getframe также работает вместо inspect.currentframe, хотя последний избегает доступа к частной функции.
Чтобы вместо этого получить имя вызывающей функции, рассмотрите f_back как inspect.currentframe().f_back.f_code.co_name.
Если также используется mypy, он может жаловаться, что:
error: Item "None" of "Optional[FrameType]" has no attribute "f_code"
Чтобы подавить указанную выше ошибку, примите во внимание:
import inspect
import types
from typing import cast
this_function_name = cast(types.FrameType, inspect.currentframe()).f_code.co_name
+1: Это тот ответ, который я хотел бы увидеть. Другие ответы предполагают, что вызывающий уже знает имя функции, что является нонсенсом в контексте этого вопроса.
Ричард: нет, это не так. ВЫ предполагаете, что вызываете имя или func_name для своей функции непосредственно в той же области, в которой она была определена, что очень часто не так. Имейте в виду, что функции являются объектами - они могут быть переданы в качестве аргументов другим функциям, сохранены в списках / dicts для последующего поиска или выполнения и т. д.
@paulus_almighty, копаться в фреймах стека мне не кажется абстрактным! На самом деле это своего рода противоположность абстрактному. См. Примечание детали реализации в документации. Не все реализации Python будут включать sys._getframe - он напрямую связан с внутренними компонентами CPython.
Это работает только внутри функции, но в вопросе указано, что функция не должна вызываться.
Если вы хотите обернуть свою функцию чем-то более удобным и легко запоминающимся, вам нужно получить кадр 1, например sys._getframe(1).f_code.co_name, поэтому вы можете определить функцию, например get_func_name(), и ожидать получения желаемого имени функции, которая вызвала.
@Igor, sys._getframe(), получает n фреймов, sys._getframe(1) берет фрейм "одного глубокого уровня" из списка / стека. Итак, если вы заключите вызов между классом-> функцией-> функцией, вам нужно будет передать количество функций / классов, которые вызвали его. Это немного странно, но просто. В одиночном и прямом вызове выполнение sys._getframe() означает sys._getframe(0), и, поскольку об этом явно не сказано, вы не можете магическим образом угадать, что вы можете передавать числа. Пожалуйста.
Не гарантируется, что sys._getframe() будет доступен во всех реализациях Python (см. исх.), вы можете использовать модуль traceback, чтобы сделать то же самое, например.
import traceback
def who_am_i():
stack = traceback.extract_stack()
filename, codeline, funcName, text = stack[-2]
return funcName
Вызов stack[-1] вернет сведения о текущем процессе.
Извините, если sys._getframe() не определен, значит, traceback.extract_stack также не работает. Последний обеспечивает грубую надстройку функциональности первого; вы не можете ожидать увидеть одно без другого. Фактически, в IronPython 2.7 extract_stack() всегда возвращает []. -1
Эта функция вернет имя функции вызывающего.
def func_name():
import traceback
return traceback.extract_stack(None, 2)[0][2]
Это похоже на ответ Альберта Вонпуппа с дружественной оберткой.
У меня был «<модуль>» в индексе [2], но сработало следующее: traceback.extract_stack (None, 2) [0] [- 1]
для меня это не работает, но работает: traceback.extract_stack () [- 1] [2]
Это сработает, если изменить первый индекс на 1, вы, ребята, должны научиться отлаживать, прежде чем комментировать ... traceback.extract_stack (None, 2) [1] [2]
В качестве расширения @ Ответ Демина я создал несколько служебных функций, которые печатают имя текущей функции и аргументы текущей функции:
import inspect
import logging
import traceback
def get_function_name():
return traceback.extract_stack(None, 2)[0][2]
def get_function_parameters_and_values():
frame = inspect.currentframe().f_back
args, _, _, values = inspect.getargvalues(frame)
return ([(i, values[i]) for i in args])
def my_func(a, b, c=None):
logging.info('Running ' + get_function_name() + '(' + str(get_function_parameters_and_values()) +')')
pass
logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s [%(levelname)s] -> %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
my_func(1, 3) # 2016-03-25 17:16:06,927 [INFO] -> Running my_func([('a', 1), ('b', 3), ('c', None)])
Мне нравится использовать декоратор функций. Я добавил класс, который также умножает время функции. Предположим, что gLog - это стандартный логгер Python:
class EnterExitLog():
def __init__(self, funcName):
self.funcName = funcName
def __enter__(self):
gLog.debug('Started: %s' % self.funcName)
self.init_time = datetime.datetime.now()
return self
def __exit__(self, type, value, tb):
gLog.debug('Finished: %s in: %s seconds' % (self.funcName, datetime.datetime.now() - self.init_time))
def func_timer_decorator(func):
def func_wrapper(*args, **kwargs):
with EnterExitLog(func.__name__):
return func(*args, **kwargs)
return func_wrapper
так что теперь все, что вам нужно сделать со своей функцией, это украсить ее и вуаля
@func_timer_decorator
def my_func():
Если вас тоже интересуют методы классов, в Python 3.3+ есть __qualname__ в дополнение к __name__.
def my_function():
pass
class MyClass(object):
def method(self):
pass
print(my_function.__name__) # gives "my_function"
print(MyClass.method.__name__) # gives "method"
print(my_function.__qualname__) # gives "my_function"
print(MyClass.method.__qualname__) # gives "MyClass.method"
Если у метода есть декоратор @property, это не сработает, каким образом получить доступ к имени в этом экземпляре?
@dusiod, похоже, вы можете получить имя / qualname из геттера свойства (который вы создаете с помощью декоратора @property): MyClass.prop.fget.__qualname__ дает MyClass.prop.
Вы просто хотите получить имя функции, вот простой код для этого. скажем, у вас есть эти функции, определенные
def function1():
print "function1"
def function2():
print "function2"
def function3():
print "function3"
print function1.__name__
вывод будет function1
Теперь предположим, что у вас есть эти функции в списке
a = [function1 , function2 , funciton3]
чтобы получить имя функции
for i in a:
print i.__name__
вывод будет
function1
функция2
функция3
import inspect
def foo():
print(inspect.stack()[0][3])
где
stack()[0] - это звонящий
stack()[3] - это строковое имя метода
Легко и просто. stack()[0] всегда будет вызывающим, [3] - строковым именем метода.
Я видел несколько ответов, в которых использовались декораторы, хотя мне показалось, что некоторые из них были немного многословными. Вот кое-что, что я использую для регистрации имен функций, а также их соответствующих входных и выходных значений. Я адаптировал его здесь, чтобы просто распечатать информацию, а не создавать файл журнала, и адаптировал его для применения к конкретному примеру OP.
def debug(func=None):
def wrapper(*args, **kwargs):
try:
function_name = func.__func__.__qualname__
except:
function_name = func.__qualname__
return func(*args, **kwargs, function_name=function_name)
return wrapper
@debug
def my_function(**kwargs):
print(kwargs)
my_function()
Выход:
{'function_name': 'my_function'}
Что мне нравится в этом, в отличие от всех других, так это то, что, просто добавив функцию debug, вы можете добавить функциональность, просто добавив декоратор к любой функции, которая вам нужна или вы хотите распечатать имя функции. Вроде бы самый простой и самый адаптируемый.
Пытаться
import sys
fn_name = sys._getframe().f_code.co_name
дальнейшая ссылка https://www.oreilly.com/library/view/python-cookbook/0596001673/ch14s08.html
import inspect
def my_first_function():
func_name = inspect.stack()[0][3]
print(func_name) # my_first_function
или же:
import sys
def my_second_function():
func_name = sys._getframe().f_code.co_name
print(func_name) # my_second_function
В чем разница между f .__ name__ и f.func_name?