52

クロージャー内の変数の 1 つのバインドされた値を変更する方法はありますか? 例を見て理解を深めてください。

def foo():
    var_a = 2
    var_b = 3

    def _closure(x):
        return var_a + var_b + x

    return _closure


localClosure = foo()

# Local closure is now "return 2 + 3 + x"
a = localClosure(1) # 2 + 3 + 1 == 6

# DO SOME MAGIC HERE TO TURN "var_a" of the closure into 0
# ...but what magic? Is this even possible?

# Local closure is now "return 0 + 3 + x"
b = localClosure(1) # 0 + 3 +1 == 4
4

9 に答える 9

48

nonlocalの魔法のおかげで、Python 3 ではかなり可能です。

def foo():
        var_a = 2
        var_b = 3

        def _closure(x, magic = None):
                nonlocal var_a
                if magic is not None:
                        var_a = magic

                return var_a + var_b + x

        return _closure


localClosure = foo()

# Local closure is now "return 2 + 3 + x"
a = localClosure(1) # 2 + 3 + 1 == 6
print(a)

# DO SOME MAGIC HERE TO TURN "var_a" of the closure into 0
localClosure(0, 0)

# Local closure is now "return 0 + 3 + x"
b = localClosure(1) # 0 + 3 +1 == 4
print(b)
于 2008-12-25T03:13:56.153 に答える
22

Pythonでそれを行う方法はないと思います。クロージャが定義されると、囲んでいるスコープ内の変数の現在の状態がキャプチャされ、(クロージャの外部から) 直接参照可能な名前がなくなります。もう一度呼び出すfoo()と、新しいクロージャは、囲んでいるスコープとは異なる変数のセットを持つことになります。

あなたの簡単な例では、クラスを使用する方が良いかもしれません:

class foo:
        def __init__(self):
                self.var_a = 2
                self.var_b = 3

        def __call__(self, x):
                return self.var_a + self.var_b + x

localClosure = foo()

# Local closure is now "return 2 + 3 + x"
a = localClosure(1) # 2 + 3 + 1 == 6

# DO SOME MAGIC HERE TO TURN "var_a" of the closure into 0
# ...but what magic? Is this even possible?
localClosure.var_a = 0

# Local closure is now "return 0 + 3 + x"
b = localClosure(1) # 0 + 3 +1 == 4

この手法を使用する場合、localClosure実際にはクロージャーではないため、名前を使用しなくなります。ただし、1 つと同じように機能します。

于 2008-12-24T23:58:23.690 に答える
11

Python 2.1のカスタム関数属性(独自の関数内から便利にアクセスできる)を使用しているため、Gregの代替回答の回答を見つけました。

def foo():
    var_b = 3

    def _closure(x):
        return _closure.var_a + var_b + x

    _closure.func_dict['var_a'] = 2
    return _closure


localClosure = foo()

# Local closure is now "return 2 + 3 + x"
a = localClosure(1) # 2 + 3 + 1 == 6

# DO SOME MAGIC HERE TO TURN "var_a" of the closure into 0
# ...but what magic? Is this even possible?
# apparently, it is
localClosure.var_a = 0

# Local closure is now "return 0 + 3 + x"
b = localClosure(1) # 0 + 3 +1 == 4

完全を期すために投稿すると思いました。とにかく乾杯。

于 2008-12-25T00:08:52.250 に答える
10

以下のことを行いました。ここの他のソリューションよりも簡単だと思います。

class State:
    pass

def foo():
    st = State()
    st.var_a = 2
    st.var_b = 3

    def _closure(x):
        return st.var_a + st.var_b + x
    def _set_a(a):
        st.var_a = a

    return _closure, _set_a


localClosure, localSetA = foo()

# Local closure is now "return 2 + 3 + x"
a = localClosure(1) # 2 + 3 + 1 == 6

# DO SOME MAGIC HERE TO TURN "var_a" of the closure into 0
localSetA(0)

# Local closure is now "return 0 + 3 + x"
b = localClosure(1) # 0 + 3 +1 == 4

print a, b
于 2010-08-03T17:44:46.333 に答える
4

単純な変数の代わりに 1 項目のリストを使用して、同様の制限を回避しました。見苦しいですが、リスト項目の変更はインタープリターによってバインド操作として扱われないため、機能します。

例えば:

def my_function()
    max_value = [0]

    def callback (data)

        if (data.val > max_value[0]):
            max_value[0] = data.val

        # more code here
        # . . . 

    results = some_function (callback)

    store_max (max_value[0])
于 2011-01-01T23:31:30.040 に答える
0

関数 foo の var_a と var_b 引数を作成しないのはなぜですか?

def foo(var_a = 2, var_b = 3):
    def _closure(x):
        return var_a + var_b + x
    return _closure

localClosure = foo() # uses default arguments 2, 3
print localClosure(1) # 2 + 3 + 1 = 6

localClosure = foo(0, 3)
print localClosure(1) # 0 + 3 + 1 = 4
于 2008-12-24T23:53:01.050 に答える
0
def foo():
    var_a = 2
    var_b = 3

    def _closure(x):
            return var_a + var_b + x

    return _closure

def bar():
        var_a = [2]
        var_b = [3]

        def _closure(x):
                return var_a[0] + var_b[0] + x


        def _magic(y):
            var_a[0] = y

        return _closure, _magic

localClosureFoo = foo()
a = localClosureFoo(1)
print a



localClosureBar, localClosureBarMAGIC = bar()
b = localClosureBar(1)
print b
localClosureBarMAGIC(0)
b = localClosureBar(1)
print b
于 2012-01-09T17:25:59.590 に答える
0

質問されたものとは少し異なりますが、次のことができます。

def f():
    a = 1
    b = 2
    def g(x, a=a, b=b):
        return a + b + x
    return g

h = f()
print(h(0))
print(h(0,2,3))
print(h(0))

必要に応じてオーバーライドできるように、クロージャーをデフォルトにします。

于 2020-04-02T12:59:03.073 に答える