python:从该函数中确定函数名称(不使用traceback)

在Python中,如果不使用traceback模块,是否有一种方法可以从函数中确定函数的名称?

假设我有一个带函数栏的foo模块。执行foo.bar()时,bar是否有方法知道bar的名称?或者更好的是,foo.bar的名字?

1
2
3
#foo.py  
def bar():
    print"my name is", __myname__ # <== how do I calculate this at runtime?


1
2
3
4
import inspect

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


Python没有在函数本身中访问函数或函数名的特性。有人提议,但遭到拒绝。如果您不想亲自处理堆栈,则应该根据上下文使用"bar"bar.__name__

所发出的拒收通知书为:

This PEP is rejected. It is not clear how it should be implemented or what the precise semantics should be in edge cases, and there aren't enough important use cases given. response has been lukewarm at best.


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

1
2
3
4
5
6
7
8
9
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调用比其他方法慢几千倍:

1
2
3
4
5
6
7
8
$ 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


您可以使用@Andreas Jung所示的方法来获得定义函数的名称,但这可能不是调用函数的名称:

1
2
3
4
5
6
7
8
9
10
11
12
import inspect

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

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

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


1
functionNameAsString = sys._getframe().f_code.co_name

我想要一个非常类似的东西,因为我想把函数名放在一个日志字符串中,它在我的代码中出现了很多地方。可能不是最好的方法,但这里有一种方法可以获得当前函数的名称。


我把这个方便的工具放在附近:

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

用法:

1
myself()


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

1
2
3
import inspect
def bar():
    print("My name is", inspect.stack()[0][3])

我找到了一个包装器,它将编写函数名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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


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


这实际上是从这个问题的其他答案推导出来的。

这里是我采用的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

与使用inspection .stack()相比,这个版本的优势可能是它应该快数千倍(参见Alex Melihoff的文章和关于使用sys._getframe()和使用inspection .stack()的计时)。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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中,代码输出

hello, I'm foo, daddy is

hello, I'm bar, daddy is foo

hello, I'm bar, daddy is


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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())

测试:

1
2
3
a = A()
a.test_class_func_name()
test_func_name()

输出:

1
2
test_class_func_name
test_func_name

这里有一个未来证明的方法。

将@CamHart和@Yuval的建议与@RoshOxymoron的公认答案相结合,可以避免:

_hidden和潜在的废弃方法索引到堆栈(可以在以后的python中重新排序)

因此,我认为这与未来的python版本(在2.7.3和3.3.2上测试过)配合得很好:

1
2
3
4
5
from __future__ import print_function
import inspect

def bar():
    print("my name is '{}'".format(inspect.currentframe().f_code.co_name))

你可以使用装饰:

1
2
3
4
5
6
7
8
def my_function(name=None):
    return name

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

>>> get_function_name(my_function)
'my_function'


我照卡姆哈特说的做了:

1
2
3
4
5
import sys
def myFunctionsHere():
    print(sys._getframe().f_code.co_name)

myFunctionsHere()

输出:

C:\Python\Python36\python.exe C:/Python/GetFunctionsNames/TestFunctionsNames.py
myFunctionsHere

Process finished with exit code 0


最近,我尝试使用上面的答案从函数的上下文中访问函数的docstring,但是由于上面的问题只返回名称字符串,所以没有成功。

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

1
2
3
4
import sys
def foo():
   """foo docstring"""
    print(eval(sys._getframe().f_code.co_name).__doc__)


我使用自己的方法在多重继承场景中安全调用super(我将所有代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
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

示例用法:

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
26
27
28
29
30
31
32
class A(object):

    def __init__():
        super(A, self).__init__()

    @with_name
    def test(self):
        print 'called from A
'

        safe_super(A, self)()

class B(object):

    def __init__():
        super(B, self).__init__()

    @with_name
    def test(self):
        print 'called from B
'

        safe_super(B, self)()

class C(A, B):

    def __init__():
        super(C, self).__init__()

    @with_name
    def test(self):
        print 'called from C
'

        safe_super(C, self)()

测试:

1
2
a = C()
a.test()

输出:

1
2
3
called from C
called from A
called from B

在每个@with_name修饰方法中,您都可以访问self。作为当前函数名。


使用这个(基于#Ron Davis的回答):

1
2
3
4
5
import sys

def thisFunctionName():
   """Returns a string with the name of the function it's called from"""
    return sys._getframe(1).f_code.co_name