Açıklama
Buradaki sorun i
, işlev f
oluşturulduğunda değerinin kaydedilmemesidir . Aksine, f
değerini arar i
o zaman denir .
Düşünürseniz, bu davranış çok mantıklı. Aslında, işlevlerin çalışmasının tek mantıklı yolu budur. Küresel bir değişkene erişen bir işleve sahip olduğunuzu düşünün, örneğin:
global_var = 'foo'
def my_function():
print(global_var)
global_var = 'bar'
my_function()
Bu kodu okuduğunuzda, tabii ki - "foo" değil "bar" yazdırmasını beklersiniz, çünkü global_var
işlev bildirildikten sonra değerinin değeri değişmiştir. Aynı şey kendi kodunuzda da oluyor: Aradığınızda f
değeri i
değişmiş ve olarak ayarlanmıştır 2
.
Çözüm
Aslında bu sorunu çözmenin birçok yolu var. İşte birkaç seçenek:
i
Varsayılan bağımsız değişken olarak kullanarak erken bağlamayı zorla
Kapanış değişkenlerinden (gibi i
) farklı olarak , varsayılan argümanlar işlev tanımlandığında hemen değerlendirilir:
for i in range(3):
def f(i=i):
return i
functions.append(f)
Bunun nasıl / neden çalıştığına dair biraz fikir vermek gerekirse: Bir işlevin varsayılan argümanları işlevin bir niteliği olarak saklanır; böylece geçerli değeri i
anlık olarak alınır ve kaydedilir.
>>> i = 0
>>> def f(i=i):
... pass
>>> f.__defaults__
(0,)
>>>
>>> i = 5
>>> f.__defaults__
(0,)
i
Bir kapanışta mevcut değerini yakalamak için bir işlev fabrikası kullanın
Sorununuzun kökü i
, değişebilen bir değişkendir. Asla değişmemesi garanti edilen başka bir değişken oluşturarak bu sorunu aşabiliriz - ve bunu yapmanın en kolay yolu kapanıştır :
def f_factory(i):
def f():
return i
return f
for i in range(3):
f = f_factory(i)
functions.append(f)
Kullanım functools.partial
mevcut değerini bağlamak i
içinf
functools.partial
mevcut bir işleve argümanlar eklemenizi sağlar. Bir bakıma o da bir tür işlev fabrikası.
import functools
def f(i):
return i
for i in range(3):
f_with_i = functools.partial(f, i)
functions.append(f_with_i)
Uyarı: Bu çözümler sadece iş eğer atamak değişkene yeni bir değer. Eğer varsa değiştirmek değişkeninde saklanan nesneyi, yine aynı sorunla karşılaştığınızda edeceğiz:
>>> i = []
>>> def f(i=i):
... print('i =', i)
...
>>> i.append(5)
>>> f()
i = [5]
i
Varsayılan bir argüman haline getirmemize rağmen hala nasıl değiştiğine dikkat edin ! Kodunuz Eğer mutasyon i
, o zaman bir bağlama gerekir kopyasını ait i
fonksiyonunuza yüzden mi:
def f(i=i.copy()):
f = f_factory(i.copy())
f_with_i = functools.partial(f, i.copy())