问题:从该函数中确定函数名称(不使用回溯)
在Python中,不使用traceback
模块,是否有办法从该函数内部确定函数名称?
说我有一个带功能栏的模块foo。执行时foo.bar()
,bar是否有办法知道bar的名称?还是更好,foo.bar
名字?
#foo.py
def bar():
print "my name is", __myname__ # <== how do I calculate this at runtime?
In Python, without using the traceback
module, is there a way to determine a function’s name from within that function?
Say I have a module foo with a function bar. When executing foo.bar()
, is there a way for bar to know bar’s name? Or better yet, foo.bar
‘s name?
#foo.py
def bar():
print "my name is", __myname__ # <== how do I calculate this at runtime?
回答 0
Python没有功能来访问函数本身或函数内部的名称。已经提出但遭到拒绝。如果您不想自己玩堆栈,则应该使用"bar"
或bar.__name__
取决于上下文。
给定的拒绝通知是:
该PEP被拒绝。目前尚不清楚在极端情况下应该如何实现它或确切的语义,也没有足够的重要用例。回应充其量是冷淡的。
Python doesn’t have a feature to access the function or its name within the function itself. It has been proposed but rejected. If you don’t want to play with the stack yourself, you should either use "bar"
or bar.__name__
depending on context.
The given rejection notice is:
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
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
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
回答 2
有几种方法可以得到相同的结果:
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
There are a few ways to get the same result:
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)
Note that the inspect.stack
calls are thousands of times slower than the alternatives:
$ 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
您可以使用@Andreas Jung显示的方法来获得定义的名称,但这可能不是使用该函数调用的名称:
import inspect
def Foo():
print inspect.stack()[0][3]
Foo2 = Foo
>>> Foo()
Foo
>>> Foo2()
Foo
我不能说这种区别对您是否重要。
You can get the name that it was defined with using the approach that @Andreas Jung shows, but that may not be the name that the function was called with:
import inspect
def Foo():
print inspect.stack()[0][3]
Foo2 = Foo
>>> Foo()
Foo
>>> Foo2()
Foo
Whether that distinction is important to you or not I can’t say.
回答 4
functionNameAsString = sys._getframe().f_code.co_name
我想要一个非常类似的东西,因为我想将函数名称放在一个日志字符串中,该字符串在我的代码中占据了很多位置。可能不是执行此操作的最佳方法,但是这是一种获取当前函数名称的方法。
functionNameAsString = sys._getframe().f_code.co_name
I wanted a very similar thing because I wanted to put the function name in a log string that went in a number of places in my code. Probably not the best way to do that, but here’s a way to get the name of the current function.
回答 5
我将这个方便的实用程序放在附近:
import inspect
myself = lambda: inspect.stack()[1][3]
用法:
myself()
I keep this handy utility nearby:
import inspect
myself = lambda: inspect.stack()[1][3]
Usage:
myself()
回答 6
我想这inspect
是最好的方法。例如:
import inspect
def bar():
print("My name is", inspect.stack()[0][3])
I guess inspect
is the best way to do this. For example:
import inspect
def bar():
print("My name is", inspect.stack()[0][3])
回答 7
我找到了一个将写函数名称的包装器
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
存根
I found a wrapper that will write the function name
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()
This will print
my_funky_name
STUB
回答 8
这实际上是从该问题的其他答案中得出的。
这是我的看法:
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()的时间]。
This is actually derived from the other answers to the question.
Here’s my take:
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
The likely advantage of this version over using inspect.stack() is that it should be thousands of times faster [see Alex Melihoff’s post and timings regarding using sys._getframe() versus using inspect.stack() ].
回答 9
print(inspect.stack()[0].function)
似乎也可以使用(Python 3.5)。
print(inspect.stack()[0].function)
seems to work too (Python 3.5).
回答 10
这是一种面向未来的方法。
将@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))
Here’s a future-proof approach.
Combining @CamHart’s and @Yuval’s suggestions with @RoshOxymoron’s accepted answer has the benefit of avoiding:
_hidden
and potentially deprecated methods
- indexing into the stack (which could be reordered in future pythons)
So I think this plays nice with future python versions (tested on 2.7.3 and 3.3.2):
from __future__ import print_function
import inspect
def bar():
print("my name is '{}'".format(inspect.currentframe().f_code.co_name))
回答 11
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
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())
Test:
a = A()
a.test_class_func_name()
test_func_name()
Output:
test_class_func_name
test_func_name
回答 12
我不确定为什么人们会变得复杂:
import sys
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))
I am not sure why people make it complicated:
import sys
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))
回答 13
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 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()
In IDE the code outputs
hello, I’m foo, daddy is
hello, I’m bar, daddy is foo
hello, I’m bar, daddy is
回答 14
您可以使用装饰器:
def my_function(name=None):
return name
def get_function_name(function):
return function(name=function.__name__)
>>> get_function_name(my_function)
'my_function'
You can use a decorator:
def my_function(name=None):
return name
def get_function_name(function):
return function(name=function.__name__)
>>> get_function_name(my_function)
'my_function'
回答 15
我使用自己的方法在多重继承场景中安全地调用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__作为当前函数名称。
I do my own approach used for calling super with safety inside multiple inheritance scenario (I put all the code)
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
sample usage:
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)()
testing it :
a = C()
a.test()
output:
called from C
called from A
called from B
Inside each @with_name decorated method you have access to self.__fname__ as the current function name.
回答 16
我最近尝试使用以上答案从该函数的上下文访问该函数的文档字符串,但由于上述问题仅返回了名称字符串,因此它不起作用。
幸运的是,我找到了一个简单的解决方案。如果像我一样,您要引用该函数,而不是简单地获取表示名称的字符串,您可以将eval()应用于函数名称的字符串。
import sys
def foo():
"""foo docstring"""
print(eval(sys._getframe().f_code.co_name).__doc__)
I recently tried to use the above answers to access the docstring of a function from the context of that function but as the above questions were only returning the name string it did not work.
Fortunately I found a simple solution. If like me, you want to refer to the function rather than simply get the string representing the name you can apply eval() to the string of the function name.
import sys
def foo():
"""foo docstring"""
print(eval(sys._getframe().f_code.co_name).__doc__)
回答 17
我建议不要依赖堆栈元素。如果有人在不同的上下文(例如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')
I suggest not to rely on stack elements. If someone use your code within different contexts (python interpreter for instance) your stack will change and break your index ([0][3]).
I suggest you something like that:
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')
回答 18
用装饰器很容易做到这一点。
>>> 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')
This is pretty easy to accomplish with a decorator.
>>> 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')