समझ की गुंजाइश के बाद भी सूची बोध नाम को हटा दें। क्या यह सही है?


118

डांट-फटकार के साथ कुछ अप्रत्याशित बातचीत की संभावनाएं हैं। क्या यह अपेक्षित व्यवहार है?

मुझे एक विधि मिली है:

def leave_room(self, uid):
  u = self.user_by_id(uid)
  r = self.rooms[u.rid]

  other_uids = [ouid for ouid in r.users_by_id.keys() if ouid != u.uid]
  other_us = [self.user_by_id(uid) for uid in other_uids]

  r.remove_user(uid) # OOPS! uid has been re-bound by the list comprehension above

  # Interestingly, it's rebound to the last uid in the list, so the error only shows
  # up when len > 1

रोने के जोखिम पर, यह त्रुटियों का एक क्रूर स्रोत है। जैसा कि मैंने नया कोड लिखा है, मैं सिर्फ कभी-कभी रिबॉन्डिंग के कारण बहुत ही अजीब त्रुटियां पाता हूं - अब भी जब मुझे पता है कि यह एक समस्या है। मुझे "हमेशा अंडरस्कोर के साथ सूची बोध में अस्थायी प्रस्तावना" जैसे नियम बनाने की आवश्यकता है, लेकिन यह भी मूर्खतापूर्ण नहीं है।

तथ्य यह है कि इस रैंडम टाइम-बम वेटिंग प्रकार की सूची के सभी अच्छे "उपयोग में आसानी" को नकारता है।


7
-1: "त्रुटियों का क्रूर स्रोत"? मुश्किल से। ऐसे तर्कपूर्ण शब्द का चयन क्यों करें? आम तौर पर सबसे महंगी त्रुटियाँ आवश्यकताओं की गलतफहमी और सरल तर्क त्रुटियां हैं। बहुत सारी प्रोग्रामिंग भाषाओं में इस तरह की त्रुटि एक मानक समस्या रही है। इसे ut क्रूर ’क्यों कहते हैं?
22

44
यह कम से कम आश्चर्य के सिद्धांत का उल्लंघन करता है। यह भी सूची बोध पर अजगर प्रलेखन में उल्लेख नहीं किया गया है जो हालांकि कई बार उल्लेख करते हैं कि वे कितने आसान और सुविधाजनक हैं। अनिवार्य रूप से यह एक लैंड-माइन है जो मेरी भाषा मॉडल के बाहर मौजूद है, और इसलिए मेरे लिए यह असंभव था।
जबावु एडम्स

33
"त्रुटियों का क्रूर स्रोत" के लिए +1। 'क्रूर' शब्द पूरी तरह से उचित है।
नथानिएल

3
एकमात्र "क्रूर" चीज जो मैं यहां देख रहा हूं वह है आपका नामकरण सम्मेलन। यह 80 का दशक नहीं है और आप 3 वर्ण चर नामों तक सीमित नहीं हैं।
उलुपे

5
नोट: दस्तावेज़ बताता है कि सूची-समझ स्पष्ट- forलूप निर्माण और for-लूप्स रिसाव चर के बराबर हैं । तो यह स्पष्ट नहीं था, लेकिन स्पष्ट रूप से कहा गया था।
बकुरीउ

जवाबों:


172

लिस्ट की समझ पाइथन 2 में लूप कंट्रोल वैरिएबल को लीक करना है लेकिन पाइथन में नहीं। 3. यहाँ गुइडो वैन रोसुम (पाइथन के निर्माता) ने इसके पीछे के इतिहास को समझाया है:

हमने सूची संकलन और जनरेटर अभिव्यक्तियों के बीच समानता को बेहतर बनाने के लिए पायथन 3 में एक और बदलाव किया। पायथन 2 में, सूची की समझ "लीक" को लूप नियंत्रण चर को आसपास के दायरे में ले जाती है:

x = 'before'
a = [x for x in 1, 2, 3]
print x # this prints '3', not 'before'

यह सूची समझ के मूल कार्यान्वयन की एक कलाकृति थी; यह सालों से पाइथन के "गंदे छोटे रहस्यों" में से एक था। यह जानबूझकर तेजी से अंधाधुंध बनाने के लिए एक जानबूझकर समझौता के रूप में शुरू हुआ, और जबकि यह शुरुआती लोगों के लिए एक सामान्य नुकसान नहीं था, यह निश्चित रूप से कभी-कभी लोगों को चुभता था। जनरेटर के भावों के लिए हम ऐसा नहीं कर सकते थे। जेनरेटर के उपयोग से जेनरेटर के भाव को लागू किया जाता है, जिसके निष्पादन के लिए अलग निष्पादन फ्रेम की आवश्यकता होती है। इस प्रकार, जनरेटर अभिव्यक्तियाँ (विशेष रूप से यदि वे एक छोटे अनुक्रम पर पुनरावृति करते हैं) सूची समझ से कम कुशल थे।

हालाँकि, पायथन 3 में, हमने जेनरेशन एक्सप्रेशंस के समान कार्यान्वयन की रणनीति का उपयोग करके सूची समझ के "गंदे छोटे रहस्य" को ठीक करने का निर्णय लिया। इस प्रकार, पायथन 3 में, उपरोक्त उदाहरण (प्रिंट का उपयोग करने के लिए संशोधन के बाद (x) :-) 'से पहले प्रिंट करेगा', यह साबित करते हुए कि सूची में 'x' अस्थायी रूप से छाया में है, लेकिन आसपास के 'x' को ओवरराइड नहीं करता है) गुंजाइश।


14
मैं जोड़ूंगा कि हालांकि गुइडो इसे "गंदा छोटा रहस्य" कहता है, कई लोग इसे एक सुविधा मानते हैं, बग नहीं।
स्टीवन रंबलस्की

38
यह भी ध्यान दें कि अब 2.7 में, सेट और डिक्शनरी कॉम्प्रिहेंशन (और जेनरेटर) में निजी स्कोप हैं, लेकिन लिस्ट कॉम्प्रिहेंशन अभी भी नहीं है। हालांकि यह कुछ समझ में आता है कि पहले पायथन 3 से सभी वापस-पोर्ट किए गए थे, लेकिन यह वास्तव में सूची की समझ के साथ विपरीत बनाता है।
मैट बी।

7
मुझे पता है कि यह एक बहुत पुराना सवाल है, लेकिन कुछ लोगों ने इसे भाषा की विशेषता क्यों माना? क्या इस तरह के परिवर्तनशील लीक के पक्ष में कुछ है?
मथियास मूलर

2
for: लूप्स लीक होने के अच्छे कारण हैं, esp। प्रारंभिक के बाद अंतिम मूल्य तक पहुँचने के लिए break- लेकिन समझ के लिए अप्रासंगिक। मुझे कुछ comp.lang.python चर्चाएँ याद हैं जहाँ लोग अभिव्यक्ति के बीच में चर निर्दिष्ट करना चाहते थे। कम पागल रास्ता मिल गया खंड उदाहरण के लिए एकल मूल्य था। sum100 = [s for s in [0] for i in range(1, 101) for s in [s + i]][-1], लेकिन बस एक समझ-स्थानीय संस्करण की जरूरत है और सिर्फ पायथन 3 में ही काम करता है। मुझे लगता है कि "लीक" एक अभिव्यक्ति के बाहर दृश्यमान चर सेट करने का एकमात्र तरीका था। हर कोई सहमत था कि ये तकनीक भयानक हैं :-)
बेनी चेर्नियाव्स्की-पास्किन

1
यहाँ समस्या सूची बोध के आस-पास के दायरे तक नहीं पहुँच पा रही है, लेकिन सूची की समझ के दायरे में बंधने से आसपास का दायरा प्रभावित होता है।
फेलिप गोनक्लेव्स Marques

48

हाँ, सूची समझें "लीक" पायथन 2.x में उनके चर, छोरों के लिए की तरह।

पूर्वव्यापी में, इसे एक गलती के रूप में मान्यता दी गई थी, और इसे जनरेटर के भाव से बचा गया था। EDIT: मैट बी नोट्स के रूप में इसे तब भी टाला गया जब सेट और डिक्शनरी कॉनसेन्सेशन सिंटैक्स को पायथन 3 से वापस लाया गया।

सूची समझ के व्यवहार को छोड़ना पड़ा क्योंकि यह पायथन 2 में है, लेकिन यह पूरी तरह से पायथन 3 में तय किया गया है।

इसका मतलब है कि सभी में:

list(x for x in a if x>32)
set(x//4 for x in a if x>32)         # just another generator exp.
dict((x, x//16) for x in a if x>32)  # yet another generator exp.
{x//4 for x in a if x>32}            # 2.7+ syntax
{x: x//16 for x in a if x>32}        # 2.7+ syntax

यह xअभिव्यक्ति के लिए हमेशा स्थानीय होता है जबकि:

[x for x in a if x>32]
set([x//4 for x in a if x>32])         # just another list comp.
dict([(x, x//16) for x in a if x>32])  # yet another list comp.

पायथन में 2.x सभी xवेरिएबल को आसपास के दायरे में लीक कर देता है।


अजगर 3.8 (?) के लिए अद्यतन : पीईपी 572:= असाइनमेंट ऑपरेटर को पेश करेगा जो जानबूझकर समझ और जनरेटर अभिव्यक्तियों से बाहर लीक करता है! यह अनिवार्य रूप से 2 उपयोग के मामलों से प्रेरित है: जैसे जल्दी समाप्त होने वाले कार्यों से "गवाह" कैप्चर करना any()और all():

if any((comment := line).startswith('#') for line in lines):
    print("First comment:", comment)
else:
    print("There are no comments")

और अद्यतन करने योग्य स्थिति:

total = 0
partial_sums = [total := total + v for v in values]

सटीक स्कूपिंग के लिए परिशिष्ट बी देखें । चर को निकटतम आस def- पास में सौंपा गया है या lambda, जब तक कि फ़ंक्शन इसे घोषित नहीं करता है nonlocalया global


7

हां, असाइनमेंट वहां होता है, जैसे यह forलूप में होता है । कोई नया दायरा नहीं बनाया जा रहा है।

यह निश्चित रूप से अपेक्षित व्यवहार है: प्रत्येक चक्र पर, मूल्य आपके द्वारा निर्दिष्ट नाम के लिए बाध्य है। उदाहरण के लिए,

>>> x=0
>>> a=[1,54,4,2,32,234,5234,]
>>> [x for x in a if x>32]
[54, 234, 5234]
>>> x
5234

एक बार जब यह पहचान लिया जाता है, तो इससे बचना काफी आसान लगता है: समझ के भीतर चर के लिए मौजूदा नामों का उपयोग न करें।


2

दिलचस्प बात यह है कि यह शब्दकोश या प्रभावित समझ को प्रभावित नहीं करता है।

>>> [x for x in range(1, 10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x
9
>>> {x for x in range(1, 5)}
set([1, 2, 3, 4])
>>> x
9
>>> {x:x for x in range(1, 100)}
{1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 11, 12: 12, 13: 13, 14: 14, 15: 15, 16: 16, 17: 17, 18: 18, 19: 19, 20: 20, 21: 21, 22: 22, 23: 23, 24: 24, 25: 25, 26: 26, 27: 27, 28: 28, 29: 29, 30: 30, 31: 31, 32: 32, 33: 33, 34: 34, 35: 35, 36: 36, 37: 37, 38: 38, 39: 39, 40: 40, 41: 41, 42: 42, 43: 43, 44: 44, 45: 45, 46: 46, 47: 47, 48: 48, 49: 49, 50: 50, 51: 51, 52: 52, 53: 53, 54: 54, 55: 55, 56: 56, 57: 57, 58: 58, 59: 59, 60: 60, 61: 61, 62: 62, 63: 63, 64: 64, 65: 65, 66: 66, 67: 67, 68: 68, 69: 69, 70: 70, 71: 71, 72: 72, 73: 73, 74: 74, 75: 75, 76: 76, 77: 77, 78: 78, 79: 79, 80: 80, 81: 81, 82: 82, 83: 83, 84: 84, 85: 85, 86: 86, 87: 87, 88: 88, 89: 89, 90: 90, 91: 91, 92: 92, 93: 93, 94: 94, 95: 95, 96: 96, 97: 97, 98: 98, 99: 99}
>>> x
9

हालाँकि यह 3 में तय किया गया है जैसा कि ऊपर उल्लेख किया गया है।


वह वाक्य रचना पाइथन 2.6 में बिल्कुल भी काम नहीं करती है। क्या आप पायथन 2.7 के बारे में बात कर रहे हैं?
पॉल हॉलिंग्सवर्थ

पाइथन 2.6 की सूची में केवल पाइथन 3.0 है। 3.1 जोड़ा सेट और शब्दकोश समझ और इन 2.7 के लिए रखी गई थी। क्षमा करें यदि वह स्पष्ट नहीं था। यह एक और जवाब के लिए एक सीमा को नोट करने के लिए था, और यह किन संस्करणों पर लागू होता है, यह पूरी तरह से सीधा नहीं है।
क्रिस ट्रैवर्स

जब मैं एक तर्क देने की कल्पना कर सकता हूं कि ऐसे मामले हैं जहां नए कोड के लिए अजगर 2.7 का उपयोग करना समझ में आता है, मैं अजगर 2.6 के लिए भी ऐसा नहीं कह सकता ... भले ही 2.6 आपके ओएस के साथ आया हो, आप इसके साथ फंस नहीं रहे हैं यह। Virtualenv स्थापित करने और नए कोड के लिए 3.6 का उपयोग करने पर विचार करें!
एलेक्स एल

पायथन 2.6 के बारे में बात मौजूदा विरासत प्रणालियों को बनाए रखने में आ सकती है। इसलिए एक ऐतिहासिक नोट के रूप में यह पूरी तरह अप्रासंगिक नहीं है। 3.0 (ick) के साथ समान
क्रिस ट्रैवर्स

खेद है कि अगर मैं कठोर लग रहा हूँ, लेकिन यह किसी भी तरह से सवाल का जवाब नहीं देता है। यह एक टिप्पणी के रूप में बेहतर अनुकूल है।
0xc0de

1

कुछ व्यवहार, अजगर 2.6 के लिए, जब यह व्यवहार वांछनीय नहीं है

# python
Python 2.6.6 (r266:84292, Aug  9 2016, 06:11:56)
Type "help", "copyright", "credits" or "license" for more information.
>>> x=0
>>> a=list(x for x in xrange(9))
>>> x
0
>>> a=[x for x in xrange(9)]
>>> x
8

-1

Python3 में जबकि सूची की समझ में परिवर्तनशील होने के बाद परिवर्तनशील नहीं हो रहा है, लेकिन जब हम सरल-लूप का उपयोग करते हैं तो चर को दायरे से बाहर पुन: असाइन किया जा रहा है।

i = 1 प्रिंट (i) प्रिंट ([i in रेंज (5)]) प्रिंट (i) i का मूल्य केवल 1 ही रहेगा।

अब सिर्फ लूप के लिए उपयोग करें I का मान पुनः दिया जाएगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.