如果我这样做
var xor = newF ^ oldF;
var added = newF & xor;
var removed = oldF & xor;
打印的值为def f():
a = 1
g = lambda: a
a = 2
return g
print(f()())
,因为构建2
后a
会发生变异。
如何让g
静态捕获g
的值,以便以后的修改被忽略?
答案 0 :(得分:1)
对于简单的情况,例如当代码很短并且我们没有很多变量要捕获时,我们可以创建一个临时的lambda并调用它:
a
这里的问题是代码很快就会变得难以阅读。
或者,我们可以将变量捕获为可选参数:
def f():
a = 1
g = (lambda a: lambda: a)(a)
a = 2
return g
问题 here 当然是我们可能不希望调用者能够指定此参数。
(而且代码的可读性也差一点。)
完全通用解决方案可能如下,但不捕获全局 :
def f():
a = 1
g = lambda a=a: a
a = 2
return g
我不试图在这里捕获全局变量的原因是语义可能会让人感到困惑 - 如果内部函数说def bind(*args, **kwargs):
# Use '*args' so that callers aren't limited in what names they can specify
func = args[0]
include_by_default = args[1] if len(args) > 1 else None
# if include_by_default == False, variables are NOT bound by default
if include_by_default == None: include_by_default = not kwargs
fc = func.__code__
fv = fc.co_freevars
q = func.__closure__
if q:
ql = []
i = 0
for qi in q:
fvi = fv[i]
ql.append((lambda v: (lambda: v).__closure__[0])(
kwargs.get(fvi, qi.cell_contents))
if include_by_default or fvi in kwargs
else qi)
i += 1
q = q.__class__(ql)
del ql
return func.__class__(fc, func.__globals__, func.__name__, func.__defaults__, q)
,那么确实 希望全局< / em> global x; x = 1
要修改,因此抑制此更改会很快使代码违反直觉。
但是,除此之外,我们可以简单地按如下方式使用它:
x
瞧,def f():
a = 1
g = bind(lambda: a)
a = 2
return g
print(f()())
即刻被捕获。如果我们只想捕获一些变量,我们可以这样做:
a
答案 1 :(得分:1)
在python3.8中,CellType
类已添加到types
中,这意味着您可以使用自定义闭包创建函数。这使我们能够编写一个函数,该函数将带有引用父框架的闭包的函数转换为具有静态值的闭包:
from types import FunctionType, CellType
def capture(f):
""" Returns a copy of the given function with its closure values and globals shallow-copied """
closure = tuple(CellType(cell.cell_contents) for cell in f.__closure__)
return FunctionType(f.__code__, f.__globals__.copy(), f.__name__, f.__defaults__, closure)
print([f() for f in [ lambda: i for i in range(5)]]) # Outputs [4, 4, 4, 4, 4]
print([f() for f in [capture(lambda: i) for i in range(5)]]) # Outputs [0, 1, 2, 3, 4]
capture
函数可以通过几种方式进行调整;当前的实现捕获所有全局变量和自由变量,并且浅层捕获。您可以决定对捕获的值进行深度复制,仅捕获自由变量,仅根据参数捕获特定的变量名称,等等。