Yanıtlar:
İç işlevler yerel olmayan değişkenleri 2.x'te okuyabilir , onları yeniden bağlayamazlar . Bu can sıkıcı bir durum ama bunun üstesinden gelebilirsiniz. Sadece bir sözlük oluşturun ve verilerinizi burada öğeler olarak saklayın. İç fonksiyonlar yasak değildir mutasyona yerel olmayan değişkenler bakın nesneleri.
Wikipedia'daki örneği kullanmak için:
def outer():
d = {'y' : 0}
def inner():
d['y'] += 1
return d['y']
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
def inner(): print d; d = {'y': 1}
. Burada print d
dış okur , d
böylece d
iç kapsamda yerel olmayan değişken oluşturur .
X = 1
, adı X
belirli bir nesneyle (bir int
değerle 1
) bağlar . X = 1; Y = X
iki adı aynı nesneye bağlar. Neyse, bazı nesnelerdir değişken ve onların değerlerini değiştirebilir.
Aşağıdaki çözüm, Elias Zamaria'nın yanıtından ilham almıştır , ancak bu yanıtın aksine, dış işlevin birden çok çağrısını doğru bir şekilde ele alır. "Değişken" inner.y
geçerli çağrı için yereldir outer
. Sadece bu bir değişken değildir, çünkü bu yasaklanmıştır, ancak bir nesne niteliği (nesnenin inner
kendisi işlevdir ). Bu çok çirkin (özniteliğin ancak inner
işlev tanımlandıktan sonra oluşturulabileceğini unutmayın ) ancak etkili görünüyor.
def outer():
def inner():
inner.y += 1
return inner.y
inner.y = 0
return inner
f = outer()
g = outer()
print(f(), f(), g(), f(), g()) #prints (1, 2, 1, 3, 2)
inc()
ve a deyin dec()
. Ardından, mevcut sayaç değerini hangi işleve ekleyeceğinize ve diğer işlevlerden bu işleve başvuracağınıza karar vermelisiniz. Biraz tuhaf ve asimetrik görünüyor. Örneğin dec()
bir satırda inc.value -= 1
.
Bir sözlükten ziyade, yerel olmayan bir sınıf için daha az karmaşa vardır . @ ChrisB örneğini değiştirmek :
def outer():
class context:
y = 0
def inner():
context.y += 1
return context.y
return inner
Sonra
f = outer()
assert f() == 1
assert f() == 2
assert f() == 3
assert f() == 4
Her bir dış () çağrısı, bağlam adı verilen yeni ve farklı bir sınıf oluşturur (yalnızca yeni bir örnek değil). Bu nedenle, @ Nathaniel'in paylaşılan bağlam hakkında dikkatli olmasını önler .
g = outer()
assert g() == 1
assert g() == 2
assert f() == 5
__slots__ = ()
sınıfı kullanmak yerine bir nesne ekleyip yaratarak, örneğin context.z = 3
bir AttributeError
. Yuvaları tanımlamayan bir sınıftan miras almadıkları sürece tüm sınıflar için mümkündür.
Sanırım buradaki anahtar, "erişim" ile kastettiğin şey. Kapanış kapsamının dışındaki bir değişkeni okumakta herhangi bir sorun olmamalıdır, örneğin,
x = 3
def outer():
def inner():
print x
inner()
outer()
beklendiği gibi çalışmalıdır (baskı 3). Ancak, x'in değerini geçersiz kılmak işe yaramaz, örneğin,
x = 3
def outer():
def inner():
x = 5
inner()
outer()
print x
3. PEP-3104 anlayışıma göre , yerel olmayan anahtar kelimenin kapsaması budur. PEP'de belirtildiği gibi, aynı şeyi başarmak için bir sınıf kullanabilirsiniz (biraz dağınık):
class Namespace(object): pass
ns = Namespace()
ns.x = 3
def outer():
def inner():
ns.x = 5
inner()
outer()
print ns.x
def ns(): pass
ardından ns.x = 3
. Hoş değil ama gözüme biraz daha az çirkin geliyor.
class Namespace: x = 3
?
ns
başvurabileceğiniz neden olan küresel bir nesnedir ns.x
modül düzeyinde print
en sonunda deyimi .
Python 2'de yerel olmayan değişkenleri uygulamanın başka bir yolu vardır, buradaki cevaplardan herhangi birinin herhangi bir nedenle istenmemesi durumunda:
def outer():
outer.y = 0
def inner():
outer.y += 1
return outer.y
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
Değişkenin atama ifadesinde işlevin adını kullanmak gereksizdir, ancak bana değişkeni bir sözlüğe koymaktan daha basit ve daha temiz görünüyor. Değer, tıpkı Chris B.'nin cevabında olduğu gibi, bir aramadan diğerine hatırlanır.
f = outer()
ve daha sonra yaparsanız g = outer()
, o zaman f
sayacı sıfırlanacaktır. Bunun nedeni , her birinin kendi bağımsız değişkenine sahip olması yerine, her ikisinin de tek bir outer.y
değişkeni paylaşmasıdır . Bu kod, Chris B'nin cevabından daha estetik görünse de, outer
birden fazla kez aramak istiyorsanız, sözcük kapsamını taklit etmenin tek yolu onun yöntemi gibi görünüyor .
outer.y
işlev çağrısı (örnek) yerel bir şey içermeyen outer()
isme bağlı işlev nesnesinin bir öznitelik, ama devrettikleri outer
onun içinde kapatmakta kapsamı. Ve bu nedenle , bu kapsamda olduğu bilinmesi koşuluyla outer.y
, herhangi bir başka isim yerine yazılı olarak da eşit derecede iyi bir şekilde kullanılabilir outer
. Bu doğru mu?
outer.y
adı kullanmak yerine inner.y
(çünkü inner
çağrının içinde bağlı outer()
olduğundan, tam olarak istediğimiz kapsam budur), ancak içsel tanımdan inner.y = 0
sonraki ilklendirme (nesnenin niteliği yaratıldığında var olması gerektiği gibi), ama tabii ki daha önce ? return inner
İşte Alois Mahdal'ın başka bir cevapla ilgili bir yorumda yaptığı bir öneriden ilham alan bir şey :
class Nonlocal(object):
""" Helper to implement nonlocal names in Python 2.x """
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def outer():
nl = Nonlocal(y=0)
def inner():
nl.y += 1
return nl.y
return inner
f = outer()
print(f(), f(), f()) # -> (1 2 3)
Güncelleme
Yakın zamanda buna dönüp baktıktan sonra, nasıl bir dekoratör gibi olduğu karşısında şaşkına döndüm - bunu bir başkası olarak uygulamanın onu daha genel ve kullanışlı hale getireceğini anladığında (bunu yapmak, okunabilirliğini bir dereceye kadar azalttığı tartışmalı bir şekilde).
# Implemented as a decorator.
class Nonlocal(object):
""" Decorator class to help implement nonlocal names in Python 2.x """
def __init__(self, **kwargs):
self._vars = kwargs
def __call__(self, func):
for k, v in self._vars.items():
setattr(func, k, v)
return func
@Nonlocal(y=0)
def outer():
def inner():
outer.y += 1
return outer.y
return inner
f = outer()
print(f(), f(), f()) # -> (1 2 3)
Her iki sürümün de hem Python 2 hem de 3'te çalıştığını unutmayın.
Python'un kapsam belirleme kurallarında bir siğil vardır - atama, bir değişkeni hemen kapsayan işlev kapsamına yerel yapar. Global bir değişken için, bunu şu şekilde çözersiniz:global
anahtar kelime çözersiniz.
Çözüm, iki kapsam arasında paylaşılan, değişken değişkenler içeren, ancak kendisine atanmamış bir değişken aracılığıyla referans verilen bir nesneyi tanıtmaktır.
def outer(v):
def inner(container = [v]):
container[0] += 1
return container[0]
return inner
Bir alternatif, bazı kapsam korsanlığıdır:
def outer(v):
def inner(varname = 'v', scope = locals()):
scope[varname] += 1
return scope[varname]
return inner
Parametrenin adını almak için bazı hileler bulabilir outer
ve sonra onu değişken adı olarak geçirebilirsiniz, ancak isme güvenmeden outer
bir Y birleştirici kullanmanız gerekir.
nonlocal
. locals()
bir sözlük oluşturur outer()
anda ler yerli inner()
tanımlanmış ama Sözlük anlamına değişiyor değil değiştirmek v
içinde outer()
. Bu, kapalı bir değişken paylaşmak isteyen daha fazla iç işleviniz olduğunda artık çalışmayacaktır. A deyin inc()
ve dec()
bu paylaşılan bir sayacı artırır ve azaltır.
nonlocal
bir python 3 özelliğidir.
nonlocal
olarak Python 2'de Python 3'lerin etkisinin nasıl elde edileceğiydi . Fikirleriniz genel durumu değil, yalnızca tek bir iç işlevi olanı kapsar . Bir örnek için bu öze bir bakın . Her iki iç işlevin de kendi kapsayıcıları vardır. Diğer cevapların zaten önerildiği gibi, dış fonksiyon kapsamında değiştirilebilir bir nesneye ihtiyacınız var.
nonlocal
Python 3'te tanıtılan anahtar kelime
Bunu yapmanın başka bir yolu (çok ayrıntılı olmasına rağmen):
import ctypes
def outer():
y = 0
def inner():
ctypes.pythonapi.PyCell_Set(id(inner.func_closure[0]), id(y + 1))
return y
return inner
x = outer()
x()
>> 1
x()
>> 2
y = outer()
y()
>> 1
x()
>> 3
Yukarıdaki Martineau zarif çözümünü pratik ve biraz daha az zarif bir kullanım durumuna genişletmek:
class nonlocals(object):
""" Helper to implement nonlocal names in Python 2.x.
Usage example:
def outer():
nl = nonlocals( n=0, m=1 )
def inner():
nl.n += 1
inner() # will increment nl.n
or...
sums = nonlocals( { k:v for k,v in locals().iteritems() if k.startswith('tot_') } )
"""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __init__(self, a_dict):
self.__dict__.update(a_dict)
Global bir değişken kullanın
def outer():
global y # import1
y = 0
def inner():
global y # import2 - requires import1
y += 1
return y
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
Şahsen ben global değişkenleri sevmiyorum. Ancak, teklifim https://stackoverflow.com/a/19877437/1083704 cevabına dayanıyor
def report():
class Rank:
def __init__(self):
report.ranks += 1
rank = Rank()
report.ranks = 0
report()
Kullanıcının global bir değişken bildirmesi gerektiğinde ranks
, her zaman report
. Geliştirmem, işlev değişkenlerini kullanıcıdan başlatma ihtiyacını ortadan kaldırıyor.
inner
başvurabilir, ancak atayamazsınız, ancak anahtarlarını ve değerlerini değiştirebilirsiniz. Bu, global değişkenlerin kullanılmasını önler.