7

以下は、python クロージャに関する誰かのブログから得た例です。Python 2.7 で実行すると、予想とは異なる出力が得られます。

flist = []

for i in xrange(3):
    def func(x):
        return x*i
    flist.append(func)

for f in flist:
    print f(2)

私の予想される出力は: 0, 2, 4
しかし、出力は: 4, 4, 4
です 誰か説明してくれる人はいますか?
前もって感謝します。

4

3 に答える 3

17

Python ではループはスコープを導入しないため、3 つの関数はすべて同じi変数を閉じ、ループが終了した後に最終的な値である 2 を参照します。

Python でクロージャを使用している私が話をしたほぼ全員が、これに悩まされているようです。必然的に、外側の関数は変更できますiが、内側の関数は変更できません (これはi、Python の構文規則に基づいてクロージャではなくローカルを作成するためです)。

これに対処するには、次の 2 つの方法があります。

# avoid closures and use default args which copy on function definition
for i in xrange(3):
    def func(x, i=i):
        return x*i
    flist.append(func)

# or introduce an extra scope to close the value you want to keep around:
for i in xrange(3):
    def makefunc(i):
        def func(x):
            return x*i
        return func
    flist.append(makefunc(i))

# the second can be simplified to use a single makefunc():
def makefunc(i):
    def func(x):
        return x*i
    return func
for i in xrange(3):
    flist.append(makefunc(i))

# if your inner function is simple enough, lambda works as well for either option:
for i in xrange(3):
    flist.append(lambda x, i=i: x*i)

def makefunc(i):
    return lambda x: x*i
for i in xrange(3):
    flist.append(makefunc(i))
于 2012-07-10T07:33:45.580 に答える
4

クロージャーを作成していません。i最初のループの後に 2 に等しいグローバル変数にそれぞれアクセスする関数のリストを生成しています。したがって、関数呼び出しごとに 2 * 2 になります。

于 2012-07-10T07:31:32.290 に答える
1

各関数はグローバルにアクセスしiます。

functools.partialが救助に来ます:

from functools import partial
flist = []

for i in xrange(3):
    def func(x, multiplier=None):
        return x * multiplier
    flist.append(partial(func, multiplier=i))
于 2012-07-10T07:35:41.810 に答える