Python代码获取当前函数到一个variables?
我怎样才能得到一个variables,包含当前正在执行的Python函数? 我不想要这个函数的名字 我知道我可以使用inspect.stack
获取当前的函数名称。 我想要实际的可调用对象。 这可以做到不使用inspect.stack
检索函数的名称,然后eval
名称来获取可调用对象?
编辑:我有一个这样做的理由,但它甚至不是一个很好的。 我使用plac来parsing命令行参数。 通过执行plac.call(main)
使用它,它从“main”的函数签名中生成一个ArgumentParser对象。 在“main”里面,如果参数有问题,我想退出一个包含来自ArgumentParser对象的帮助文本的错误消息,这意味着我需要通过调用plac.parser_from(main).print_help()
来直接访问这个对象。 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])
但是这个实现依赖于具有名字的函数,我想这不是太繁重。 我永远不会做plac.call(lambda ...)
。
从长远来看,让plac的作者实现一个print_help方法来打印最近使用plac调用的函数的帮助文本或类似的东西可能会更有用。
堆栈框架告诉我们我们在哪个代码对象。如果我们可以find一个函数对象,该对象在其func_code
属性中引用该代码对象,我们已经find了这个函数。
幸运的是,我们可以向垃圾收集器询问哪些对象持有对我们的代码对象的引用,并筛选这些对象,而不必遍历Python世界中的每个活动对象。 通常只有less数对代码对象的引用。
现在,函数可以共享代码对象,在从函数返回函数的情况下,也就是闭包。 当有多个函数使用给定的代码对象时,我们无法确定它是哪个函数,所以我们返回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, "func_code", None) is code: if getattr(func, "func_globals", None) is globs: funcs.append(func) if len(funcs) > 1: return None return funcs[0] if funcs else None
一些testing用例:
def foo(): return giveupthefunc() zed = lambda: giveupthefunc() bar, foo = foo, None print bar() print zed()
我不确定这个的性能特点,但是我认为这对您的用例应该没问题。
这就是你所要求的,尽可能靠近我。 testing在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()
输出:
Current function is <function main at 0x2aec09fe2ed8> Current function is <function inner1 at 0x2aec09fe2f50> Current function is <function inner2 at 0x2aec0a0635f0>
我最近花了很多时间试图做这样的事情,最终走了离开。 有很多angular落案例。
如果您只想调用堆栈的最低级别,则可以引用def
语句中使用的名称。 这将被绑定到你想通过词法closures的function。
例如:
def recursive(*args, **kwargs): me = recursive
me
现在将引用所讨论的函数,而不pipe函数调用的范围是什么,只要不在定义的范围内重新定义。 有没有什么理由为什么这不起作用?
为了得到一个在调用堆栈上执行得更高的函数,我想不出任何可以做到的事情。
这里有另一种可能性:一个装饰器隐式地将引用传递给被调用的函数作为第一个参数(类似于绑定实例方法中的self
)。 你必须修饰你想要接受这样一个引用的每个函数,但是“显式比隐式更好”,正如他们所说的那样。
当然,它具有装饰器的所有缺点:另一个函数调用会稍微降低性能,并且封装函数的签名不再可见。
import functools def gottahavethatfunc(func): @functools.wraps(func) def wrapper(*args, **kwargs): return func(func, *args, **kwargs) return wrapper
testing用例说明,即使您更改了绑定函数的名称,装饰后的函数仍然会获取对自身的引用。 这是因为你只是改变包装函数的绑定。 它也说明了它与lambda的使用。
@gottahavethatfunc def quux(me): return me zoom = gottahavethatfunc(lambda me: me) baz, quux = quux, None print baz() print zoom()
当使用实例或类方法使用此装饰器时,该方法应该接受函数引用作为第一个参数,传统self
作为第二个参数。
class Demo(object): @gottahavethatfunc def method(me, self): return me print Demo().method()
装饰器依赖于一个闭包来保存对包装函数中的包装函数的引用。 直接创build闭包实际上可能更清晰,并且不会有额外函数调用的开销:
def my_func(): def my_func(): return my_func return my_func my_func = my_func()
在内部函数中,名称my_func
始终指向该函数; 它的价值不取决于可能被改变的全球名称。 然后,我们只是将该函数“提升”到全局名称空间,将引用replace为外部函数。 也在一个class上工作:
class K(object): def my_method(): def my_method(self): return my_method return my_method my_method = my_method()
我只是在每个函数的开头定义一个“关键字”,它只是对函数实际名称的引用。 我只是为了任何function而做这个,如果需要的话:
def test(): this=test if not hasattr(this,'cnt'): this.cnt=0 else: this.cnt+=1 print this.cnt
调用堆栈并不保留对函数本身的引用 – 虽然运行框架作为代码对象的引用,即与给定函数关联的代码。
(函数是带有代码的对象,以及有关它们的环境的一些信息,例如闭包,名称,全局variables字典,文档string,缺省参数等等)。
因此,如果您正在运行常规函数,那么您最好在globals词典上使用自己的名称来调用自己,正如已经指出的那样。
如果您正在运行一些dynamic的或lambda代码(其中不能使用函数名称),唯一的解决方法是重新构build另一个函数对象,它将重新使用当前正在运行的代码对象并调用该新函数。
你会失去一些东西,比如默认的参数,并且可能很难使用闭包(虽然可以完成)。
我已经写了一篇关于这样做的博客文章 – 从内部调用匿名函数 – 我希望那里的代码可以帮助你:
http://metapython.blogspot.com/2010/11/recursive-lambda-functions.html
注意:避免使用inspect.stack – 它太慢了,因为它每次调用时都会重build很多信息。 宁愿使用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)
如果你真的需要原始的函数本身,可以使用上面的“我自己”函数在一些作用域(比如调用函数全局字典)上search一个函数对象,该对象与代码对象匹配的函数对象,而不是创造一个新的function。
sys._getframe(0).f_code返回你所需要的:正在执行的codeobject。 有一个代码对象,你可以通过codeobject .co_name来获取一个名字
好了,在再次阅读问题和意见后,我认为这是一个体面的testing案例:
def foo(n): """ print numbers from 0 to n """ if n: foo(n-1) print n g = foo # assign name 'g' to function object foo = None # clobber name 'foo' which refers to function object g(10) # dies with TypeError because function object tries to call NoneType
我试着通过使用装饰器临时摧毁全局命名空间并将函数对象重新分配给函数的原始名称来解决它:
def selfbind(f): """ Ensures that f's original function name is always defined as f when f is executed """ oname = f.__name__ def g(*args, **kwargs): # Clobber global namespace had_key = None if globals().has_key(oname): had_key = True key = globals()[oname] globals()[oname] = g # Run function in modified environment result = f(*args, **kwargs) # Restore global namespace if had_key: globals()[oname] = key else: del globals()[oname] return result return g @selfbind def foo(n): if n: foo(n-1) print n g = foo # assign name 'g' to function object foo = 2 # calling 'foo' now fails since foo is an int g(10) # print from 0..10, even though foo is now an int print foo # prints 2 (the new value of Foo)
我确定我没有想过所有的用例。 我看到的最大的问题是函数对象故意改变了自己名字指向的内容(被修饰器覆盖的操作),但是只要recursion函数没有在中间重新定义自己的名字recursion。
仍然不确定我是否需要这样做,但思考是有趣的。
这里是get_referrers()答案的变体(Python 3.5.1),它试图区分使用相同代码对象的闭包:
import functools import gc import inspect def get_func(): frame = inspect.currentframe().f_back code = frame.f_code return [ referer for referer in gc.get_referrers(code) if getattr(referer, "__code__", None) is code and set(inspect.getclosurevars(referer).nonlocals.items()) <= set(frame.f_locals.items())][0] def f1(x): def f2(y): print(get_func()) return x + y return f2 f_var1 = f1(1) f_var2 = f1(2) f_var1(3) f_var2(3) def f3(): print(get_func()) f3() def wrapper(func): functools.wraps(func) def wrapped(*args, **kwargs): return func(*args, **kwargs) return wrapped @wrapper def f4(): print(get_func()) f4() f5 = lambda: get_func() print(f5())
更正我以前的答案,因为子字典检查已经在dict_items上调用了“<=”,如果有dict-values是自己的字典,那么额外的set()调用会导致问题:
import gc import inspect def get_func(): frame = inspect.currentframe().f_back code = frame.f_code return [ referer for referer in gc.get_referrers(code) if getattr(referer, "__code__", None) is code and inspect.getclosurevars(referer).nonlocals.items() <= frame.f_locals.items()][0]