在Python中,如何在不调用函数的情况下以字符串的形式获得函数名?
1 2 3 4
| def my_function():
pass
print get_function_name_as_string(my_function) # my_function is not in quotes |
应该输出"my_function"。
Python中有这样的函数吗?如果没有,有关于如何在Python中实现get_function_name_as_string的想法吗?
- stackoverflow.com/questions/245304/…
- 下次请在问题中加入你的动机。在当前的形式下,它显然使人们感到困惑,其中一些人倾向于假设您将逐字调用get_function_name_as_string(my_function)并期望得到"my_function"的结果。我猜您的动机是通用代码,它将函数作为第一类对象使用,并且需要检索作为参数传递的函数的名称。
- 问题背后的动机与技术答案无关。不要把它作为嘲笑、批评、理智的姿态和逃避回答的借口。
- 然而,动机可以帮助决定是否提出了正确的问题。在这个例子中,用户已经清楚地知道函数的名称,所以使用另一个函数来返回它?为什么不直接做一些像print("my_function")这样的事情呢?答案在于动机。
- @Dannid,这是一个愚蠢的论点。如果OP要求获取函数的名称,是因为它是以需要输出的方式封装或理解的,例如为了调试的目的。在需要这种需求的环境中手工调整它是一种可怕的编程实践。您可能有一个要调用的函数列表,或者传递一个函数并希望记录函数名。除了为了好玩/学习Python之外,还有其他一百万个理由,如果你想学的话,这些理由已经足够了。
- 再读一遍我的评论,我不确定自己为什么会站在希望获得动力的一边。动力应该是无关紧要的。看来我是在替穷人说话。我认为我的感觉和沟通不是很好是它有助于知道什么是函数的目标-你调试吗?编写函数模板?创建动态全局变量?这是一个一次性的功能,还是一个您将经常使用的永久功能?无论如何,我应该同意动机与技术答案无关,尽管它可能有助于决定哪个技术答案是最好的。
- 事实上,动机是相关的,当你不清楚的时候,质疑它是至关重要的。但是关于嘲笑的建议是正确的。meta.stackexchange.com/questions/66377/what-is-the-xy-proble? & # 8203; m
使用__name__是首选的方法,因为它的应用是一致的。与func_name不同的是,它也适用于内置函数:
1 2 3 4 5 6 7
| >>> 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__属性,所以您只需要记住一个特殊的名称。
- -1:您正在硬编码函数名。如果您已经知道函数名,为什么还要调用它的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)。所以Albert Vonpupp的答案在我看来更好。
- @RichardGomes,一个答案适用于在函数本身中获取名称,另一个答案适用于从函数引用中获取名称。OP的问题表明了后者。
- @RichardGomes,实际上我来这个问题是为了寻找解决这个问题的方法。我试图解决的问题是创建一个可用于记录所有调用的装饰器。
- @RichardGomes函数是一类对象,可以有多个名称绑定到它们。这并不一定是f.__name__ == 'f'的情况。
- 这篇文章帮助我解决了这个问题。
- 这是我觉得唯一的线程,以赞成票反对以及澄清意见(即。,第一个和第二个):D
- 这是Stack Overflow的一个经典例子:"我不在乎你想做什么,我将拒绝给你答案,因为我不同意。"
- 我不认为这个解决方案有任何帮助,因为大多数人只有在他们想要一些通用的东西时才会使用它,而这些东西你可以多次调用作为一个方法。如果您想跟踪运行函数的顺序,那么这种方法是很好的。上面的解是,函数是foo,调用foo。然后将其设置为变量名。您还不如设置一个值为foo的字符串。下面描述的_getframe()调用是另一个解决方案,它在一个类中的多个函数中工作!正是我所需要的。
- @EamonnKenny我写了一个装饰器,它接受一个泛型函数作为输入。在某些情况下,如果方法不受支持,则会引发错误。我的异常报告异常消息中不支持的方法名。__name__解决了这个问题。
- 哈哈因为@RichardGomes的恶意挑衅,它鼓励我投赞成票。
你也可以用
1 2
| import sys
this_function_name = sys._getframe().f_code.co_name |
- +1:这就是我想知道的答案。其他答案假设调用者已经知道函数名,这在这个问题的上下文中是无意义的。
- 理查德:没有。假设在函数定义的范围内直接调用函数name或func_name,但通常情况并非如此。请记住函数是对象——它们可以作为参数传递给其他函数,存储在列表/dict中,以便以后查找或执行,等等。
- @ paulus_全能,深入堆栈框架并不会让我觉得抽象!事实上,它是抽象的对立面。请参阅文档中的实现细节说明。并不是所有的Python实现都包含sys._getframe——它直接连接到CPython的内部。
- 这只在函数内部有效,但是问题指定不应该调用函数。
- 如果你想把你的函数包装成更有用、更容易记住的东西,你必须检索像sys._getframe(1).f_code.co_name这样的帧1,这样你就可以定义像get_func_name()这样的函数,并期望得到调用函数的期望名称。
- 这正是我想要的。谢谢你!现在我应该更好地理解为什么它会起作用。
- @Igor, sys._getframe(),获取一个n帧,case of sys._getframe(1)将从列表/堆栈中获取一个"one deep level"帧。因此,如果在类->函数->函数之间封装调用,则需要传递调用它的函数/类的数量。这有点奇怪,但很简单。在单个直接调用中,执行sys._getframe()意味着执行sys._getframe(0),因为没有明确地告诉您不能神奇地猜测您可以传递数字。不客气
函数还有其他有趣的属性。键入dir(func_name)来列出它们。func_name.func_code.co_code是编译后的函数,存储为字符串。
1 2
| import dis
dis.dis(my_function) |
将以几乎人类可读的格式显示代码。:)
- f的差值是多少?__name__ f.func_name ?
- 它们是一样的。python.org/doc/2.5.2/ref/types.html #类型
- 双下划线名称传统上被认为是私有的,但从未强制执行。所以最好使用func_name,即使它们可能是相同的。
- Sam:名字是私有的,名字是特殊的,有概念上的区别。
- 如果有人对Matthew前面的回答感到困惑,注释系统将一些双下划线解释为bold代码。用反号转义后,消息应该是:__names__是私有的,__names是特殊的。
- @MatthewTrevor,你有参考资料吗?
- 实际上,我认为正确的是_names是私有的(前面有一个下划线,只是一个约定),__names__是特殊的(前面和后面有两个下划线)。不确定双下划线之前是否有任何意义,正式或作为惯例。
- @MestreLion它的名字叫"名字乱改"。
- 没错,我忘了名字拼错了。因此,我认为最好的建议是:对"普通"私人成员使用约定(_name),因为__name受制于命名错误,而__name__可能是"特殊的",这取决于name,除非其中任何一个是您想要的,并且您知道自己在做什么。
- func_name在python3中已经不存在了,所以如果你想要兼容性,你需要使用func.__name__
这个函数将返回调用者的函数名。
1 2 3
| def func_name():
import traceback
return traceback.extract_stack(None, 2)[0][2] |
这就像阿尔伯特·冯普(Albert Vonpupp)用友好的包装给出的答案。
- 我在索引[2]处有"",但是下面的工作是有效的:回溯。extract_stack(没有,2)[0][1]
- 对我来说,这不起作用,但是这有作用:traceback.extract_stack()[-1][2]
- 如果将第一个索引更改为1,您应该在注释之前学会调试…回溯。extract_stack(没有,2)[1][2]
如果您也对类方法感兴趣,Python 3.3+除了__name__之外还有__qualname__。
1 2 3 4 5 6 7 8 9 10 11 12
| 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" |
我喜欢使用函数装饰器。我添加了一个类,它也乘以函数时间。假设gLog是一个标准的python日志记录器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 |
现在你要做的就是装饰它
1 2
| @func_timer_decorator
def my_func(): |
_getframe()不能保证在所有Python实现中都可用(参见参考资料),您可以使用回溯模块来做同样的事情,例如。
1 2 3 4 5 6
| 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
作为@Demyn答案的扩展,我创建了一些实用函数来打印当前函数的名称和参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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)]) |
你只需要得到函数名这里有一个简单的代码。假设你定义了这些函数
1 2 3 4 5 6 7 8 9
| def function1():
print"function1"
def function2():
print"function2"
def function3():
print"function3"
print function1.__name__ |
输出将是function1
现在假设你有一个列表中的这些函数
1
| a = [function1 , function2 , funciton3] |
获取函数的名称
1 2
| for i in a:
print i.__name__ |
输出将是
function1
function2
function3
1 2 3 4
| import inspect
def foo():
print(inspect.stack()[0][3]) |