पायथन में [] के बिना सूची की समझ


85

एक सूची में शामिल होना:

>>> ''.join([ str(_) for _ in xrange(10) ])
'0123456789'

join एक पुनरावृत्ति लेना चाहिए।

जाहिर है, joinतर्क है [ str(_) for _ in xrange(10) ], और यह एक सूची समझ है

इसे देखो:

>>>''.join( str(_) for _ in xrange(10) )
'0123456789'

अब, joinतर्क सिर्फ str(_) for _ in xrange(10), नहीं [], लेकिन परिणाम समान है।

क्यों? है str(_) for _ in xrange(10)भी एक सूची या एक iterable उत्पादन?


1
मुझे लगता joinहै कि यह सबसे अधिक संभावना है कि सी में लिखा है और इसलिए एक सूची समझ की तुलना में बहुत तेजी से चलता है ... परीक्षण का समय!
जोएल कॉर्नेट

जाहिरा तौर पर, मैंने आपके प्रश्न को पूरी तरह से गलत पढ़ा है। यह मेरे लिए एक जनरेटर लौट रहा है ...
जोएल कॉर्नेट

18
बस एक नोट: _कोई विशेष अर्थ नहीं है, यह एक नियमित चर नाम है। इसका उपयोग अक्सर थ्रो-दूर नाम के रूप में किया जाता है लेकिन यह मामला नहीं है (आप चर का उपयोग कर रहे हैं)। मैं एक कोड में इसका उपयोग करने से बचूंगा (इस तरह कम से कम)।
rplnt

जवाबों:


67
>>>''.join( str(_) for _ in xrange(10) )

इसे एक जनरेटर अभिव्यक्ति कहा जाता है , और पीईपी 289 में समझाया गया है ।

जनरेटर अभिव्यक्तियों और सूची समझ के बीच मुख्य अंतर यह है कि पूर्व स्मृति में सूची नहीं बनाते हैं।

ध्यान दें कि अभिव्यक्ति लिखने का तीसरा तरीका है:

''.join(map(str, xrange(10)))

1
जैसा कि मुझे पता है, एक जनरेटर का उत्पादन टपल-जैसी अभिव्यक्ति के माध्यम से किया जा सकता है, जैसे ( str(_) for _ in xrange(10) )। लेकिन मुझे भ्रम था कि, क्यों ()छोड़ा जा सकता है join, जिसका अर्थ है, कोड `'की तरह होना चाहिए। (x (10)) में _ के लिए ((_)), सही?
अलकॉट

1
@Accott ट्यूपल्स के बारे में मेरी समझ यह है कि वे वास्तव में अल्पविराम द्वारा अलग-अलग अभिव्यक्तियों की सूची से परिभाषित होते हैं न कि कोष्ठक; कोष्ठक केवल एक असाइनमेंट में मानों को नेत्रहीन रूप से समूहित करने के लिए या वास्तव में मानों को समूहित करने के लिए हैं यदि ट्यूपल कुछ अन्य अल्पविराम से अलग की गई सूची में, फ़ंक्शन कॉल की तरह जा रहे थे। यह अक्सर जैसे कोड चलाकर प्रदर्शित किया जाता है tup = 1, 2, 3; print(tup)। इसे ध्यान में रखते हुए, forएक अभिव्यक्ति के हिस्से के रूप में उपयोग करने से जनरेटर का निर्माण होता है और इसे गलत लिखित लूप से अलग करने के लिए कोष्ठक होता है।
एरिक एड लोहमर

132

अन्य उत्तरदाता यह जवाब देने में सही थे कि आपने एक जनरेटर अभिव्यक्ति की खोज की थी (जिसमें सूची बोध के समान अंकन है लेकिन आसपास के वर्ग कोष्ठक के बिना)।

सामान्य तौर पर, जीनएक्सप्स (जैसा कि वे प्यार से जानते हैं) सूची की समझ से अधिक स्मृति कुशल और तेज हैं।

फिर भी, यह मामला है ''.join(), एक सूची समझ तेज और अधिक स्मृति कुशल दोनों है। कारण यह है कि जुड़ने के लिए डेटा पर दो पास बनाने की आवश्यकता होती है, इसलिए इसे वास्तव में एक वास्तविक सूची की आवश्यकता होती है। यदि आप इसे एक देते हैं, तो यह तुरंत अपना काम शुरू कर सकता है। यदि आप इसे इसके बजाय एक जीनएक्सपी देते हैं, तो यह तब तक काम शुरू नहीं कर सकता है जब तक कि यह जेनएक्सएक्स को थकावट से स्मृति में एक नई सूची नहीं बनाता है:

~ $ python -m timeit '"".join(str(n) for n in xrange(1000))'
1000 loops, best of 3: 335 usec per loop
~ $ python -m timeit '"".join([str(n) for n in xrange(1000)])'
1000 loops, best of 3: 288 usec per loop

नक्शे में itertools.imap की तुलना करते समय एक ही परिणाम होता है :

~ $ python -m timeit -s'from itertools import imap' '"".join(imap(str, xrange(1000)))'
1000 loops, best of 3: 220 usec per loop
~ $ python -m timeit '"".join(map(str, xrange(1000)))'
1000 loops, best of 3: 212 usec per loop

4
@lazyr आपकी दूसरी टाइमिंग बहुत अधिक काम कर रही है। एक सूची के चारों ओर एक जीनएक्सपी को न लपेटें - सीधे एक जीनएक्सपी का उपयोग करें। कोई आश्चर्य नहीं कि आपको विषम समय मिला।
रेमंड हेटिंगर

11
क्या आप बता सकते हैं कि ''.join()स्ट्रिंग बनाने के लिए इटरेटर पर 2 पास की आवश्यकता क्यों है ?
डिग्वोलोविन

27
@ovgolovin मुझे लगता है कि पहला पास स्ट्रिंग्स की लंबाई को योग करने के लिए है ताकि समवर्ती स्ट्रिंग के लिए स्मृति की सही मात्रा आवंटित करने में सक्षम हो, जबकि दूसरा पास आवंटित स्ट्रिंग्स में व्यक्तिगत स्ट्रिंग्स की प्रतिलिपि बनाने के लिए है।
लॉरिट्ज वी। थुलो

20
@lazyr यह अनुमान सही है। यह वही है जो str.join करता है :-)
रेमंड

4
कभी-कभी मैं वास्तव में एसओ पर एक विशिष्ट जवाब "पसंदीदा" करने की क्षमता को याद करता हूं।
वायुसेना

5

आपका दूसरा उदाहरण सूची की समझ के बजाय एक जनरेटर अभिव्यक्ति का उपयोग करता है। अंतर यह है कि सूची की समझ के साथ, एक सूची पूरी तरह से निर्मित और पारित की जाती है .join()। जनरेटर अभिव्यक्ति के साथ, आइटम एक-एक करके उत्पन्न होते हैं और इसके द्वारा खपत होती है .join()। उत्तरार्द्ध कम मेमोरी का उपयोग करता है और आम तौर पर तेज होता है।

जैसा कि होता है, सूची निर्माणकर्ता किसी जनरेटर अभिव्यक्ति सहित खुशी से किसी भी पुनरावृत्ति का उपभोग करेगा। इसलिए:

[str(n) for n in xrange(10)]

के लिए बस "कृत्रिम चीनी" है:

list(str(n) for n in xrange(10))

दूसरे शब्दों में, एक सूची बोध एक जनरेटर अभिव्यक्ति है जो एक सूची में बदल जाती है।


2
क्या आप सुनिश्चित हैं कि वे हुड के बराबर हैं? टाइमिट कहता है:: [str(x) for x in xrange(1000)]262 usec list(str(x) for x in xrange(1000)),: 304 usec।
लॉरिट्ज वी। थुलो

2
@ लज़ीर तुम सही हो। सूची की समझ तेज है। और यही कारण है कि सूची संकलन पायथन 2.x में लीक हो गया। यह वही है जो जीवीआर ने लिखा था: "" यह सूची समझ के मूल कार्यान्वयन की एक कलाकृति थी; यह वर्षों तक पायथन के "गंदे छोटे रहस्यों" में से एक था। यह एक जानबूझकर समझौता blindingly उपवास सूची comprehensions बनाने के लिए के रूप में बाहर शुरू किया, और जबकि यह शुरुआती के लिए एक आम ख़तरा नहीं था, यह निश्चित रूप से डंक मार लोगों को कभी-कभी "। Python-history.blogspot.com/2010/06/...
ovgolovin

3
@ovgolovin इसका कारण यह है कि सूची का काम तेज है, क्योंकि इसमें काम शुरू करने से पहले एक सूची बनानी होगी। "रिसाव" जिसका आप उल्लेख करते हैं कि कोई गति समस्या नहीं है - इसका अर्थ है कि लूप इंडक्शन चर को सूची के बाहर उजागर किया गया है।
रेमंड हेटिंगर

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

4
@ovgolovin आपने एक listcomp कार्यान्वयन विवरण से एक गलत छलांग लगाई है कि str.join इस तरह से क्यों करता है। Str.join कोड में पहली लाइनों में से एक है seq = PySequence_Fast(orig, "");और यह एकमात्र कारण है कि iterators, str.join () कॉल करते समय सूचियों या ट्यूपल्स की तुलना में अधिक धीमी गति से चलते हैं। यदि आप इस पर आगे चर्चा करना चाहते हैं, तो आपका स्वागत है एक चैट शुरू करने के लिए। मुद्दे के साथ परिचित)।
रेमंड हेटिंगर

5

जैसा कि उल्लेख किया गया है कि यह एक जनरेटर अभिव्यक्ति है

प्रलेखन से:

केवल एक तर्क के साथ कॉल पर कोष्ठकों को छोड़ा जा सकता है। विस्तार के लिए अनुभाग कॉल देखें ।


4

यदि यह पैरेन्स में है, लेकिन कोष्ठक नहीं है, तो यह तकनीकी रूप से एक जनरेटर अभिव्यक्ति है। जेनरेटर के भाव पहली बार अजगर 2.4 में पेश किए गए थे।

http://wiki.python.org/moin/Generators

शामिल होने के बाद का हिस्सा ( str(_) for _ in xrange(10) ), अपने आप में, एक जनरेटर अभिव्यक्ति है। आप कुछ ऐसा कर सकते हैं:

mylist = (str(_) for _ in xrange(10))
''.join(mylist)

और इसका मतलब बिल्कुल वही है जो आपने ऊपर के दूसरे मामले में लिखा था।

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

आपके विशेष उदाहरणों में, सूची और जनरेटर संभवतः बहुत अलग तरीके से प्रदर्शन नहीं करते हैं, लेकिन सामान्य तौर पर, जब भी मैं कर सकता हूं, तो मैं जनरेटर अभिव्यक्तियों (और यहां तक ​​कि जनरेटर फ़ंक्शन) का उपयोग करना पसंद करता हूं, क्योंकि जनरेटर के लिए पूरी सूची की तुलना में धीमा होना बेहद दुर्लभ है भौतिकीकरण।


1

यह एक सूची बोध के बजाय एक जनरेटर है। जेनरेटर भी पुनरावृत्त होते हैं, लेकिन पूरी सूची बनाने के बजाय पहले इसे शामिल होने के लिए पास करते हैं, यह xrange में प्रत्येक मूल्य को एक-एक करके पास करता है, जो बहुत अधिक कुशल हो सकता है।


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