如何在Python中不变地捕获闭包变量的CURRENT值? [英] How do I capture the CURRENT values of closure variables immutably in Python?

查看:76
本文介绍了如何在Python中不变地捕获闭包变量的CURRENT值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我愿意

def f():
    a = 1
    g = lambda: a
    a = 2
    return g
print(f()())

输出的值是 2 ,因为 a g之后发生了突变被构造。

如何获取 g 来捕获 a 静态地使以后的修改被忽略吗?

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆