如何在Python中不变地捕获闭包变量的CURRENT值? [英] How do I capture the CURRENT values of closure variables immutably in Python?
问题描述
如果我愿意
def f():
a = 1
g = lambda: a
a = 2
return g
print(f()())
输出的值是 2
,因为 a
在 g之后发生了突变
被构造。
如何获取 g
来捕获 a $ c $的值c>静态地使以后的修改被忽略吗?
The value that is printed is 2
, because a
is mutated after g
is constructed.
How do I get g
to capture the value of a
statically so that later modifications are ignored?
推荐答案
对于简单的情况,例如代码短而我们不如果有很多变量需要捕获,我们可以创建一个临时的lambda并命名为:
For simple cases, such as when the code is short and we don't have many variables to capture, we can create a temporary lambda and call it:
def f():
a = 1
g = (lambda a: lambda: a)(a)
a = 2
return g
这里的问题是代码可能很快变得难以阅读。
The issue here is that the code can quickly become harder to read.
或者,我们可以将变量捕获为可选参数:
Alternatively, we can capture the variable as an optional argument:
def f():
a = 1
g = lambda a=a: a
a = 2
return g
问题此处当然是
(而且代码的可读性也会降低。)
The issue here is, of course, that we might not want the caller to be able to specify this parameter.
(And the code can be a little less readable, too.)
以下为完全通用的解决方案,除了它不捕获 globals :
A fully general solution might be the following, except that it does not capture globals:
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)
我之所以不尝试捕获全局变量,是因为语义可以令人困惑-如果内部函数显示 global x; x = 1
,当然确实想要修改 global x
,所以
The reason I do not attempt to capture globals here is that the semantics can get confusing -- if an inner function says global x; x = 1
, it certainly does want the global x
to be modified, so suppressing this change would quickly make the code very counterintuitive.
但是,除非如此,否则我们可以按如下方式简单地使用它:
However, barring that, we would be able to simply use it as follows:
def f():
a = 1
g = bind(lambda: a)
a = 2
return g
print(f()())
还有voilà,
被立即捕获。而且,如果我们只想捕获一些变量,则可以执行以下操作:
And voilà, a
is instantly captured. And if we want to only capture some variables, we can do:
def f():
a = 1
b = 2
g = bind(lambda: a + b, b=5) # capture 'b' as 5; leave 'a' free
a = 2
b = 3
return g
print(f()())
这篇关于如何在Python中不变地捕获闭包变量的CURRENT值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!