628

在 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?
4

23 回答 23

504
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>
于 2011-02-21T15:16:04.770 回答
261

Python 没有在函数本身中访问函数或其名称的功能。它已被提议但被拒绝。如果您不想自己玩堆栈,您应该使用"bar"bar.__name__取决于上下文。

给定的拒绝通知是:

此 PEP 被拒绝。目前尚不清楚它应该如何实现,或者在边缘情况下应该有什么精确的语义,并且没有给出足够重要的用例。反应充其量是不冷不热的。

于 2011-02-21T15:16:44.527 回答
220

有几种方法可以获得相同的结果:

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
于 2013-06-28T14:04:54.643 回答
55
functionNameAsString = sys._getframe().f_code.co_name

我想要一个非常相似的东西,因为我想把函数名放在一个日志字符串中,该字符串在我的代码中的许多地方都有。可能不是最好的方法,但这是一种获取当前函数名称的方法。

于 2013-03-31T03:05:31.227 回答
53

您可以使用@Andreas Jung 显示的方法获取它定义的名称,但这可能不是调用该函数的名称:

import inspect

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

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

我不能说这种区别对你是否重要。

于 2011-02-21T15:34:31.333 回答
31

我把这个方便的实用程序放在附近:

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

用法:

myself()
于 2014-07-08T09:48:23.860 回答
29

我想inspect这是最好的方法。例如:

import inspect
def bar():
    print("My name is", inspect.stack()[0][3])
于 2011-02-21T15:21:01.260 回答
25

我找到了一个将写入函数名称的包装器

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

存根

于 2013-11-25T11:46:55.140 回答
23

这实际上来自该问题的其他答案。

这是我的看法:

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() 的时间安排]。

于 2015-07-24T16:35:54.857 回答
18

print(inspect.stack()[0].function)似乎也可以工作(Python 3.5)。

于 2016-01-07T21:31:14.540 回答
17

这是一种面向未来的方法。

将@CamHart 和@Yuval 的建议与@RoshOxymoron接受的答案结合起来有利于避免:

  • _hidden和可能被弃用的方法
  • 索引到堆栈中(可以在未来的 python 中重新排序)

所以我认为这与未来的 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 上测试

于 2015-03-17T21:56:36.337 回答
14
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

你好,我是酒吧,爸爸是

于 2015-10-15T22:46:41.207 回答
14

我不确定为什么人们让它变得复杂:

import sys 
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))
于 2019-07-09T21:15:19.193 回答
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
于 2016-10-22T09:37:35.583 回答
10

您可以使用装饰器:

def my_function(name=None):
    return name

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

>>> get_function_name(my_function)
'my_function'
于 2015-04-29T19:07:24.827 回答
8

使用__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__返回其全名的参数,包括它属于哪个类(请参阅限定名称)。

于 2020-09-28T22:00:13.993 回答
6

我建议不要依赖堆栈元素。如果有人在不同的上下文中使用您的代码(例如 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')
于 2019-05-03T09:51:04.507 回答
6

使用装饰器很容易做到这一点。

>>> 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')
于 2020-05-14T02:03:58.853 回答
5

我用自己的方法在多重继承场景中安全地调用 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__ 作为当前函数名。

于 2015-12-24T16:31:16.503 回答
5

我最近尝试使用上述答案从该函数的上下文中访问该函数的文档字符串,但由于上述问题仅返回名称字符串,因此它不起作用。

幸运的是,我找到了一个简单的解决方案。如果像我一样,您想引用函数而不是简单地获取表示名称的字符串,您可以将 eval() 应用于函数名称的字符串。

import sys
def foo():
    """foo docstring"""
    print(eval(sys._getframe().f_code.co_name).__doc__)
于 2018-09-13T07:33:13.393 回答
2

由于在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(...)
于 2021-08-13T12:25:41.880 回答
1

我喜欢使用装饰器的想法,但我更愿意避免接触函数参数。因此,我提供了另一种选择:

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
于 2021-10-16T12:29:39.187 回答
0
str(str(inspect.currentframe())).split(' ')[-1][:-1]
于 2021-07-12T12:30:57.263 回答