警告带有自由变量的每个(嵌套)函数(递归)
Warn for every (nested) function with free variables (recursively)
我想做以下事情:
for every nested function f anywhere in this_py_file:
if has_free_variables(f):
print warning
为什么?主要是作为 described elsewhere 的后期绑定闭包陷阱的保险。即:
>>> 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
>>>
每当我收到有关自由变量的警告时,我都会添加一个显式异常(在极少数情况下我会想要这种行为)或像这样修复它:
... def inner(i=i):
然后行为变得更像 Java 中的嵌套 classes(其中要在内部 class 中使用的任何变量都必须是 final
)。
(据我所知,除了解决后期绑定的问题,这也会促进更好地利用内存,因为如果一个函数"closes over"一些变量在外部范围内,那么外部范围就不能只要函数存在,就会被垃圾收集。对吧?)
我找不到任何方法来获取嵌套在其他函数中的函数。目前,我能想到的最好的方法是检测解析器,这看起来工作量很大。
对于 "get a hold" 的嵌套函数(即使您正在覆盖它们),您必须使用 eval
在每个声明中创建变量定义名称。
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
您需要 import copy
并使用 rr.append(copy.copy(inner))
考虑以下函数:
def outer_func():
outer_var = 1
def inner_func():
inner_var = outer_var
return inner_var
outer_var += 1
return inner_func
__code__
对象可用于恢复内部函数的代码对象:
outer_code = outer_func.__code__
inner_code = outer_code.co_consts[2]
从这个代码对象中,可以恢复自由变量:
inner_code.co_freevars # ('outer_var',)
您可以检查是否应检查代码对象:
hasattr(inner_code, 'co_freevars') # True
从文件中获取所有函数后,这可能类似于:
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 做这个。但是接受的答案中显示的方式在那里不起作用,因为 co_consts
在代码对象上不可用。 (此外,似乎没有任何其他方法可以查询代码对象以获取嵌套函数的代码对象。)
当然,代码对象就在某处,我们拥有源代码和完全访问权限,所以只需要在合理的时间内找到一种简单的方法即可。所以这是一种可行的方法。 (请坐好。)
假设我们在模块 mod
:
中有这样的代码
def outer():
def inner():
print "Inner"
先直接获取外层函数的代码对象:
code = mod.outer.__code__
在 Jython 中,这是 PyTableCode
的实例,通过阅读源代码,我们发现实际功能是在 Java-class 中实现的您给定的脚本,由代码对象的 funcs
字段引用。 (所有这些由脚本组成的 classes 都是 PyFunctionTable
的子 classes,因此这是 funcs
的声明类型。)这在 Jython 中是不可见的,由于神奇的机器,这是设计师的说法,您访问这些东西的风险由您自己承担。
所以我们需要深入研究一下 Java。像这样的 class 就可以了:
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:
>>> import Getter
>>> funcs = Getter.getFuncs(mod.outer.__code__)
>>> funcs
mod$py@1bfa3a2
现在,这个 funcs
对象具有在 Jython 脚本中任意位置声明的所有函数(那些任意嵌套在 classes 中的函数,等等)。此外,它还有保存相应代码对象的字段。
>>> fields = funcs.class.getDeclaredFields()
在我的例子中,嵌套函数对应的代码对象恰好是最后一个:
>>> flast = fields[-1]
>>> flast
static final org.python.core.PyCode mod$py.inner
获取感兴趣的代码对象:
>>> 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
其余与接受的答案相同,即检查 co_freevars
,(与 co_consts
不同,在 Jython 中有)。
这种方法的一个好处是,您可以准确枚举在源代码文件中任何位置声明的所有代码对象:函数、方法、生成器,无论它们是否嵌套在任何事物之下或彼此之下。他们无处可藏。
我想做以下事情:
for every nested function f anywhere in this_py_file:
if has_free_variables(f):
print warning
为什么?主要是作为 described elsewhere 的后期绑定闭包陷阱的保险。即:
>>> 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
>>>
每当我收到有关自由变量的警告时,我都会添加一个显式异常(在极少数情况下我会想要这种行为)或像这样修复它:
... def inner(i=i):
然后行为变得更像 Java 中的嵌套 classes(其中要在内部 class 中使用的任何变量都必须是 final
)。
(据我所知,除了解决后期绑定的问题,这也会促进更好地利用内存,因为如果一个函数"closes over"一些变量在外部范围内,那么外部范围就不能只要函数存在,就会被垃圾收集。对吧?)
我找不到任何方法来获取嵌套在其他函数中的函数。目前,我能想到的最好的方法是检测解析器,这看起来工作量很大。
对于 "get a hold" 的嵌套函数(即使您正在覆盖它们),您必须使用 eval
在每个声明中创建变量定义名称。
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
您需要 import copy
并使用 rr.append(copy.copy(inner))
考虑以下函数:
def outer_func():
outer_var = 1
def inner_func():
inner_var = outer_var
return inner_var
outer_var += 1
return inner_func
__code__
对象可用于恢复内部函数的代码对象:
outer_code = outer_func.__code__
inner_code = outer_code.co_consts[2]
从这个代码对象中,可以恢复自由变量:
inner_code.co_freevars # ('outer_var',)
您可以检查是否应检查代码对象:
hasattr(inner_code, 'co_freevars') # True
从文件中获取所有函数后,这可能类似于:
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 做这个。但是接受的答案中显示的方式在那里不起作用,因为 co_consts
在代码对象上不可用。 (此外,似乎没有任何其他方法可以查询代码对象以获取嵌套函数的代码对象。)
当然,代码对象就在某处,我们拥有源代码和完全访问权限,所以只需要在合理的时间内找到一种简单的方法即可。所以这是一种可行的方法。 (请坐好。)
假设我们在模块 mod
:
def outer():
def inner():
print "Inner"
先直接获取外层函数的代码对象:
code = mod.outer.__code__
在 Jython 中,这是 PyTableCode
的实例,通过阅读源代码,我们发现实际功能是在 Java-class 中实现的您给定的脚本,由代码对象的 funcs
字段引用。 (所有这些由脚本组成的 classes 都是 PyFunctionTable
的子 classes,因此这是 funcs
的声明类型。)这在 Jython 中是不可见的,由于神奇的机器,这是设计师的说法,您访问这些东西的风险由您自己承担。
所以我们需要深入研究一下 Java。像这样的 class 就可以了:
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:
>>> import Getter
>>> funcs = Getter.getFuncs(mod.outer.__code__)
>>> funcs
mod$py@1bfa3a2
现在,这个 funcs
对象具有在 Jython 脚本中任意位置声明的所有函数(那些任意嵌套在 classes 中的函数,等等)。此外,它还有保存相应代码对象的字段。
>>> fields = funcs.class.getDeclaredFields()
在我的例子中,嵌套函数对应的代码对象恰好是最后一个:
>>> flast = fields[-1]
>>> flast
static final org.python.core.PyCode mod$py.inner
获取感兴趣的代码对象:
>>> 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
其余与接受的答案相同,即检查 co_freevars
,(与 co_consts
不同,在 Jython 中有)。
这种方法的一个好处是,您可以准确枚举在源代码文件中任何位置声明的所有代码对象:函数、方法、生成器,无论它们是否嵌套在任何事物之下或彼此之下。他们无处可藏。