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

时间:2011-02-21 15:11:36

标签: python function introspection traceback

在Python中,不使用traceback模块,有没有办法从该函数中确定函数的名称?

假设我有一个带功能栏的模块foo。执行foo.bar()时,有没有办法让酒吧知道酒吧的名字?或者更好,foo.bar的名字?

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

22 个答案:

答案 0 :(得分:341)

import inspect

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

答案 1 :(得分:155)

Python没有在函数本身中访问函数或其名称的功能。它已被proposed但被拒绝了。如果您不想自己玩堆栈,则应根据具体情况使用"bar"bar.__name__

给出的拒绝通知是:

  

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

答案 2 :(得分:124)

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

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调用比其他选项慢几千倍:

$ 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

答案 3 :(得分:43)

您可以使用the approach that @Andreas Jung shows获取使用{{3}}定义的名称,但这可能不是调用该函数的名称:

import inspect

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

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

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

答案 4 :(得分:34)

functionNameAsString = sys._getframe().f_code.co_name

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

答案 5 :(得分:20)

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

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

用法:

myself()

答案 6 :(得分:18)

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

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

答案 7 :(得分:13)

我找到了一个将编写函数名称

的包装器
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

答案 8 :(得分:11)

这实际上是从问题的其他答案中得出的。

这是我的看法:

from scipy import weave

def cummode(x, axis = 1):

    assert x.ndim == 2 and axis == 1, 'Only implemented for a special case!'
    all_values, element_ids = np.unique(x, return_inverse=True)
    n_unique = len(all_values)
    element_ids = element_ids.reshape(x.shape)
    result = np.zeros(x.shape, dtype = int)
    counts = np.zeros(n_unique, dtype = int)
    code = """
    int n_samples = Nelement_ids[0];
    int n_events = Nelement_ids[1];
    for (int i=0; i<n_samples; i++){
        int maxcount = 0;
        int maxel = -1;

        for (int k=0; k<n_unique; k++)
            counts[k] = 0;

        for (int j=0; j<n_events; j++){
            int ix = i*n_events+j;
            int k = element_ids[ix];
            counts[k]+=1;
            if (counts[k] > maxcount){
                maxcount = counts[k];
                maxel = k;
            }
            result[ix]=maxel;
        }
    }
    """
    weave.inline(code, ['element_ids', 'result', 'n_unique', 'counts'], compiler = 'gcc')
    mode_values = all_values[result]
    return mode_values

这个版本比使用inspect.stack()的可能优势是它应该快几千倍[参见Alex Melihoff关于使用sys._getframe()和使用inspect.stack()的帖子和时间。 ]

答案 9 :(得分:11)

print(inspect.stack()[0].function)似乎也有效(Python 3.5)。

答案 10 :(得分:10)

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

     

你好,我是吧,爸爸是

答案 11 :(得分:9)

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

将@ CamHart和@Yuval的建议与@ RoshOxymoron accepted answer相结合,可以避免:

  • _hidden以及可能已弃用的方法
  • 索引到堆栈中(可以在将来的pythons中重新排序)

所以我认为这对未来的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))

答案 12 :(得分:9)

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

答案 13 :(得分:5)

我做了CamHart说的话:

import sys
def myFunctionsHere():
    print(sys._getframe().f_code.co_name)

myFunctionsHere()

输出:

  

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

     

处理完成,退出代码为0

答案 14 :(得分:3)

我使用自己的方法在多重继承场景中调用超级安全(我把所有代码都放在一起)

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__作为当前函数名。

答案 15 :(得分:3)

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

    interface TodoState {
      todos: Todo[]
    }

//no error
    const arrowFunc: React.FC<TodoState> = ({ todos }) => { }

//error: binding element 'todos' implicitly has an 'any' type.ts(7031)
    function nonArrow({ todos }): React.FC<TodoState> {}

答案 16 :(得分:3)

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

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

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

答案 17 :(得分:3)

使用此方法(根据#Ron Davis的回答):

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

答案 18 :(得分:2)

我建议不要依赖堆栈元素。如果有人在不同的上下文(例如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')

答案 19 :(得分:1)

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

import sys 
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))

答案 20 :(得分:0)

str(str(inspect.currentframe())).split(' ')[-1][:-1]

答案 21 :(得分:-1)

使用__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文档中(请参见Callable types部分)。

每个函数都有一个返回其名称的__name__参数,甚至有一个返回其全名的__qualname__参数,包括其所属的类(请参见Qualified name)。