如何在函数内部获取函数对象(Python)
我想要实现一个类似这样的功能:
def x():
print get_def_name()
但我不一定知道x
的名字。
理想情况下,它应该返回'x'
,其中x就是这个函数的名字。
4 个回答
我找到了一个和Vazirani的解决方案类似的方法,不过我往前走了一步,想根据名字来获取函数对象。下面是我的解决方案:
import inspect
def named_func():
func_name = inspect.stack()[0].function
func_obj = inspect.stack()[1].frame.f_locals[func_name]
print(func_name, func_obj, func_obj.xxx)
named_func.xxx = 15
named_func()
输出结果是
named_func <function named_func at 0x7f3bc84622f0> 15
可惜我无法用lambda函数做到这一点。我一直在尝试。
听起来你想要声明一个匿名函数,这样就可以得到一个新的函数对象的引用。
在Python中,你可以用 lambda
来创建一个简单的匿名函数,但如果是复杂的函数,就必须给它起个名字。不过,实际上任何函数对象都是一个对象,你可以把它的引用传来传去,所以名字并不重要。
# lambda
sqr = lambda n: n**2
assert sqr(2) == 4
assert sqr(3) == 9
# named function
def f(n):
return n**2
sqr = f
assert sqr(2) == 4
assert sqr(3) == 9
注意,这个函数确实有个名字,叫 f
,但在这里名字并不是关键。我们把名字 sqr
赋给这个函数对象的引用,然后用这个名字。其实我们也可以把这个函数的引用放进一个列表或者其他数据结构里。
你可以重复使用函数的名字:
def f(n):
return n**2
sqr = f
def f(n):
return n**3
cube = f
所以,虽然Python并不完全支持匿名函数,但你可以达到类似的效果。给函数起名字其实也不是个问题。
如果你真的不想让这个函数有名字,你可以解除这个名字的绑定:
def f(n):
return n**2
lst = [f] # save function reference in a list
del(f) # unbind the name
现在访问这个函数的唯一方式就是通过列表;函数的名字就消失了。
在Python中,函数其实是对象,而这些对象有一个属性,可以告诉你它们是用什么名字定义的:
>>> def x():
... pass
...
>>> print x.__name__
x
所以,一个简单的方法可能是这样:
>>> def x():
... print x.__name__
...
>>> x()
x
这样看起来是可以的。不过,因为你必须在函数内部知道x
的名字才能做到这一点,所以你其实并没有得到什么好处;你完全可以直接这样做:
def x():
print "x"
实际上,这样做还不如前面的方法,因为__name__
这个属性只指向函数被定义时的名字。如果它被绑定到另一个名字上,你可能会发现它的表现和你预期的不一样:
>>> y = x
>>> y()
x
更糟糕的是,如果原来的名字不再存在,它根本就无法工作:
>>> del x
>>> y()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in x
NameError: global name 'x' is not defined
这个第二个问题其实是可以解决的,虽然方法不太优雅。诀窍是写一个装饰器,可以把函数的名字作为参数传进去:
>>> from functools import wraps
>>> def introspective(func):
... __name__ = func.__name__
... @wraps(func)
... def wrapper(*args, **kwargs):
... return func(__name__=__name__, *args, **kwargs)
... return wrapper
...
>>> @introspective
... def x(__name__):
... print __name__
...
>>> x()
x
>>> y = x
>>> y()
x
>>> del x
>>> y()
x
... 不过正如你所看到的,你得到的仍然只是函数被定义时的名字,而不是它现在绑定的名字。
实际上,简单(而且正确)的答案是“别这么做”。在Python中,函数对象并不知道自己被绑定到了什么名字(或多个名字)上——如果你觉得你的函数需要这些信息,那说明你可能做错了什么。
你可以使用Python自带的inspect库来实现这个功能。
如果你想处理更复杂的情况,可以查看它的文档,但下面这段代码就能满足你的需求:
from inspect import getframeinfo, currentframe
def test_func_name():
return getframeinfo(currentframe()).function
print(test_func_name())