1. ホーム
  2. python

[解決済み] レキシカルクロージャーの仕組みを教えてください。

2022-04-27 06:01:32

質問

Javascriptのコードでレキシカルクロージャの問題を調査していたところ、Pythonでこの問題に行き当たりました。

flist = []

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

for f in flist:
    print f(2)

この例では lambda . 4 4 4"と表示されるのは驚きです。私は"0 2 4"を期待します。

この同等のPerlのコードでは、正しく動作します。

my @flist = ();

foreach my $i (0 .. 2)
{
    push(@flist, sub {$i * $_[0]});
}

foreach my $f (@flist)
{
    print $f->(2), "\n";
}

"0 2 4"が印刷されます。

この違いを教えてください。


更新しました。

問題点 ではありません。 i はグローバルである。これは、同じ動作を表示します。

flist = []

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

outer()
#~ print i   # commented because it causes an error

for f in flist:
    print f(2)

コメント行にあるように i はその時点では不明です。それでも "4 4 4" と表示されます。

どのように解決するのですか?

Pythonは実際に定義通りの動作をしています。 3つの独立した関数 が作成されますが、それぞれ で定義された環境のクロージャになります。 - この場合、グローバル環境(ループが他の関数内に配置されている場合は、外側の関数の環境)です。これがまさに問題なのですが、この環境では iはミュータント であり、クロージャはすべて は同じ i を参照しています。 .

私が思いつく最善の解決策は、関数クリータを作成して、その関数クリータから あれ 代わりに これによって 異なる環境 は、作成された各関数に対して 異なるi をそれぞれ作成します。

flist = []

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

for f in flist:
    print f(2)

これは、副作用と関数型プログラミングを混ぜると起こることです。