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

Rob*_*Rob 423 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?
Run Code Online (Sandbox Code Playgroud)

小智 372

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
Run Code Online (Sandbox Code Playgroud)

  • 您还可以使用:```print(inspect.currentframe().f_code.co_name)``或获取调用者的名字:```print(inspect.currentframe().f_back.f_code.co_name)``` .我认为它应该更快,因为你没有检索所有堆栈帧的列表,因为```inspect.stack()```. (49认同)
  • 这很好,因为你也可以做`[1] [3]`来获取来电者的名字. (45认同)
  • 请注意:inspect.stack() 可能会产生大量的性能开销,因此请谨慎使用!在我的手臂盒上完成了 240 毫秒(出于某种原因) (6认同)
  • `inspect.currentframe().f_back.f_code.co_name`不能用于装饰方法,而`inspect.stack()[0] [3]`做... (5认同)
  • 代码中的神奇数字“3”是什么? (4认同)
  • @Michael 请发表您的评论作为答案。 (3认同)

Ros*_*ron 176

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

给出的拒绝通知是:

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

  • 将@ CamHart的方法与@Yuval相结合避免了@RoshOxymoron的答案中的"隐藏"和可能被弃用的方法以及@ neuro/@ AndreasJung的答案的数字索引:`print(inspect.currentframe().f_code.co_name)` (27认同)
  • `inspect.currentframe()`就是这样一种方式. (15认同)
  • 为什么这是选择的答案?问题不在于访问当前函数或模块本身,而在于名称。并且堆栈跟踪/调试功能已经有了这些信息。 (7认同)
  • 这个答案太旧了。现在是错误的。请删除它。 (6认同)
  • @ScottDavidTesler发送邮件到python-dev([或python-ideas](https://docs.python.org/devguide/langchanges.html#pep-process))列表 (3认同)
  • 是否有可能总结出被拒绝的原因? (2认同)

Ale*_*sky 139

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

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)
Run Code Online (Sandbox Code Playgroud)

请注意,这些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
Run Code Online (Sandbox Code Playgroud)

  • 我使用“sys._getframe().f_code.co_name”而不是“inspect.currentframe().f_code.co_name”只是因为我已经导入了“sys”模块。这是一个合理的决定吗?(考虑到速度看起来非常相似) (3认同)
  • 这是全面的答案,在我看来应该是公认的答案 (3认同)
  • 我们也可以使用 sys._getframe().f_back.f_code.co_name 来获取调用者姓名 (3认同)
  • `inspect.currentframe()`似乎是执行时间和私有成员使用之间的良好权衡 (2认同)

bgp*_*ter 44

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

import inspect

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

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo
Run Code Online (Sandbox Code Playgroud)

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

  • 与.func_name`相同的情况.值得记住的是Python中的类名和函数名是一回事,引用它们的变量是另一回事. (3认同)
  • 速度与什么有关?是否存在您需要在硬实时情况或其他情况下获得这些信息的情况? (3认同)

Ron*_*vis 38

functionNameAsString = sys._getframe().f_code.co_name
Run Code Online (Sandbox Code Playgroud)

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

  • 完全可以工作,只使用 sys,不需要加载更多模块,但不太容易记住:V (2认同)

xxy*_*zzy 21

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

import inspect
myself = lambda: inspect.stack()[1][3]
Run Code Online (Sandbox Code Playgroud)

用法:

myself()
Run Code Online (Sandbox Code Playgroud)

  • @prismalytics.io:如果你称自己为 (myself()) 并且不只是使用它的值(myself),你会得到你想要的。 (2认同)

Bjo*_*orn 20

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

import inspect
def bar():
    print("My name is", inspect.stack()[0][3])
Run Code Online (Sandbox Code Playgroud)

  • 相反,如果使用“inspect.stack()[0][3]”,请使用“inspect.stack()[0].function”,即使堆栈跟踪中的语义发生变化,它也应该更加健壮。 (3认同)

cad*_*6uk 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()
Run Code Online (Sandbox Code Playgroud)

这将打印

my_funky_name

存根


Gin*_*ino 12

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

这是我的看法:

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
Run Code Online (Sandbox Code Playgroud)

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


Pie*_*sin 12

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

  • 唉,它不适用于2.7 :-( (2认同)

hob*_*obs 11

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

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

  • _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))
Run Code Online (Sandbox Code Playgroud)


Lee*_*Lee 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()
Run Code Online (Sandbox Code Playgroud)

在IDE中代码输出

你好,我是foo,爸爸是

你好,我是酒吧,爸爸是foo

你好,我是酒吧,爸爸是


小智 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())
Run Code Online (Sandbox Code Playgroud)

测试:

a = A()
a.test_class_func_name()
test_func_name()
Run Code Online (Sandbox Code Playgroud)

输出:

test_class_func_name
test_func_name
Run Code Online (Sandbox Code Playgroud)


Dou*_*tog 7

你可以使用装饰器:

def my_function(name=None):
    return name

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

>>> get_function_name(my_function)
'my_function'
Run Code Online (Sandbox Code Playgroud)

  • 这是如何回答发帖人的问题的?您能否扩展此示例以包括如何从函数内部获知函数名称的示例? (3认同)
  • 这看起来像是一种获取函数的“__name__”属性的复杂方法。使用需要知道您想要获取的东西,这对我来说在函数未动态定义的简单情况下似乎不太有用。 (3认同)

小智 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')
Run Code Online (Sandbox Code Playgroud)


kar*_*k r 6

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

import sys 
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))
Run Code Online (Sandbox Code Playgroud)

  • 也许是因为您正在使用 sys 模块外部的私有函数。一般来说,这被认为是一种不好的做法,不是吗? (3认同)
  • @tikej,用法同意此处所述的最佳实践:https://docs.quantifiedcode.com/python-anti-patterns/ Correctness/accessing_a_protected_member_from_outside_the_class.html (3认同)

Vag*_*eev 6

使用__name__属性:

# foo.py
def bar():
    print(f"my name is {bar.__name__}")
Run Code Online (Sandbox Code Playgroud)

您可以使用__name__属性轻松地从函数内部访问函数的名称。

>>> def bar():
...     print(f"my name is {bar.__name__}")
...
>>> bar()
my name is bar
Run Code Online (Sandbox Code Playgroud)

我自己多次遇到过这个问题,正在寻找解决方法。正确答案包含在 Python 的文档中(请参阅可调用类型部分)。

每个函数都有一个__name__返回其名称的__qualname__参数,甚至返回其全名的参数,包括它属于哪个类(请参阅限定名称)。

  • @PyNoob:将 `bar` 重命名为 `foo` 后, `print('bar')` 愉快地打印(错误地)“bar”,而 `print(bar.__name__)` 则失败。 (8认同)
  • 如果必须知道函数名称“bar”才能执行此操作,这有什么意义? (2认同)

Mel*_*nez 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
Run Code Online (Sandbox Code Playgroud)

示例用法:

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)()
Run Code Online (Sandbox Code Playgroud)

测试它:

a = C()
a.test()
Run Code Online (Sandbox Code Playgroud)

输出:

called from C
called from A
called from B
Run Code Online (Sandbox Code Playgroud)

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


Chr*_*sen 5

我做了CamHart所说的:

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

myFunctionsHere()
Run Code Online (Sandbox Code Playgroud)

输出:

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

流程结束,退出代码为0


Joh*_*bes 5

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

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

import sys
def foo():
    """foo docstring"""
    print(eval(sys._getframe().f_code.co_name).__doc__)
Run Code Online (Sandbox Code Playgroud)


小智 5

这很容易用装饰器来完成。

>>> 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')
Run Code Online (Sandbox Code Playgroud)