Yuvalanmış python işlevleri neden kapatma olarak adlandırılmıyor?

Python'da iç içe geçmiş işlevleri gördüm ve kullandım ve kapatma tanımına uydular. Öyleyse neden closures yerine nested functions deniyor?

İç içe işlevler dış dünya tarafından kullanılmadığı için kapalı değil mi?

GÜNCELLEME: Kapanmayı okudum ve Python'la ilgili bu kavramı merak ediyordum. Aşağıdaki yorumda biri tarafından belirtilen bir makaleyi aradım ve buldum, ancak bu makaledeki açıklamayı tam olarak anlayamadım, bu yüzden bu soruyu soruyorum.

204
26 окт. Srikar Appalaraju tarafından 26 ekim ayarla . 2010-10-26 06:11 '10, 06:11 2010-10-26 06:11
@ 7 cevaplar

Bir fonksiyon, yürütmesini tamamlayan kapanış alanından bir yerel değişkene erişime sahip olduğunda kapanma gerçekleşir.

 def make_printer(msg): def printer(): print msg return printer printer = make_printer('Foo!') printer() 

make_printer , printer işlevi için derlenmiş kodun sabit, yerel olarak da msg değerinin make_printer yığına yeni bir çerçeve make_printer . Daha sonra bir fonksiyon yaratır ve döndürür. printer işlevi msg değişkenine make_printer , make_printer işlevinin döndürülmesinden sonra bellekte depolanır.

Öyleyse, iç içe geçmiş işlevleriniz değilse

  • yayılma alanları için yerel olan erişim değişkenleri
  • bu alanın dışına çıktıklarında bunu yapın,

onlar kapanış değil.

İşte kapatılmayan iç içe geçmiş bir fonksiyon örneği.

 def make_printer(msg): def printer(msg=msg): print msg return printer printer = make_printer("Foo!") printer() #Output: Foo! 

Burada değeri parametrenin varsayılan değerine bağlarız. Bu, printer işlevi oluşturulduğunda gerçekleşir ve bu nedenle, make_printer işleminden sonra make_printer printer ilgili olarak harici msg değerine başvurmamanız gerekir. msg , bu bağlamda printer işlevi için yalnızca normal bir yerel değişkendir.

324
26 окт. aaronasterling tarafından verilen cevap 26 Oct 2010-10-26 06:20 '10 6: 20 2010-10-26 06:20

Soru zaten aaronasterling tarafından cevaplandı

Bununla birlikte, birileri değişkenlerin kaputun altında nasıl depolandıklarıyla ilgilenebilir.

Fragmente gitmeden önce:

Kapanış, değişkenleri ortamdan alan bir fonksiyondur. Bir geri çağırma işlevini, G / Ç işlemlerini gerçekleştirecek başka bir işlevin argümanı olarak ilettiğinizde, bu geri çağırma işlevi daha sonra çağrılır ve bu işlev, bildirilen bağlamı ve tüm kullanılabilir değişkenleri neredeyse sihirli bir şekilde hatırlar. bu bağlamda.

  • İşlev serbest değişkenler kullanmıyorsa, bir kapanma oluşturmaz.

  • Serbest değişkenleri kullanan başka bir iç seviye varsa - önceki tüm seviyeler sözcüksel çevreyi korur (örneğin sonunda)

  • func_closure işlevinin öznitelikleri python <3.X de veya __closure__ python> 3.X de serbest değişkenleri __closure__ .

  • Python'daki her işlev bu kapanış niteliklerine sahiptir, ancak serbest değişken yoksa içerikleri kaydetmez.

örnek: nitelik kapatma, ancak içeride içerik yok, çünkü serbest değişken yok.

 >>> def foo(): ... def fii(): ... pass ... return fii ... >>> f = foo() >>> f.func_closure >>> 'func_closure' in dir(f) True >>> 

Not: BEDAVA DEĞİŞKEN BİR KAPALI OLMALIDIR.

Yukarıdakiyle aynı snippet'i kullanarak açıklayacağım:

 >>> def make_printer(msg): ... def printer(): ... print msg ... return printer ... >>> printer = make_printer('Foo!') >>> printer() #Output: Foo! 

Ve tüm Python işlevleri yakın bir özniteliğe sahiptir, bu nedenle close işleviyle ilişkili ekli değişkenleri göz önünde bulundurun.

İşte printer işlevi için func_closure özniteliği.

 >>> 'func_closure' in dir(printer) True >>> printer.func_closure (<cell at 0x108154c90: str object at 0x108151de0>,) >>> 

closure niteliği, ekteki kapsamda tanımlanan değişkenler hakkında bilgi içeren bir hücre nesneleri dizisi döndürür.

border=0

İlk eleman, Yok olabilir veya serbest değişkenli fonksiyonlar için bağlamalar içeren bir hücre teli olan ve salt okunur olan func_closure'dur.

 >>> dir(printer.func_closure[0]) ['__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cell_contents'] >>> 

Burada, yukarıdaki çıktıda, ne sakladığını görmek için cell_contents görebilirsiniz:

 >>> printer.func_closure[0].cell_contents 'Foo!' >>> type(printer.func_closure[0].cell_contents) <type 'str'> >>> 

Bu nedenle, printer() işlevini çağırdığımızda, cell_contents içinde depolanan değeri ifade eder. Böylece sonucu "Foo!" Olarak aldık.

Yine, yukarıdaki parçayı bazı değişikliklerle kullanmayı açıklayacağım:

  >>> def make_printer(msg): ... def printer(): ... pass ... return printer ... >>> printer = make_printer('Foo!') >>> printer.func_closure >>> 

Yukarıdaki bölümde, yazıcı işlevi olarak bir mesaj yazdırmıyorum, bu nedenle herhangi bir serbest değişken oluşturmuyor. Serbest değişken olmadığından kapanışta içerik olmayacaktır. Yukarıda gördüklerimiz.

Şimdi tüm Free Variable closure kullanarak temizlemek için başka bir parçadan bahsedeceğim:

 >>> def outer(x): ... def intermediate(y): ... free = 'free' ... def inner(z): ... return '%s %s %s %s' % (x, y, free, z) ... return inner ... return intermediate ... >>> outer('I')('am')('variable') 'I am free variable' >>> >>> inter = outer('I') >>> inter.func_closure (<cell at 0x10c989130: str object at 0x10c831b98>,) >>> inter.func_closure[0].cell_contents 'I' >>> inn = inter('am') 

Böylece, func_closure özelliğinin bir kapanma hücresi func_closure olduğunu görüyoruz, açıkça onlara ve içeriklerine başvurabiliriz - hücre "cell_contents" özelliğine sahiptir

 >>> inn.func_closure (<cell at 0x10c9807c0: str object at 0x10c9b0990>, <cell at 0x10c980f68: str object at 0x10c9eaf30>, <cell at 0x10c989130: str object at 0x10c831b98>) >>> for i in inn.func_closure: ... print i.cell_contents ... free am I >>> 

Burada, inn'i çağırdığımızda tüm serbest değişkenlere atıfta bulunacağım, böylece I am free variable

 >>> inn('variable') 'I am free variable' >>> 
82
03 янв. Tarafından verilen cevap James Sapam tarafından Ocak 03 2014-01-03 09:41 '14 9:41 2014-01-03 09:41

Python'un kapanış için çok az desteği var. Ne demek istediğimi görmek için, javascript ile kapatmayı kullanan bir sayaç örneği verin:

 function initCounter(){ var x = 0; function counter () { x += 1; console.log(x); }; return counter; } count = initCounter(); count(); //Prints 1 count(); //Prints 2 count(); //Prints 3 

Kapanış oldukça şık, çünkü bu şekilde yazılmış fonksiyonları "dahili hafızaya" alma yeteneği veriyor. Python 2.7 ile bu mümkün değildir. Eğer denersen

 def initCounter(): x = 0; def counter (): x += 1 ##Error, x not defined print x return counter count = initCounter(); count(); ##Error count(); count(); 

X'in tanımlanmadığını belirten bir mesaj alacaksınız. Fakat eğer diğerleri basabileceğinizi göstermişse, bu nasıl olabilir? Bu Python'un değişken fonksiyonların kapsamını yönetme biçiminden kaynaklanmaktadır. Bir dahili fonksiyon, bir fonksiyonun harici değişkenlerini okuyabilmesine rağmen, bunları yazamaz .

Bu bir utanç. Ancak salt okunur kapanma ile, en azından Python'un sözdizimsel şeker sunduğu bir işlev dekoratör modeli uygulayabilirsiniz.

güncelleştirme

Belirtildiği gibi, python'un kapsamı üzerindeki kısıtlamalarla baş etmenin yolları var ve onlar hakkında konuşacağım.

1. Genel global kullanın (genellikle önerilmez).

2. Basit bir değiştirilebilir sınıf Object tanımlayın

 class Object(object): pass 

ve değişkenleri saklamak için initCounter içinde bir Object scope oluşturun.

 def initCounter (): scope = Object() scope.x = 0 def counter(): scope.x += 1 print scope.x return counter 

scope aslında sadece bir bağlantı olduğu için, alanlarıyla yapılan eylemler scope gerçekten değiştirmez, bu nedenle hatalar oluşmaz.

3. @ unutbu tarafından belirtildiği gibi alternatif bir yol, her değişkeni bir dizi ( x = [0] ) olarak tanımlamak ve ilk öğesini ( x[0] += 1 ) değiştirmektir. Yine, x kendisi değişmediği için hata oluşmaz.

4. @raxacoricofallapatorius tarafından önerildiği gibi, x özellik counter

 def initCounter (): def counter(): counter.x += 1 print counter.x counter.x = 0 return counter 
58
09 мая '14 в 10:22 2014-05-09 10:22 Cevap 09 Mayıs 14’te Cristian Garcia tarafından 10: 22’de veriliyor 2014-05-09 10:22

Ayrı ama kalıcı bir isim alanına ihtiyaç duyduğum bir durum vardı. Dersleri kullandım. Ben farklıyım Ayrılmış fakat kalıcı isimler kapanır.

 >>> class f2: ... def __init__(self): ... self.a = 0 ... def __call__(self, arg): ... self.a += arg ... return(self.a) ... >>> f=f2() >>> f(2) 2 >>> f(2) 4 >>> f(4) 8 >>> f(8) 16 # **OR** >>> f=f2() # **re-initialize** >>> f(f(f(f(2)))) # **nested** 16 # handy in list comprehensions to accumulate values >>> [f(i) for f in [f2()] for i in [2,2,4,8]][-1] 16 
9
24 июня '14 в 20:38 2014-06-24 20:38 Cevap fp_mora tarafından 24 Haziran ' 14'de 20:38 2014-06-24 20:38 tarihinde verilmiştir.

Python 2'nin kapanması yoktu - kapanışlara benzer geçici çözümler vardı.

Alınan cevaplarda birçok örnek var - bir iç fonksiyondaki değişkenlere kopyalamak, bir nesneyi bir iç fonksiyona değiştirmek, vb.

Python 3'te destek daha açık ve sıkıştırılmıştır:

 def closure(): count = 0 def inner(): nonlocal count count += 1 print(count) return inner 

kullanın:

 start = closure() start() # prints 1 start() # prints 2 start() # prints 3 

Yerel nonlocal , bir iç işlevi, açıkça belirtilen bir dış değişkene bağlayarak etkin bir şekilde kapatır. Bu yüzden daha açık bir şekilde “kapanış”.

8
14 марта '17 в 4:49 2017-03-14 04:49 Cevap Lee Benson tarafından 14 Mart 17, 04:49 te 04:49
 def nested1(num1): print "nested1 has",num1 def nested2(num2): print "nested2 has",num2,"and it can reach to",num1 return num1+num2 #num1 referenced for reading here return nested2 

Bu sağlar:

 In [17]: my_func=nested1(8) nested1 has 8 In [21]: my_func(5) nested2 has 5 and it can reach to 8 Out[21]: 13 

Bu, kapatmanın ne olduğu ve nasıl kullanılabileceğinin bir örneğidir.

5
28 янв. Cevap Krcn U tarafından 28 Ocak'ta verildi . 2014-01-28 14:57 '14, 14:57 2014-01-28 14:57

İşleri daha net göstermeye yardımcı olması için python ve JS örneği arasında başka bir basit karşılaştırma önerebilirim.

JS:

 function make () { var cl = 1; function gett () { console.log(cl); } function sett (val) { cl = val; } return [gett, sett] } 

ve uygulama:

 a = make(); g = a[0]; s = a[1]; s(2); g(); // 2 s(3); g(); // 3 

piton:

 def make (): cl = 1 def gett (): print(cl); def sett (val): cl = val return gett, sett 

ve uygulama:

 g, s = make() g() #1 s(2); g() #1 s(3); g() #1 

Nedeni :. Yukarıda python'da belirtilen diğer birçokları gibi, eğer iç bölgede aynı ada sahip bir değişkene yapılan bir atama varsa, iç bölgede yeni bir referans oluşturulur. JS ile öyle değil, açıkça var anahtar sözcüğüyle bir açıklama yapmazsanız.

0
30 июня '17 в 14:45 2017-06-30 14:45 Cevap 30 Haziran 17'de forum düzenleyicisine 14:45 2017-06-30 14:45

etiketleriyle ilgili diğer sorular veya Bir Soru Sor