2013-05-09 12 views
6

कोई समझा सकते हैं क्यों निम्नलिखित प्रोग्राम विफल:चर गुंजाइश

Traceback (most recent call last): 
    File "a.py", line 13, in <module> 
    main() 
    File "a.py", line 10, in main 
    g(f) 
    File "a.py", line 3, in g 
    f() 
    File "a.py", line 8, in f 
    print x 
UnboundLocalError: local variable 'x' referenced before assignment 

लेकिन अगर मैं बस एक सरणी के लिए चर x बदलने के लिए, यह काम करता है:

def g(f): 
    for _ in range(10): 
    f() 

def main(): 
    x = 10 
    def f(): 
    print x 
    x = x + 1 
    g(f) 

if __name__ == '__main__': 
    main() 
संदेश के साथ

:

def g(f): 
    for _ in range(10): 
    f() 

def main(): 
    x = [10] 
    def f(): 
    print x[0] 
    x[0] = x[0] + 1 
    g(f) 

if __name__ == '__main__': 
    main() 
उत्पादन के साथ

10 
11 
12 
13 
14 
15 
16 
17 
18 
19 

f() से अगर यह x उपयोग नहीं कर सकते, क्यों यह सुलभ हो जाता है x एक सरणी है कारण मैं उलझन में हूँ, है?

धन्यवाद।

उत्तर

4

लेकिन यह उत्तर कहता है कि समस्या x को असाइन करने के साथ है। यदि यह है, तो प्रिंटिंग इसे ठीक काम करना चाहिए, है ना?

आपको उस क्रम को समझना होगा जिसमें चीजें होती हैं। आपके पायथन कोड को संकलित और निष्पादित करने से पहले, पार्सर नामक कुछ को पाइथन कोड के माध्यम से पढ़ता है और सिंटैक्स की जांच करता है। एक और चीज पार्सर स्थानीय चर के रूप में चिह्नित चर है। जब पार्सर स्थानीय दायरे में कोड में असाइनमेंट देखता है, तो असाइनमेंट के लेफ्टथेंड पक्ष पर चर को स्थानीय के रूप में चिह्नित किया जाता है। उस समय, अभी तक कुछ भी संकलित नहीं किया गया है - अकेले निष्पादित होने दें, और इसलिए कोई असाइनमेंट नहीं होता है; चर को केवल स्थानीय चर के रूप में चिह्नित किया जाता है।

पार्सर समाप्त होने के बाद, कोड संकलित और निष्पादित किया गया है। जब निष्पादन प्रिंट बयान तक पहुँच जाता है:

def main(): 
    x = 10  #<---x in enclosing scope 

    def f(): 
    print x #<----- 

    x = x + 1 #<-- x marked as local variable inside the function f() 

प्रिंट बयान लगता है कि यह आगे जाना है और प्रिंट (LEGB देखने की प्रक्रिया में 'ई') गुंजाइश enclosing में एक्स चाहिए। हालांकि, क्योंकि पार्सर ने पहले एक्स को स्थानीय चर के रूप में चिह्नित किया है(), पाइथन एक्स को देखने के लिए स्थानीय स्कोप (एलईजीबी लुकअप प्रक्रिया में 'एल') से आगे नहीं बढ़ता है।चूंकि एक्स को 'प्रिंट एक्स' निष्पादित समय पर स्थानीय दायरे में असाइन नहीं किया गया है, इसलिए पाइथन एक त्रुटि को थूकता है।

ध्यान दें कि यहां तक ​​कि यदि कोई असाइनमेंट होता है तो कोड कभी निष्पादित नहीं होगा, पार्सर अभी भी स्थानीय चर के रूप में असाइनमेंट के बाईं ओर चर को चिह्नित करता है। पार्सर को इस बारे में कोई जानकारी नहीं है कि चीजें कैसे निष्पादित होंगी, इसलिए यह आपकी फ़ाइल में सिंटैक्स त्रुटियों और स्थानीय चर के लिए अंधेरे से खोज करती है - यहां तक ​​कि कोड में जो कभी निष्पादित नहीं कर सकता है। यहाँ है कि के कुछ उदाहरण हैं: जब स्थानीय चर अंकन

def dostuff(): 
    x = 10 

    def f(): 
     print x 

     if False: #The body of the if will never execute... 
      a b c #...yet the parser finds a syntax error here 


    return f 

f = dostuff() 
f() 



--output:-- 
File "1.py", line 8 
    a b c 
    ^
SyntaxError: invalid syntax 

पार्सर एक ही बात करता है:

def dostuff(): 
    x = 10 

    def f(): 
     print x 

     if False: #The body of the if will never execute... 
      x = 0 #..yet the parser marks x as a local variable 

    return f 

f = dostuff() 
f() 

अब देखो क्या होता है जब आपको लगता है कि पिछले कार्यक्रम क्रियान्वित:

Traceback (most recent call last): 
    File "1.py", line 11, in <module> 
    f() 
    File "1.py", line 4, in f 
    print x 
UnboundLocalError: local variable 'x' referenced before assignment 

जब कथन 'प्रिंट एक्स' निष्पादित होता है, क्योंकि पार्सर ने स्थानीय चर के रूप में एक्स को चिह्नित किया है तो एक्स के लिए लुकअप स्थानीय दायरे पर बंद हो जाता है।

वह 'फीचर' पायथन के लिए अद्वितीय नहीं है - यह अन्य भाषाओं में भी होता है।

सरणी उदाहरण के लिए, जब आप लिखते हैं:

x[0] = x[0] + 1 

कि एक सरणी नामित एक्स ऊपर देखने जाकर उसका पहला तत्व के लिए कुछ आवंटित करने के लिए अजगर बताता है। चूंकि स्थानीय दायरे में x नामक किसी भी चीज़ के लिए कोई असाइनमेंट नहीं है, इसलिए पार्सर x को स्थानीय चर के रूप में चिह्नित नहीं करता है।

+0

+1 संकलक का उल्लेख करने के लिए +1 कैसे स्कॉप्स काम करता है। मेरे लिए नया दृष्टिकोण। –

1

समस्या यह है कि परिवर्तनीय x को बंद करके उठाया जाता है। जब आप बंद होने से उठाए गए चर को असाइन करने का प्रयास करते हैं, तो पाइथन शिकायत करेगा जब तक कि आप global या nonlocal कीवर्ड का उपयोग करें। इस मामले में जहां आप list का उपयोग कर रहे हैं, आप नाम को असाइन नहीं कर रहे हैं - आप बंद करने में उठाए गए ऑब्जेक्ट को संशोधित कर सकते हैं, लेकिन आप इसे असाइन नहीं कर सकते हैं।


असल में, त्रुटि print x लाइन पर होती है क्योंकि जब अजगर समारोह को पार्स करता है, यह देखता है कि x तो यह मान लिया गया है करने के लिए x एक स्थानीय चर होना चाहिए असाइन किया गया है। जब आप print x लाइन पर जाते हैं, तो पाइथन स्थानीय x को देखने का प्रयास करता है लेकिन यह वहां नहीं है। यह बाइटकोड का निरीक्षण करने के लिए dis.dis का उपयोग करके देखा जा सकता है। यहां, पायथन LOAD_FAST निर्देश का उपयोग करता है जो LOAD_GLOBAL निर्देश के बजाय स्थानीय चर के लिए उपयोग किया जाता है जिसका उपयोग गैर-स्थानीय चर के लिए किया जाता है।

आम तौर पर, यह एक NameError का कारण होता है, लेकिन अजगर func_closure या func_globals में x की तलाश द्वारा एक छोटे से अधिक उपयोगी हो की कोशिश करता है। यदि उनमें से किसी एक में x पाता है, तो यह आपको UnboundLocalError उठाता है ताकि आपको क्या हो रहा है इसके बारे में बेहतर जानकारी मिल सके - आपके पास एक स्थानीय चर है जो नहीं मिला ("बाध्य नहीं है")।

python3.x केवल

python2.x - python3.x पर, उन विशेषताओं __closure__ को बदल दिया है और __globals__ क्रमशः

+2

लेकिन फिर यह 'x = x + 1' के बजाय 'प्रिंट x' पर क्यों विफल रहता है? –

+0

@icando: 'प्रिंट x' पहले आता है? – Ryan

+0

@rynah: लेकिन यह जवाब कहता है कि समस्या * * x' को असाइन करने के साथ है। यदि ऐसा है, तो 'प्रिंटिंग' करना ठीक काम करना चाहिए, है ना? – cHao

3

कारण पहले में है उदाहरण के लिए आपने एक असाइनमेंट ऑपरेशन, x = x + 1 का उपयोग किया, इसलिए जब कार्यों को परिभाषित किया गया था तो पाइथन ने सोचा था कि x स्थानीय चर है। लेकिन जब आप वास्तव में फ़ंक्शन पायथन को स्थानीय स्तर पर आरएचएस पर x के लिए कोई मान नहीं ढूंढ पाए, तो एक त्रुटि उठाई गई।

काम करने के बजाय अपने दूसरे उदाहरण में आप बस, एक परिवर्तनशील वस्तु बदल तो अजगर कोई आपत्ति उठाना कभी नहीं होगा और enclosing दायरे से x[0] के मूल्य लायेगा (वास्तव में यह इसके लिए सबसे पहले enclosing दायरे में लग रहा है, तो वैश्विक गुंजाइश और आखिर में बिल्टिन में, लेकिन जैसे ही यह पाया गया बंद हो जाता है)।

पायथन 3x में आप nonlocal कीवर्ड का उपयोग करके इसे संभाल सकते हैं और py2x में आप या तो आंतरिक फ़ंक्शन को मान पास कर सकते हैं या फ़ंक्शन विशेषता का उपयोग कर सकते हैं।

का उपयोग करते हुए समारोह विशेषता:

def main(): 
    main.x = 1 
    def f(): 
     main.x = main.x + 1 
     print main.x 
    return f 

main()() #prints 2 

स्पष्ट रूप से मूल्य पासिंग:

def main(): 
    x = 1 
    def f(x): 
     x = x + 1 
     print x 
     return x 
    x = f(x)  #pass x and store the returned value back to x 

main() #prints 2 

py3x में nonlocal का उपयोग करना:

def main(): 
    x = 1 
    def f(): 
     nonlocal x 
     x = x + 1 
     print (x) 
    return f 

main()() #prints 2 
+0

फ़ंक्शन विशेषता की अनुशंसा नहीं की जा सकती है। चूंकि विशेषता की केवल एक प्रति है, इसलिए कोई भी रिकर्सन अप्रत्याशित परिणाम देगा। –

0

समस्या कतार में है

x = x + 1 

यह पहली बार x फ़ंक्शन f() में असाइन किया जा रहा है, यह संकलक बताता है कि x एक स्थानीय नाम है। यह पिछले पंक्ति print x के साथ संघर्ष करता है, जो स्थानीय x के किसी भी पिछले असाइनमेंट को नहीं ढूंढ सकता है। वह जगह है जहां आपकी त्रुटि UnboundLocalError: local variable 'x' referenced before assignment से आता है।

ध्यान दें कि त्रुटि तब होती है जब संकलक में print x में ऑब्जेक्ट का पता लगाने की कोशिश करता है। तो print x निष्पादित नहीं होता है।

x[0] = x[0] + 1 

कोई नए नाम से बदलें यह जोड़ा जाता है। तो संकलक जानता है कि आप f() के बाहर सरणी का जिक्र कर रहे हैं।

संबंधित मुद्दे