2012-07-10 11 views
7

Aşağıda, bir kişinin blog'undan python kapama hakkında bir örnek var. Python 2.7'de çalıştırıyorum ve beklediğimden farklı bir çıktı alıyorum. 0, 2, 4
Fakat çıkışı:Python kapatma hakkında

flist = [] 

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

for f in flist: 
    print f(2) 

Benim beklenen çıkışı 4, 4, 4
herkes açıklamaya yardımcı olabilir var mı?
Önceden teşekkür ederiz.

+3

olası yinelenen (http://stackoverflow.com/questions/233673/lexical-closures-in-python) – BrenBarn

cevap

16

Döngüler aynı i değişken üzerinde öylesine her üç fonksiyon yakın Python kapsamını tanıtmak yoktur ve bir döngü bittikten sonra nihai değerinin, sevk edecektir 2.

Neredeyse herkes sanki görünüyor ben Python'daki kapanışları kimin kullandığını konuşmak bu tarafından ısırıldı. Sonuç olarak, dış fonksiyonun i değiştirilebileceği, ancak iç fonksiyonun (bu, Python'un sözdizimsel kurallarına dayanan bir kapanma yerine i bir yerel olmasını sağlayamayacağıdır) olmasıdır.

# 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)) 
+0

+1 Harika bir açıklama ve çözüm. – jamylak

+0

Diğer okuyucular için not: Python 3, 'func' işlevinin 'i 'değerini değiştirmesine izin veren' non' işlevli 'anahtar sözcüğünü ekler ve bu da diğerlerini etkileyecektir. Bu durumda kullanışlı değil, ancak birkaç iç işleviniz varsa kullanışlıdır. –

+0

Sonuncusu lambda ile biraz daha basitleştirebilirsiniz, ancak bu, func() 'in ne yapabileceğini kısıtlar. – Dubslow

4

Kapatma oluşturmuyorsunuz. İlk döngüden sonra 2'ye eşit olan i genel değişkenine erişen işlevlerin bir listesini oluşturuyorsunuz. Böylece her bir işlev çağrısı için 2 * 2 ile biter.

+0

için teşekkür ederiz Cevap. Beklenen çıktımı almak için kapatma oluşturmak istiyorsam, kodu nasıl değiştirebilirim? Bana biraz öneri verebilir misiniz? –

+0

@ Alex.Zhang: Peki, yaşadığınız davranışın neden beklediğinizle aynı olmadığına dair bir açıklama isteyin. Çözüm için http://stackoverflow.com/a/11408601/21945 adresine bakın. – mhawke

1

Her işlev küresel i erişir:

bu sorunu çözmek için iki yol vardır.

functools.partial

kurtarmaya gelir: [Python Sözcük kapanışları] arasında

from functools import partial 
flist = [] 

for i in xrange(3): 
    def func(x, multiplier=None): 
     return x * multiplier 
    flist.append(partial(func, multiplier=i))