Warn for every (nested) function with free variables (recursively)
我想做以下工作:
1 2 3 | for every nested function f anywhere in this_py_file: if has_free_variables(f): print warning |
为什么?主要是作为针对后期约束关闭的保险,如别处所述。即:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> def outer(): ... rr = [] ... for i in range(3): ... def inner(): ... print i ... rr.append(inner) ... return rr ... >>> for f in outer(): f() ... 2 2 2 >>> |
每当我收到关于自由变量的警告时,我要么添加一个显式异常(在极少数情况下,我希望这种行为),要么像这样修复它:
1 | ... def inner(i=i): |
然后,行为变得更像Java中的嵌套类(在内部类中使用的任何变量都必须是EDCOX1(0))。
(据我所知,除了解决后期绑定问题之外,这还将促进更好地使用内存,因为如果函数"关闭"了外部作用域中的某些变量,那么只要函数存在,外部作用域就不能被垃圾收集。对吗?)
我找不到任何方法来获取嵌套在其他函数中的函数。目前,我能想到的最好的方法是插入一个解析器,这看起来是一项非常多的工作。
考虑以下功能:
1 2 3 4 5 6 7 8 9 | def outer_func(): outer_var = 1 def inner_func(): inner_var = outer_var return inner_var outer_var += 1 return inner_func |
1 2 | outer_code = outer_func.__code__ inner_code = outer_code.co_consts[2] |
从这个代码对象,可以恢复自由变量:
1 | inner_code.co_freevars # ('outer_var',) |
您可以检查代码对象是否应使用:
1 | hasattr(inner_code, 'co_freevars') # True |
从文件中获取所有函数后,可能会出现以下情况:
1 2 3 4 | for func in function_list: for code in outer_func.__code__.co_consts[1:-1]: if hasattr(code, 'co_freevars'): assert len(code.co_freevars) == 0 |
了解更多内部工作的人可能会提供更好的解释或更简洁的解决方案。
我也想在Jython做这个。但是,接受的答案中显示的方法在那里不起作用,因为
当然,代码对象在某个地方,我们有源代码和完全访问权限,所以在合理的时间内找到一个简单的方法只是个问题。所以这是一个可行的方法。(抓紧你的座位。)
假设我们在模块
1 2 3 | def outer(): def inner(): print"Inner" |
首先直接获取外部函数的代码对象:
1 | code = mod.outer.__code__ |
在Jython中,这是一个
所以我们需要潜入Java。像这样的课程可以做到:
1 2 3 4 5 6 7 8 9 10 | import java.lang.reflect.Field; public class Getter { public static Object getFuncs(Object o) throws NoSuchFieldException, IllegalAccessException { Field f = o.getClass().getDeclaredField("funcs"); f.setAccessible(true); return f.get(o); } } |
回到Jython:
1 2 3 4 | >>> import Getter >>> funcs = Getter.getFuncs(mod.outer.__code__) >>> funcs mod$py@1bfa3a2 |
现在,这个
1 | >>> fields = funcs.class.getDeclaredFields() |
在我的例子中,与嵌套函数对应的代码对象恰好是最后一个:
1 2 3 | >>> flast = fields[-1] >>> flast static final org.python.core.PyCode mod$py.inner$24 |
要获取感兴趣的代码对象:
1 2 3 4 5 | >>> flast.setAccessible(True) >>> inner_code = flast.get(None) #"None" because it's a static field. >>> dir(inner_code) co_argcount co_filename co_flags co_freevars co_name co_nlocals co_varnames co_cellvars co_firstlineno |
剩下的和接受的答案一样,即检查
这种方法的一个好处是,您可以精确地枚举源代码文件中任意位置声明的所有代码对象:函数、方法、生成器,无论它们是否嵌套在任何对象之下或彼此之下。他们再也没有地方藏身了。
要"保留"嵌套函数(即使您正在重写它们),您必须使用
1 2 3 4 5 6 7 8 9 | def outer(): rr = [] for i in range(3): eval("def inner"+str(i)+"""(): print"""+str(i)) rr.append(eval("inner"+str(i))) return rr for f in outer(): f() |
印刷品
1 2 3 | 1 2 3 |
你需要使用
https://pymotw.com/2/copy/复制/