在 Python 中,不使用traceback
模块,有没有办法从该函数中确定函数的名称?
假设我有一个foo
带有功能的模块bar
。执行时foo.bar()
,有没有办法bar
知道它bar
的名字?或者更好foo.bar
的是,名字?
#foo.py
def bar():
print "my name is", __myname__ # <== how do I calculate this at runtime?
在 Python 中,不使用traceback
模块,有没有办法从该函数中确定函数的名称?
假设我有一个foo
带有功能的模块bar
。执行时foo.bar()
,有没有办法bar
知道它bar
的名字?或者更好foo.bar
的是,名字?
#foo.py
def bar():
print "my name is", __myname__ # <== how do I calculate this at runtime?
import inspect
def foo():
print(inspect.stack()[0][3])
print(inspect.stack()[1][3]) # will give the caller of foos name, if something called foo
foo()
输出:
foo <module_caller_of_foo>
Python 没有在函数本身中访问函数或其名称的功能。它已被提议但被拒绝。如果您不想自己玩堆栈,您应该使用"bar"
或bar.__name__
取决于上下文。
给定的拒绝通知是:
此 PEP 被拒绝。目前尚不清楚它应该如何实现,或者在边缘情况下应该有什么精确的语义,并且没有给出足够重要的用例。反应充其量是不冷不热的。
有几种方法可以获得相同的结果:
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
更新 08/2021(原帖是为 Python2.7 编写的)
Python 3.9.1 (default, Dec 11 2020, 14:32:07)
[GCC 7.3.0] :: Anaconda, Inc. on linux
python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
500 loops, best of 5: 390 usec per loop
python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
500 loops, best of 5: 398 usec per loop
python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
2000000 loops, best of 5: 176 nsec per loop
python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
5000000 loops, best of 5: 62.8 nsec per loop
functionNameAsString = sys._getframe().f_code.co_name
我想要一个非常相似的东西,因为我想把函数名放在一个日志字符串中,该字符串在我的代码中的许多地方都有。可能不是最好的方法,但这是一种获取当前函数名称的方法。
您可以使用@Andreas Jung 显示的方法获取它定义的名称,但这可能不是调用该函数的名称:
import inspect
def Foo():
print inspect.stack()[0][3]
Foo2 = Foo
>>> Foo()
Foo
>>> Foo2()
Foo
我不能说这种区别对你是否重要。
我把这个方便的实用程序放在附近:
import inspect
myself = lambda: inspect.stack()[1][3]
用法:
myself()
我想inspect
这是最好的方法。例如:
import inspect
def bar():
print("My name is", inspect.stack()[0][3])
我找到了一个将写入函数名称的包装器
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
存根
这实际上来自该问题的其他答案。
这是我的看法:
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() 相比,此版本的可能优势在于它应该快数千倍 [请参阅 Alex Melihoff 的帖子和有关使用 sys._getframe() 与使用 inspect.stack() 的时间安排]。
print(inspect.stack()[0].function)
似乎也可以工作(Python 3.5)。
这是一种面向未来的方法。
将@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))
更新:在 3.7.10、3.8.10 和 3.9.5 上测试
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
你好,我是酒吧,爸爸是
我不确定为什么人们让它变得复杂:
import sys
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))
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
您可以使用装饰器:
def my_function(name=None):
return name
def get_function_name(function):
return function(name=function.__name__)
>>> get_function_name(my_function)
'my_function'
使用__name__
属性:
# foo.py
def bar():
print(f"my name is {bar.__name__}")
__name__
您可以使用属性轻松地从函数中访问函数的名称。
>>> def bar():
... print(f"my name is {bar.__name__}")
...
>>> bar()
my name is bar
我自己多次遇到过这个问题,正在寻找解决方法。正确答案包含在 Python 的文档中(请参阅可调用类型部分)。
每个函数都有一个__name__
返回其名称的参数,甚至还有一个__qualname__
返回其全名的参数,包括它属于哪个类(请参阅限定名称)。
我建议不要依赖堆栈元素。如果有人在不同的上下文中使用您的代码(例如 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')
使用装饰器很容易做到这一点。
>>> from functools import wraps
>>> def named(func):
... @wraps(func)
... def _(*args, **kwargs):
... return func(func.__name__, *args, **kwargs)
... return _
...
>>> @named
... def my_func(name, something_else):
... return name, something_else
...
>>> my_func('hello, world')
('my_func', 'hello, world')
我用自己的方法在多重继承场景中安全地调用 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 修饰的方法中,您可以访问 self.__fname__ 作为当前函数名。
我最近尝试使用上述答案从该函数的上下文中访问该函数的文档字符串,但由于上述问题仅返回名称字符串,因此它不起作用。
幸运的是,我找到了一个简单的解决方案。如果像我一样,您想引用函数而不是简单地获取表示名称的字符串,您可以将 eval() 应用于函数名称的字符串。
import sys
def foo():
"""foo docstring"""
print(eval(sys._getframe().f_code.co_name).__doc__)
由于在python 3.9sys._getframe().f_back.f_code.co_name
中根本不起作用,因此可以从现在开始使用以下内容:
from inspect import currentframe
def testNameFunction() -> str:
return currentframe().f_back.f_code.co_name
print(f'function name is {testNameFunction()}(...)')
结果:
function name is testNameFunction(...)
我喜欢使用装饰器的想法,但我更愿意避免接触函数参数。因此,我提供了另一种选择:
import functools
def withname(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
global __name
__saved_name = globals().get("__name")
__name = f.__name__
ret = f(*args, **kwargs)
__name = __saved_name
return ret
return wrapper
@withname
def f():
print(f"in f: __name=={__name}")
g()
print(f"back in f: __name=={__name}")
@withname
def g():
print(f"in g: __name=={__name}")
__name
由于它是全局变量,因此在调用该函数时我们需要保存和恢复。上面调用f()
会产生:
in f: __name==f
in g: __name==g
back in f: __name==f
global
不幸的是,如果我们不更改函数参数,就没有变量可以替代。引用不是在函数上下文中创建的变量,将生成查找全局变量的代码:
>>> def f(): print(__function__)
>>> from dis import dis
>>> dis(f)
1 0 LOAD_GLOBAL 0 (print)
2 LOAD_GLOBAL 1 (__function__)
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
str(str(inspect.currentframe())).split(' ')[-1][:-1]