गैर-कीवर्ड कीवर्ड Python 2.x में


117

मैं पायथन 2.6 में एक क्लोजर को लागू करने की कोशिश कर रहा हूं और मुझे एक गैर-वैरिएबल चर का उपयोग करने की आवश्यकता है लेकिन ऐसा लगता है कि यह कीवर्ड अजगर 2.x में उपलब्ध नहीं है। अजगर के इन संस्करणों में किसी को गैर-गायक चर का उपयोग कैसे करना चाहिए?

जवाबों:


125

आंतरिक कार्य 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

4
शब्दकोश से मूल्य को संशोधित करना क्यों संभव है?
coelhudo

9
@coelhudo क्योंकि आप nonlocal चर को संशोधित कर सकते हैं । लेकिन आप गैर-वैरिएबल चर को असाइनमेंट नहीं कर सकते। उदाहरण के लिए, इस UnboundLocalError बढ़ा देंगे: def inner(): print d; d = {'y': 1}। यहाँ, print dबाहरी को पढ़ता है dइस प्रकार dआंतरिक दायरे में नॉनकोल वैरिएबल बनाता है।
सुजानशाक्य

21
इस उत्तर के लिए धन्यवाद। मुझे लगता है कि आप शब्दावली में सुधार कर सकते हैं: इसके बजाय "पढ़ सकते हैं, बदल नहीं सकते", हो सकता है "संदर्भ दे सकते हैं, असाइन नहीं कर सकते"। आप किसी ऑब्जेक्ट की सामग्री को एक गैर-स्कोप दायरे में बदल सकते हैं, लेकिन आप यह नहीं बदल सकते कि किस ऑब्जेक्ट को संदर्भित किया गया है।
मेटामैट

3
वैकल्पिक रूप से, आप डिक्शनरी के बजाय मनमानी क्लास और इंस्टेंटिएट ऑब्जेक्ट का उपयोग कर सकते हैं। मुझे यह अधिक सुरुचिपूर्ण और पठनीय लगता है क्योंकि यह कोड को शाब्दिक (शब्दकोश कुंजी) के साथ बाढ़ नहीं देता है, साथ ही आप विधियों का उपयोग कर सकते हैं।
एलोइस महदाल

6
पायथन के लिए, मुझे लगता है कि क्रिया "बाइंड" या "रिबंड" का उपयोग करना और "चर" के बजाय संज्ञा "नाम" का उपयोग करना सबसे अच्छा है। पायथन 2.x में, आप किसी ऑब्जेक्ट को बंद कर सकते हैं, लेकिन आप मूल स्कोप में नाम को रिबंड नहीं कर सकते। C जैसी भाषा में, वैरिएबल घोषित करने से कुछ स्टोरेज (या तो स्टैटिक स्टोरेज या स्टैक पर अस्थायी) हो जाता है। पायथन में, अभिव्यक्ति X = 1केवल Xएक विशेष ऑब्जेक्ट ( intमान के साथ 1) के साथ नाम को बांधता है । X = 1; Y = Xदो नामों को एक ही सटीक वस्तु से बांधता है। वैसे भी, कुछ वस्तुएँ आपस में मिलती हैं और आप उनके मूल्यों को बदल सकते हैं।
स्टेवेहा

37

निम्नलिखित समाधान एलियास ज़मरिया द्वारा उत्तर से प्रेरित है , लेकिन उस उत्तर के विपरीत बाहरी फ़ंक्शन के कई कॉल को सही ढंग से संभालता है। "चर" 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)

यह ugly नहीं है। इनर का उपयोग करने के बजाय। मैं, बाहरी का उपयोग करें। आप बाहरी इनर को परिभाषित कर सकते हैं।
jgomo3

1
ठीक है, मैं देख रहा हूं कि मेरी टिप्पणी गलत है। इलायस ज़मरिया ने आउटर का समाधान भी लागू किया। लेकिन जैसा कि नथानिएल ने टिप्पणी की है [1], आपको सावधान रहना चाहिए। मुझे लगता है कि इस उत्तर को समाधान के रूप में प्रचारित किया जाना चाहिए, लेकिन बाहरी समाधान और गुफाओं के बारे में ध्यान दें। [1] stackoverflow.com/questions/3190706/...
jgomo3

यह बदसूरत हो जाता है यदि आप एक से अधिक आंतरिक फ़ंक्शन को साझा करना चाहते हैं जो कि नॉनोकल म्यूटेबल अवस्था साझा करता है। एक कहो inc()और एक dec()बाहरी से लौटे कि वृद्धि और एक साझा काउंटर में गिरावट। फिर आपको यह तय करना होगा कि वर्तमान काउंटर मान को संलग्न करने के लिए कौन सा फ़ंक्शन है और उस फ़ंक्शन को दूसरे एक से संदर्भित करना है। जो कुछ अजीब और विषम लगता है। जैसे dec()एक लाइन में inc.value -= 1
लाठी

33

शब्दकोश की बजाय, गैर-वर्ग वर्ग में कम अव्यवस्था है । @ क्रिसबी का उदाहरण संशोधित करना :

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

अच्छा! यह शब्दकोश की तुलना में वास्तव में अधिक सुरुचिपूर्ण और पठनीय है।
विन्केन्जो

मैं यहां सामान्य रूप से स्लॉट्स का उपयोग करने की सलाह दूंगा। यह आपको टाइपोस से बचाता है।
डरहम

@DerWeh दिलचस्प है, मैं उस के बारे में कभी नहीं सुना है। क्या आप किसी भी वर्ग के समान कह सकते हैं? मैं टाइपो द्वारा flummoxed हो रही नफरत करता हूँ।
बॉब स्टीन

@BusStein क्षमा करें, मुझे वास्तव में समझ में नहीं आया कि आपका क्या मतलब है। लेकिन __slots__ = ()वर्ग का उपयोग करने के बजाय एक वस्तु को जोड़कर और बनाकर, उदाहरण के context.z = 3लिए a बढ़ा होगा AttributeError। यह सभी वर्गों के लिए संभव है, जब तक कि वे स्लॉट को परिभाषित न करने वाले वर्ग से विरासत में मिले।
DerWeh

@DerWeh मैंने टाइपो को पकड़ने के लिए स्लॉट का उपयोग करने के बारे में कभी नहीं सुना । आप सही हैं यह केवल कक्षा उदाहरणों में मदद करेगा, कक्षा चर नहीं। हो सकता है कि आप pyfiddle पर एक कामकाजी उदाहरण बनाना चाहते हैं। इसमें कम से कम दो अतिरिक्त लाइनों की आवश्यकता होगी। आप अधिक अव्यवस्था के बिना यह कैसे करेंगे तस्वीर नहीं कर सकते।
बॉब स्टीन

14

मुझे लगता है कि यहां कुंजी का मतलब "पहुंच" से है। क्लोजर दायरे के बाहर एक चर पढ़ने के साथ कोई समस्या नहीं होनी चाहिए, उदाहरण के लिए,

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। यह सुंदर नहीं है, लेकिन यह मेरी आँख से थोड़ा कम बदसूरत है।
davidchambers

2
पतन का कोई विशेष कारण? मैं मानता हूँ कि मेरा सबसे सुंदर समाधान नहीं है, लेकिन यह काम करता है ...
ig0774

2
किस बारे में class Namespace: x = 3?
फेउर्मुरमेल

3
मुझे नहीं लगता कि यह तरीका गैर-बोलचाल का अनुकरण करता है, क्योंकि यह एक संदर्भ के बजाय एक संदर्भ में एक वैश्विक संदर्भ बनाता है।
कार्मेलो

1
मैं @CarmeloS से सहमत हूं, PEP-3104 में कुछ ऐसा ही करने के तरीके के रूप में नहीं करने का आपका आखिरी ब्लॉक कोड है । 2. nsएक वैश्विक वस्तु है, जिसके कारण आप बहुत ही अंत ns.xमें printबयान में मॉड्यूल स्तर पर संदर्भ ले सकते हैं ।
मार्टिउ

13

पायथन 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

यह चर के असाइनमेंट स्टेटमेंट में फ़ंक्शन के नाम का उपयोग करने के लिए बेमानी है, लेकिन यह एक शब्दकोश में चर को डालने की तुलना में मेरे लिए सरल और साफ दिखता है। वैल्यू को एक कॉल से दूसरे कॉल पर याद किया जाता है, जैसे क्रिस बी के जवाब में।


19
कृपया सावधान रहें: जब इस तरह से लागू किया जाता है, यदि आप करते हैं f = outer()और फिर बाद में करते हैं g = outer(), तो fकाउंटर रीसेट हो जाएगा। इसका कारण यह है कि वे दोनों अपने स्वयं के स्वतंत्र होने के बजाय एक एकल outer.y चर साझा करते हैं। यद्यपि यह कोड क्रिस बी के उत्तर की तुलना में अधिक सौंदर्यवादी रूप से मनभावन लगता है, लेकिन यदि आप outerएक से अधिक बार कॉल करना चाहते हैं, तो उनका तरीका केवल लेक्सिकल स्कूपिंग का अनुकरण करने का एकमात्र तरीका प्रतीत होता है ।
नाथनियल

@ नथानियल: मुझे यह देखने दो कि क्या मैं इसे सही ढंग से समझता हूँ। outer.yफ़ंक्शन कॉल (उदाहरण) के लिए कुछ भी स्थानीय को शामिल करने के लिए असाइनमेंट नहीं है outer(), लेकिन फ़ंक्शन ऑब्जेक्ट की एक विशेषता को असाइन करता है जो outerइसके संलग्न दायरे में नाम के लिए बाध्य है । और इसलिए एक समान रूप से अच्छी तरह से इस्तेमाल किया जा सकता है, लेखन में outer.y, बजाय किसी अन्य नाम outer, बशर्ते कि यह उस दायरे में बँधा हुआ हो। क्या ये सही है?
मार्क वैन लीउवेन

1
सुधार, मुझे "उस दायरे में बंधे" के बाद कहा जाना चाहिए: एक ऐसी वस्तु जिसका प्रकार सेटिंग विशेषता (जैसे कि फ़ंक्शन, या किसी भी वर्ग उदाहरण) की अनुमति देता है। इसके अलावा, चूंकि यह गुंजाइश वास्तव में हम चाहते हैं की तुलना में बाहर है, यह निम्नलिखित बहुत बदसूरत समाधान का सुझाव नहीं होगा: outer.yनाम का उपयोग करने के बजाय inner.y(क्योंकि innerकॉल के अंदर बाध्य है outer(), जो वास्तव में हम चाहते हैं गुंजाइश है), लेकिन डाल आंतरिक की परिभाषा के inner.y = 0 बाद का प्रारंभ (जैसा कि वस्तु का गुण होने पर मौजूद होना चाहिए), लेकिन निश्चित रूप से पहले ? return inner
मार्क वैन लीउवेन

@MarcvanLeeuwen आपकी टिप्पणी से लगता है कि एलियास ने इनर के साथ समाधान को प्रेरित किया है। आपके द्वारा अच्छा सोचा गया।
अंकुर अग्रवाल

ग्लोबल वैरिएबल्स तक पहुंचना और अपडेट करना शायद ऐसा नहीं है जब ज्यादातर लोग क्लोजर के कैप्चर को म्यूट करते समय करने की कोशिश कर रहे हों।
बिंकी

12

यहाँ एक सुझाव से प्रेरित कुछ है जो अलोइस महदाल ने एक अन्य उत्तर के संबंध में टिप्पणी की है :

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 दोनों में काम करते हैं।


यह एक पठनीयता और शाब्दिक स्कूपिंग के संरक्षण के मामले में सबसे अच्छा लगता है।
एरोन एस। कुरलैंड

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()vouter()inc()dec()
ब्लैकजैक

@BlackJack nonlocalएक पायथन 3 फीचर है।
मार्सिन

हाँ मैं जानता हूँ। सवाल यह है कि अजगर 3 की प्रभाव को प्राप्त करने के लिए था nonlocalअजगर 2 में सामान्य रूप में । आपके विचार सामान्य मामले को कवर नहीं करते हैं, लेकिन केवल एक आंतरिक कार्य के साथ होते हैं। पर एक नजर डालें इस सार एक उदाहरण के लिए। दोनों आंतरिक कार्यों का अपना कंटेनर होता है। बाहरी फ़ंक्शन के दायरे में आपको एक परिवर्तनशील वस्तु की आवश्यकता होती है, जैसा कि पहले से ही सुझाए गए अन्य उत्तर हैं।
ब्लेक जेक

@ ब्लेकजैक यह सवाल नहीं है, लेकिन आपके इनपुट के लिए धन्यवाद।
मार्सिन

हाँ, यह सवाल है। पृष्ठ के शीर्ष पर एक नज़र डालें। Adinsa के पास Python 2.6 है और nonlocalPython 3 में शुरू किए गए कीवर्ड के बिना किसी क्लोजर में नॉनक्लॉक वैरिएबल तक पहुँच की आवश्यकता है
BlackJack

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

1
वास्तव में मैं एक शब्दकोश का उपयोग कर समाधान पसंद करता हूं, लेकिन यह एक अच्छा है :)
एज़र फर्नांडीस

0

एक व्यावहारिक और कुछ हद तक कम उपयोग के मामले में ऊपर मार्टिनो सुरुचिपूर्ण समाधान का विस्तार:

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)

-3

एक वैश्विक चर का उपयोग करें

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, लेकिन इसे असाइन नहीं कर सकते, लेकिन आप इसे कुंजी और मान संशोधित कर सकते हैं। यह वैश्विक चरों के उपयोग से बचा जाता है।
जोहानसस्टा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.