2017-06-01 22 views
57

मैंने गलती से इस तरह की कुछ कोड लिखा है:लूप्स में मनमाने ढंग से लक्षित अभिव्यक्तियों की अनुमति क्यों है?

foo = [42] 
k = {'c': 'd'} 

for k['z'] in foo: # Huh?? 
    print k 

लेकिन मेरे आश्चर्य करने के लिए, यह एक सिंटैक्स त्रुटि नहीं था। इसके बजाय, यह {'c': 'd', 'z': 42} प्रिंट करता है।

i = iter(foo) 
while True: 
    try: 
     k['z'] = i.next() # literally translated to assignment; modifies k! 
     print k 
    except StopIteration: 
     break 

लेकिन ... क्यों इस भाषा द्वारा अनुमति दी है:

मेरे अनुमान कि कोड सचमुच की तरह कुछ करने के लिए अनुवाद किया है है? मुझे उम्मीद है कि for-stmt's target expression में पहचानकर्ताओं के केवल एक पहचानकर्ता और टुपल्स की अनुमति दी जानी चाहिए। क्या ऐसी कोई परिस्थिति है जिसमें यह वास्तव में उपयोगी है, न केवल अजीब गेटचा?

+0

इसे अनुमति देने के लिए उपयोगी नहीं होना चाहिए। एक शब्दकोश में एक कुंजी मान मान्य पहचानकर्ता है। चूंकि नामस्थान हैं, स्वयं, शब्दकोश, इसे बदलने के लिए बहुत सारे पायथन इंटर्नल्स को बदलने की आवश्यकता होगी। और कुछ ऐसा ठीक करने के लिए जो केवल एक मुद्दा है यदि आप आकस्मिक कोड लिखते हैं जिसके परिणामस्वरूप कोई त्रुटि नहीं होती है। –

+0

@AlanLeuthard मैं देख रहा हूं कि आप क्या कह रहे हैं (और आपका उत्तर अच्छी तरह से बाहर निकलता है) लेकिन अधिक सटीक होने के लिए, एक ['पहचानकर्ता '] (https://docs.python.org/2/reference/lexical_analysis.html# व्याकरण-टोकन-पहचानकर्ता) केवल अक्षरों और अंकों का अनुक्रम हो सकता है, यानि एक वास्तविक चर * नाम * की बजाय किसी अन्य असाइन करने योग्य अभिव्यक्ति की बजाय ['सदस्यता'] (https://docs.python.org/2/ संदर्भ/expressions.html # व्याकरण टोकन-सदस्यता)। – jtbandes

+0

हाँ। पहचानकर्ता के बजाय लक्ष्य कहा जाना चाहिए –

उत्तर

30

for पाश तो क्या एक वेनिला असाइनमेंट की एलएचएस पर काम करता है के साथ काम करना चाहिए असाइनमेंट के मानक नियमों का पालन करती for:

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

के लिए for निर्माण बस लक्ष्य जो आपके नमूना कोड के मामले में STORE_SUBSCR है निर्दिष्ट करने के लिए अंतर्निहित तंत्र सम्मन:

+०१२३५१६४१०
>>> foo = [42] 
>>> k = {'c': 'd'} 
>>> dis.dis('for k["e"] in foo: pass') 
    1   0 SETUP_LOOP    16 (to 18) 
       2 LOAD_NAME    0 (foo) 
       4 GET_ITER 
     >> 6 FOR_ITER     8 (to 16) 
       8 LOAD_NAME    1 (k) 
      10 LOAD_CONST    0 ('e') 
      12 STORE_SUBSCR <-------------------- 
      14 JUMP_ABSOLUTE   6 
     >> 16 POP_BLOCK 
     >> 18 LOAD_CONST    1 (None) 
      20 RETURN_VALUE 

लेकिन मेरे आश्चर्य करने के लिए, यह एक सिंटैक्स त्रुटि

जाहिर है, जो कुछ भी जैसे कि निम्न एक नियमित रूप से काम में काम करता है नहीं था:

पूर्ण टुकड़ा काम:

>>> for [][:] in []: 
... pass 
... 
>>> 

सूची सदस्यता

>>> for [2][0] in [42]: 
... pass 
... 
>>> 

शब्दकोश सदस्यता आदि मान्य उम्मीदवार लक्ष्य, अकेला अपवाद एक श्रृंखलित काम होने के साथ हो सकता है; हालांकि, मैं चुपचाप सोचता हूं कि चेनिंग करने के लिए कोई गंदा वाक्यविन्यास पका सकता है।


मैं केवल एक पहचानकर्ता और पहचानकर्ता

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

हालांकि, खोल विस्तारित (अजगर 3) जो नियमित रूप से कार्य में बहुत उपयोगी है भी पाश के लिए एक में समान रूप से काम आता है:

>>> lst = [[1, '', '', 3], [3, '', '', 6]] 
>>> for x, *y, z in lst: 
... print(x,y,z) 
... 
1 ['', ''] 3 
3 ['', ''] 6 

विभिन्न लक्ष्यों यहां भी बुलाया जाता है करने के लिए असाइन करने के लिए इसी तंत्र; कई STORE_NAME रों:

>>> dis.dis('for x, *y, z in lst: pass') 
    1   0 SETUP_LOOP    20 (to 22) 
       2 LOAD_NAME    0 (lst) 
       4 GET_ITER 
     >> 6 FOR_ITER    12 (to 20) 
       8 EXTENDED_ARG    1 
      10 UNPACK_EX    257 
      12 STORE_NAME    1 (x) <----- 
      14 STORE_NAME    2 (y) <----- 
      16 STORE_NAME    3 (z) <----- 
      18 JUMP_ABSOLUTE   6 
     >> 20 POP_BLOCK 
     >> 22 LOAD_CONST    0 (None) 
      24 RETURN_VALUE 

पता चलता है कि एक for मुश्किल से सरल काम क्रमिक निष्पादित बयान है चला जाता है।

+2

यह देखते हुए कि जंजीर असाइनमेंट ' (x = (y = v)) '' '(x = y) = v', मुझे संदेह है कि आप इसे बनाने के लिए वाक्यविन्यास के साथ आ सकते हैं – Bergi

+0

@ बर्गि हां, यह एक कठिन कॉल है, लेकिन कोई कुछ करीब हो सकता है के साथ चेनिंग: 'एक्स के लिए, ज़िप में वाई (* टीई (iterable, no_of_targets)): ... '। ओटीओएच, मुझे लगता है कि 'x' पहले असाइन किया जाना चाहिए, 'y' नहीं, ताकि' x = y = v' 'x = v होगा; y = v' –

5

क्या कोई ऐसी स्थिति है जिसमें यह वास्तव में उपयोगी है?

दरअसल। कभी itertools.combinations से छुटकारा पाना चाहते थे?

def combinations (pool, repeat):   
    def combinations_recurse (acc, pool, index = 0): 
     if index < len(acc): 
      for acc[index] in pool: 
       yield from combinations_recurse(acc, pool, index + 1) 
     else: 
      yield acc 

    yield from combinations_recurse([pool[0]] * repeat, pool) 

for comb in combinations([0, 1], 3): 
    print(comb) 
26

निम्नलिखित कोड समझ जाएगा, है ना?

foo = [42] 
for x in foo: 
    print x 

for पाश सूची foo से अधिक पुनरावृति और बदले में वर्तमान नाम स्थान में नाम x करने के लिए प्रत्येक वस्तु आवंटित होगा। परिणाम एक एकल पुनरावृत्ति और 42 की एक एकल प्रिंट होगा।

अपने कोड में x के स्थान पर, आप k['z'] है। k['z'] एक वैध भंडारण नाम है। मेरे उदाहरण में x की तरह, यह अभी तक मौजूद नहीं है। यह प्रभाव में ग्लोबल नेम स्पेस में k.z है। पाश k.z या k['z'] बनाता है और उसी तरह यह x बना सकते हैं और मेरे उदाहरण में यह करने के लिए मान निर्दिष्ट हैं में मानों यह यह करने के लिए foo में पाता प्रदान करती है। आप foo में अधिक मान ... था

foo = [42, 51, "bill", "ted"] 
k = {'c': 'd'} 
for k['z'] in foo: 
    print k 

परिणाम होगा में:

{'c': 'd', 'z': 42} 
{'c': 'd', 'z': 51} 
{'c': 'd', 'z': 'bill'} 
{'c': 'd', 'z': 'ted'} 

आप पूरी तरह से वैध आकस्मिक कोड लिखा था। यह भी अजीब कोड नहीं है। आप आमतौर पर चर के रूप में शब्दकोश प्रविष्टियों के बारे में नहीं सोचते हैं।

भले ही कोड अजीब नहीं है, तो इस तरह के असाइनमेंट की अनुमति कैसे उपयोगी हो सकती है?

key_list = ['home', 'car', 'bike', 'locker'] 
loc_list = ['under couch', 'on counter', 'in garage', 'in locker'] 
chain = {} 
for index, chain[key_list[index]] in enumerate(loc_list): 
    pass 

शायद ऐसा करने का सबसे अच्छा तरीका नहीं है, लेकिन दो बराबर लंबाई सूचियों को एक शब्दकोश में एक साथ रखता है। मुझे यकीन है कि अन्य चीजें हैं जो अधिक अनुभवी प्रोग्रामर ने लूप के लिए शब्दकोश कुंजीपटल कुंजी असाइनमेंट का उपयोग किया है। शायद ...

+2

इस स्पष्टीकरण ने बाकी की तुलना में इसे अधिक स्पष्ट बना दिया। अच्छा कार्य! – tpg2114

+1

जबकि आपका पहला कोड नमूना प्रश्न में मेरे "अनुमान" से बहुत दूर नहीं जाता है, यह स्पष्टीकरण बहुत स्पष्ट है और मुझे यकीन है कि यह भविष्य के पाठकों के लिए उपयोगी होगा ... यदि कोई भी कभी गलती से यह लिखने के लिए होता है :) धन्यवाद! – jtbandes

+2

हालांकि 'dict 'ज़िप (key_list, loc_list))' '' लूप के इस दुरुपयोग की तुलना में संभवतः समझना आसान है। – Graipher

6

प्रत्येक नाम सिर्फ एक शब्दकोश कुंजी है *।

for x in blah: 

ठीक है

for vars()['x'] in blah: 

* (यद्यपि कि शब्दकोश में इस तरह के समारोह कार्यक्षेत्रों में के रूप में कुछ अनुकूलन, के मामले में, एक वास्तविक dict वस्तु के रूप में लागू करने की आवश्यकता नहीं)।

+0

ये दो कोड उदाहरण "ठीक" नहीं हैं, क्योंकि पहला कार्य कार्यों के अंदर काम करता है जबकि दूसरा कोई नहीं है, लेकिन आपका मुख्य बिंदु खड़ा है। –

+0

शायद मेरी अंग्रेजी समस्या है। मैंने नहीं कहा, और न ही मैं यह कहना चाहता हूं कि वे कोड नमूने ठीक वही हैं। (बेशक, के बाद से आप शायद देखा है कि मैं फुटनोट नीचे शामिल थे।) मैं सिर्फ मतलब, "एक नाम करने के लिए काम की सटीक अर्थ विज्ञान बस कुछ शब्दकोश पर एक setitem कि" यद्यपि कि शब्दकोश एक के रूप में लागू करने की आवश्यकता नहीं असली पायथन dict वस्तु। – Veky

+2

हाँ, काफी स्पष्ट, एक उदाहरण के रूप में यह कुल समझ में आता है। मैं सिर्फ देखा है बहुत सारे लोग इस तथ्य शब्दकोश 'स्थानीय लोगों द्वारा दिया() को संशोधित कि द्वारा भ्रमित किया जा रहा' (या 'वार्स()') के अंदर कार्यों काम नहीं करता है, तो मैं इसके बारे में जितना संभव हो स्पष्ट होना पसंद करते हैं। –

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