जवाबों:
आंतरिक कार्य 2.x में गैर-वैरिएबल चर पढ़ सकते हैं , बस उन्हें खंडन न करें। यह कष्टप्रद है, लेकिन आप इसके आसपास काम कर सकते हैं। बस एक शब्दकोश बनाएं, और उसमें अपना डेटा संग्रहीत करें। आंतरिक कार्यों को उन वस्तुओं को उत्परिवर्तित करने से प्रतिबंधित नहीं किया जाता है, जो गैर-भिन्न चर संदर्भित करते हैं।
विकिपीडिया से उदाहरण का उपयोग करने के लिए:
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}
। यहाँ, print d
बाहरी को पढ़ता है d
इस प्रकार d
आंतरिक दायरे में नॉनकोल वैरिएबल बनाता है।
X = 1
केवल X
एक विशेष ऑब्जेक्ट ( int
मान के साथ 1
) के साथ नाम को बांधता है । X = 1; Y = X
दो नामों को एक ही सटीक वस्तु से बांधता है। वैसे भी, कुछ वस्तुएँ आपस में मिलती हैं और आप उनके मूल्यों को बदल सकते हैं।
निम्नलिखित समाधान एलियास ज़मरिया द्वारा उत्तर से प्रेरित है , लेकिन उस उत्तर के विपरीत बाहरी फ़ंक्शन के कई कॉल को सही ढंग से संभालता है। "चर" inner.y
वर्तमान कॉल के लिए स्थानीय है outer
। केवल यह वैरिएबल नहीं है, क्योंकि यह निषिद्ध है, लेकिन एक वस्तु विशेषता (ऑब्जेक्ट inner
ही फ़ंक्शन है)। यह बहुत बदसूरत है (ध्यान दें कि विशेषता केवल inner
फ़ंक्शन परिभाषित होने के बाद बनाई जा सकती है) लेकिन प्रभावी लगती है।
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()
और एक dec()
बाहरी से लौटे कि वृद्धि और एक साझा काउंटर में गिरावट। फिर आपको यह तय करना होगा कि वर्तमान काउंटर मान को संलग्न करने के लिए कौन सा फ़ंक्शन है और उस फ़ंक्शन को दूसरे एक से संदर्भित करना है। जो कुछ अजीब और विषम लगता है। जैसे dec()
एक लाइन में inc.value -= 1
।
शब्दकोश की बजाय, गैर-वर्ग वर्ग में कम अव्यवस्था है । @ क्रिसबी का उदाहरण संशोधित करना :
def outer():
class context:
y = 0
def inner():
context.y += 1
return context.y
return inner
फिर
f = outer()
assert f() == 1
assert f() == 2
assert f() == 3
assert f() == 4
प्रत्येक बाहरी () कॉल एक नया और अलग वर्ग बनाता है जिसे संदर्भ कहा जाता है (केवल एक नया उदाहरण नहीं)। तो यह साझा संदर्भ के बारे में @ नथानिएल के खबरदार होने से बचा जाता है।
g = outer()
assert g() == 1
assert g() == 2
assert f() == 5
__slots__ = ()
वर्ग का उपयोग करने के बजाय एक वस्तु को जोड़कर और बनाकर, उदाहरण के context.z = 3
लिए a बढ़ा होगा AttributeError
। यह सभी वर्गों के लिए संभव है, जब तक कि वे स्लॉट को परिभाषित न करने वाले वर्ग से विरासत में मिले।
मुझे लगता है कि यहां कुंजी का मतलब "पहुंच" से है। क्लोजर दायरे के बाहर एक चर पढ़ने के साथ कोई समस्या नहीं होनी चाहिए, उदाहरण के लिए,
x = 3
def outer():
def inner():
print x
inner()
outer()
उम्मीद के मुताबिक काम करना चाहिए (मुद्रण 3)। हालाँकि, x के मान को ओवरराइड करने से काम नहीं होता है, जैसे,
x = 3
def outer():
def inner():
x = 5
inner()
outer()
print x
अभी भी मुद्रित होगा। PEP-3104 की मेरी समझ से यह गैर-कीवर्ड कीवर्ड कवर करने के लिए है। जैसा कि पीईपी में बताया गया है, आप एक ही चीज़ (गन्दा) को पूरा करने के लिए एक वर्ग का उपयोग कर सकते हैं:
class Namespace(object): pass
ns = Namespace()
ns.x = 3
def outer():
def inner():
ns.x = 5
inner()
outer()
print ns.x
def ns(): pass
उसके बाद ns.x = 3
। यह सुंदर नहीं है, लेकिन यह मेरी आँख से थोड़ा कम बदसूरत है।
class Namespace: x = 3
?
ns
एक वैश्विक वस्तु है, जिसके कारण आप बहुत ही अंत ns.x
में print
बयान में मॉड्यूल स्तर पर संदर्भ ले सकते हैं ।
पायथन 2 में नॉन-फोकल वैरिएबल को लागू करने का एक और तरीका है, यदि यहां कोई भी उत्तर जो कुछ भी है उसके लिए अवांछनीय हैं:
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
यह चर के असाइनमेंट स्टेटमेंट में फ़ंक्शन के नाम का उपयोग करने के लिए बेमानी है, लेकिन यह एक शब्दकोश में चर को डालने की तुलना में मेरे लिए सरल और साफ दिखता है। वैल्यू को एक कॉल से दूसरे कॉल पर याद किया जाता है, जैसे क्रिस बी के जवाब में।
f = outer()
और फिर बाद में करते हैं g = outer()
, तो f
काउंटर रीसेट हो जाएगा। इसका कारण यह है कि वे दोनों अपने स्वयं के स्वतंत्र होने के बजाय एक एकल outer.y
चर साझा करते हैं। यद्यपि यह कोड क्रिस बी के उत्तर की तुलना में अधिक सौंदर्यवादी रूप से मनभावन लगता है, लेकिन यदि आप outer
एक से अधिक बार कॉल करना चाहते हैं, तो उनका तरीका केवल लेक्सिकल स्कूपिंग का अनुकरण करने का एकमात्र तरीका प्रतीत होता है ।
outer.y
फ़ंक्शन कॉल (उदाहरण) के लिए कुछ भी स्थानीय को शामिल करने के लिए असाइनमेंट नहीं है outer()
, लेकिन फ़ंक्शन ऑब्जेक्ट की एक विशेषता को असाइन करता है जो outer
इसके संलग्न दायरे में नाम के लिए बाध्य है । और इसलिए एक समान रूप से अच्छी तरह से इस्तेमाल किया जा सकता है, लेखन में outer.y
, बजाय किसी अन्य नाम outer
, बशर्ते कि यह उस दायरे में बँधा हुआ हो। क्या ये सही है?
outer.y
नाम का उपयोग करने के बजाय inner.y
(क्योंकि inner
कॉल के अंदर बाध्य है outer()
, जो वास्तव में हम चाहते हैं गुंजाइश है), लेकिन डाल आंतरिक की परिभाषा के inner.y = 0
बाद का प्रारंभ (जैसा कि वस्तु का गुण होने पर मौजूद होना चाहिए), लेकिन निश्चित रूप से पहले ? return inner
यहाँ एक सुझाव से प्रेरित कुछ है जो अलोइस महदाल ने एक अन्य उत्तर के संबंध में टिप्पणी की है :
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)
अपडेट करें
हाल ही में इस पर वापस देखने के बाद, मैं इस बात से हैरान था कि डेकोरेटर की तरह यह कैसे था - जब यह मुझ पर चढ़ा था कि इसे लागू करने से यह अधिक सामान्य और उपयोगी होगा (हालांकि ऐसा करने से यकीनन कुछ हद तक इसकी पठनीयता कम हो जाती है)।
# 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)
ध्यान दें कि दोनों संस्करण पायथन 2 और 3 दोनों में काम करते हैं।
अजगर के टेढ़े-मेढ़े नियमों में एक मस्सा होता है - असाइनमेंट एक वैरिएबल लोकल को उसके फंक्शन स्कोप को तुरंत घेरने का काम करता है। वैश्विक चर के लिए, आप इसके साथ हल करेंगेglobal
कीवर्ड के ।
समाधान एक वस्तु को पेश करने के लिए है जो दो स्कोप के बीच साझा किया जाता है, जिसमें परस्पर परिवर्तनशील चर होते हैं, लेकिन स्वयं को एक चर के माध्यम से संदर्भित किया जाता है जिसे असाइन नहीं किया गया है।
def outer(v):
def inner(container = [v]):
container[0] += 1
return container[0]
return inner
एक विकल्प कुछ स्कोप्स हैकरी है:
def outer(v):
def inner(varname = 'v', scope = locals()):
scope[varname] += 1
return scope[varname]
return inner
आप पैरामीटर का नाम प्राप्त करने के लिए कुछ प्रवंचनाओं का पता लगाने में सक्षम हो सकते हैं outer
, और फिर इसे varname के रूप में पास कर सकते हैं, लेकिन नाम का भरोसा किए बिनाouter
आपको वाई कॉम्बीनेटर का उपयोग करने की आवश्यकता होगी।
nonlocal
। उस समय में स्थानीय लोगों locals()
का शब्दकोश परिभाषित किया जाता है, लेकिन उस शब्दकोश को बदलने से उसमें परिवर्तन नहीं होता है । जब आपके पास अधिक आंतरिक फ़ंक्शंस होंगे जो किसी बंद ओवर वैरिएबल को साझा करना चाहते हैं तो यह काम नहीं करेगा। एक और कहो कि वृद्धि और एक साझा काउंटर में वृद्धि। outer()
inner()
v
outer()
inc()
dec()
nonlocal
एक पायथन 3 फीचर है।
nonlocal
अजगर 2 में सामान्य रूप में । आपके विचार सामान्य मामले को कवर नहीं करते हैं, लेकिन केवल एक आंतरिक कार्य के साथ होते हैं। पर एक नजर डालें इस सार एक उदाहरण के लिए। दोनों आंतरिक कार्यों का अपना कंटेनर होता है। बाहरी फ़ंक्शन के दायरे में आपको एक परिवर्तनशील वस्तु की आवश्यकता होती है, जैसा कि पहले से ही सुझाए गए अन्य उत्तर हैं।
nonlocal
Python 3 में शुरू किए गए कीवर्ड के बिना किसी क्लोजर में नॉनक्लॉक वैरिएबल तक पहुँच की आवश्यकता है
इसे करने का एक और तरीका (हालाँकि यह बहुत अधिक क्रिया है):
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
एक व्यावहारिक और कुछ हद तक कम उपयोग के मामले में ऊपर मार्टिनो सुरुचिपूर्ण समाधान का विस्तार:
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)
एक वैश्विक चर का उपयोग करें
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
व्यक्तिगत रूप से, मुझे वैश्विक चर पसंद नहीं हैं। लेकिन, मेरा प्रस्ताव https://stackoverflow.com/a/19877437/1083834 उत्तर पर आधारित है
def report():
class Rank:
def __init__(self):
report.ranks += 1
rank = Rank()
report.ranks = 0
report()
जहां उपयोगकर्ता को एक वैश्विक चर घोषित करने की आवश्यकता होती है ranks
, हर बार आपको कॉल करने की आवश्यकता होती है report
। मेरा सुधार उपयोगकर्ता से फ़ंक्शन चर को आरंभ करने की आवश्यकता को समाप्त करता है।
inner
, लेकिन इसे असाइन नहीं कर सकते, लेकिन आप इसे कुंजी और मान संशोधित कर सकते हैं। यह वैश्विक चरों के उपयोग से बचा जाता है।