Rya*_*son 32 python function introspection
如何在Python中获取包含当前正在执行的函数的变量?我不想要这个功能的名字.我知道我可以使用inspect.stack获取当前的函数名称.我想要实际的可调用对象.这可以在不使用inspect.stack检索函数的名称然后eval输入名称来获取可调用对象的情况下完成吗?
编辑:我有理由这样做,但它甚至不是一个好的.我正在使用plac来解析命令行参数.你可以通过do来使用它plac.call(main),它从"main"的函数签名生成一个ArgumentParser对象.在"main"中,如果参数有问题,我想退出一个包含ArgumentParser对象的帮助文本的错误消息,这意味着我需要通过调用直接访问该对象plac.parser_from(main).print_help().能够说出来是很好的:plac.parser_from(get_current_function()).print_help()所以我不依赖于被命名为"main"的函数.现在,我的"get_current_function"实现将是:
import inspect
def get_current_function():
return eval(inspect.stack()[1][3])
Run Code Online (Sandbox Code Playgroud)
但是这个实现依赖于具有名称的函数,我认为这不是太繁重.我永远不会这样做plac.call(lambda ...).
从长远来看,要求plac的作者实现print_help方法来打印最近使用plac或类似东西调用的函数的帮助文本可能更有用.
kin*_*all 27
堆栈框架告诉我们我们所处的代码对象.如果我们能找到一个在其__code__属性中引用该代码对象的函数对象,我们就找到了该函数.
幸运的是,我们可以向垃圾收集器询问哪些对象持有对我们的代码对象的引用,并对这些对象进行筛选,而不是必须遍历Python世界中的每个活动对象.通常只有少数对代码对象的引用.
现在,函数可以共享代码对象,并且在从函数返回函数的情况下执行,即闭包.当使用给定的代码对象有多个函数时,我们无法分辨它是哪个函数,所以我们返回None.
import inspect, gc
def giveupthefunc():
frame = inspect.currentframe(1)
code = frame.f_code
globs = frame.f_globals
functype = type(lambda: 0)
funcs = []
for func in gc.get_referrers(code):
if type(func) is functype:
if getattr(func, "__code__", None) is code:
if getattr(func, "__globals__", None) is globs:
funcs.append(func)
if len(funcs) > 1:
return None
return funcs[0] if funcs else None
Run Code Online (Sandbox Code Playgroud)
一些测试用例:
def foo():
return giveupthefunc()
zed = lambda: giveupthefunc()
bar, foo = foo, None
print bar()
print zed()
Run Code Online (Sandbox Code Playgroud)
我不确定这个的性能特征,但我认为它对你的用例应该没问题.
aar*_*ing 15
我最近花了很多时间尝试做这样的事情并最终离开它.有很多角落案例.
如果您只想要调用堆栈的最低级别,则只需引用该def语句中使用的名称即可.这将通过词法闭包绑定到您想要的功能.
例如:
def recursive(*args, **kwargs):
me = recursive
Run Code Online (Sandbox Code Playgroud)
me现在将引用相关函数,无论调用函数的范围如何,只要它不在定义发生的范围内重新定义.有什么理由说这不起作用?
为了获得一个在调用堆栈中执行更高功能的函数,我想不出任何可以可靠地执行的操作.
buk*_*zor 15
这就是你要求的,尽可能接近我.在python版本2.4,2.6,3.0中测试.
#!/usr/bin/python
def getfunc():
from inspect import currentframe, getframeinfo
caller = currentframe().f_back
func_name = getframeinfo(caller)[2]
caller = caller.f_back
from pprint import pprint
func = caller.f_locals.get(
func_name, caller.f_globals.get(
func_name
)
)
return func
def main():
def inner1():
def inner2():
print("Current function is %s" % getfunc())
print("Current function is %s" % getfunc())
inner2()
print("Current function is %s" % getfunc())
inner1()
#entry point: parse arguments and call main()
if __name__ == "__main__":
main()
Run Code Online (Sandbox Code Playgroud)
输出:
Current function is <function main at 0x2aec09fe2ed8>
Current function is <function inner1 at 0x2aec09fe2f50>
Current function is <function inner2 at 0x2aec0a0635f0>
Run Code Online (Sandbox Code Playgroud)
kin*_*all 10
这是另一种可能性:装饰器隐式地将对被调用函数的引用作为第一个参数传递(类似于self绑定实例方法).你必须装饰你想要接收这样一个引用的每个函数,但正如他们所说的那样,"显式优于隐式".
当然,它具有装饰器的所有缺点:另一个函数调用会略微降低性能,并且包装函数的签名不再可见.
import functools
def gottahavethatfunc(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(func, *args, **kwargs)
return wrapper
Run Code Online (Sandbox Code Playgroud)
测试用例说明,即使您更改了函数绑定的名称,装饰函数仍会获得对自身的引用.这是因为您只是更改了包装函数的绑定.它还说明了它与lambda的使用.
@gottahavethatfunc
def quux(me):
return me
zoom = gottahavethatfunc(lambda me: me)
baz, quux = quux, None
print baz()
print zoom()
Run Code Online (Sandbox Code Playgroud)
当使用带有实例或类方法的装饰器时,该方法应该接受函数引用作为第一个参数,传统self作为第二个参数.
class Demo(object):
@gottahavethatfunc
def method(me, self):
return me
print Demo().method()
Run Code Online (Sandbox Code Playgroud)
装饰器依赖于一个闭包来保存对包装器中包装函数的引用.直接创建闭包可能实际上更干净,并且不会有额外函数调用的开销:
def my_func():
def my_func():
return my_func
return my_func
my_func = my_func()
Run Code Online (Sandbox Code Playgroud)
在内部函数中,名称my_func始终引用该函数; 它的值不依赖于可能更改的全局名称.然后我们将该函数"提升"到全局命名空间,替换对外部函数的引用.也在课堂上工作:
class K(object):
def my_method():
def my_method(self):
return my_method
return my_method
my_method = my_method()
Run Code Online (Sandbox Code Playgroud)
小智 6
我只是在每个函数的开头定义一个"关键字",它只是对函数实际名称的引用.我只是为任何函数执行此操作,如果它需要或不需要:
def test():
this=test
if not hasattr(this,'cnt'):
this.cnt=0
else:
this.cnt+=1
print this.cnt
Run Code Online (Sandbox Code Playgroud)
调用堆栈不保留对函数本身的引用 - 尽管运行帧作为对代码对象的引用,该代码对象是与给定函数关联的代码。
(函数是带有代码的对象,以及一些关于它们的环境的信息,例如闭包、名称、全局字典、文档字符串、默认参数等)。
因此,如果你正在运行一个常规函数,你最好在 globals 字典中使用它自己的名字来调用它自己,正如已经指出的那样。
如果您正在运行一些不能使用函数名称的动态或 lambda 代码,唯一的解决方案是重建另一个函数对象,该对象重新使用当前运行的代码对象并调用该新函数。
你会丢失一些东西,比如默认参数,并且可能很难让它与闭包一起工作(尽管它可以完成)。
我写了一篇关于这样做的博客文章 - 从内部调用匿名函数 - 我希望那里的代码可以帮助你:
http://metapython.blogspot.com/2010/11/recursive-lambda-functions.html
附带说明:避免使用 o inspect.stack ——它太慢了,因为每次调用它都会重建大量信息。更喜欢使用inspect.currentframe 来处理代码帧。
这听起来可能很复杂,但代码本身很短——我把它贴在下面。上面的帖子包含有关其工作原理的更多信息。
from inspect import currentframe
from types import FunctionType
lambda_cache = {}
def myself (*args, **kw):
caller_frame = currentframe(1)
code = caller_frame.f_code
if not code in lambda_cache:
lambda_cache[code] = FunctionType(code, caller_frame.f_globals)
return lambda_cache[code](*args, **kw)
if __name__ == "__main__":
print "Factorial of 5", (lambda n: n * myself(n - 1) if n > 1 else 1)(5)
Run Code Online (Sandbox Code Playgroud)
如果您确实需要原始函数本身,则可以使上面的“myself”函数在某些范围(如调用函数全局字典)上搜索函数对象,该函数对象的代码对象将与从框架中检索到的对象相匹配,而不是创建一个新函数。
小智 5
sys._getframe(0).f_code完全返回您需要的内容:正在执行的代码对象。有了代码对象,您可以使用codeobject .co_name 检索名称