2016-01-16 5 views
5

एक विशाल स्ट्रिंग से अक्षर निकालने की समस्या पर विचार करें।तारों में शामिल हों। जनरेटर या सूची समझ?

एक तरह से करने के लिए

''.join([c for c in hugestring if c.isalpha()]) 

है तंत्र स्पष्ट है: सूची समझ वर्णों की एक सूची उत्पन्न करता है। जॉइन विधि जानता है कि सूची की लंबाई तक पहुंचने में कितने पात्रों को शामिल करने की आवश्यकता है। ऐसा करने के लिए

अन्य तरीका

''.join(c for c in hugestring if c.isalpha()) 

यहाँ एक जनरेटर में जनरेटर समझ परिणाम है। जॉइन विधि यह नहीं जानता कि यह कितने पात्रों में शामिल होने जा रहा है क्योंकि जनरेटर के पास लेन विशेषता नहीं है। इसलिए शामिल होने का यह तरीका सूची समझ विधि से धीमा होना चाहिए।

लेकिन पायथन में परीक्षण से पता चलता है कि यह धीमा नहीं है। ऐसा क्यों है? कोई भी बता सकता है कि जेनरेटर पर कैसे काम करता है।

स्पष्ट है:

sum(j for j in range(100)) 

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

उत्तर

10

जब: यह संभव है केवल विशिष्ट उदाहरण आप परीक्षण कर रहे हैं (उदाहरण के लिए, अगर हालत आप परीक्षण कर रहे कम बार होता है, कीमत CPython लंबाई नहीं जानने के लिए भुगतान करता समय से आगे छोटा हो सकता है) पर निर्भर करता है आप str.join(gen) पर कॉल करते हैं जहां gen जनरेटर है, परिणामस्वरूप अनुक्रम की लंबाई की जांच करने के लिए पाइथन list(gen) के बराबर करता है।

विशेष रूप से, अगर आप look at the code implementing str.join in CPython, तो आप इस कॉल देखेंगे:

fseq = PySequence_Fast(seq, "can only join an iterable"); 

PySequence_Fast करने के लिए कॉल एक सूची में seq तर्क रूपांतरण करता है तो यह पहले से ही एक सूची या टपल नहीं था।

तो, आपके कॉल के दो संस्करण लगभग समान रूप से संभाले जाते हैं। सूची की समझ में, आप स्वयं सूची बना रहे हैं और इसे join में पास कर रहे हैं। जनरेटर अभिव्यक्ति संस्करण में, जनरेटर वस्तु आप में पास सही join के शुरू में एक list में बदल जाता है, और .. कोड के बाकी दोनों संस्करणों के लिए एक ही संचालित

+0

तो गति ओपी नोटिस में अंतर पूरी तरह परिस्थिति में होना चाहिए, है ना? –

+0

@ Ev.Kounis: प्रश्नकर्ता ने कहा कि दो संस्करण गति में समान थे ("** ** ** धीमा नहीं"), जो समझ में आता है कि वे 'जॉइन' द्वारा लिया गया समय और सूची समझ से लिया गया समय दोनों को माप रहे थे साथ में। यदि आपने केवल 'join' को मापा है, तो जेनरेटर अभिव्यक्ति संस्करण धीमा हो जाएगा क्योंकि इसे किसी भी स्ट्रिंग में शामिल होने से पहले पूरे जेनरेटर को किसी सूची में डंप करना होगा। इसमें दूसरी समझ में सूची समझ के निर्माण के रूप में उतना ही समय लगेगा। – Blckknght

1

join() अनुक्रम के तत्वों के अनुक्रमिक अनुक्रम के रूप में कार्यान्वित करने की आवश्यकता नहीं है जो लंबे और लंबे संचित स्ट्रिंग (जो वास्तव में लंबे अनुक्रमों के लिए बहुत धीमी होगी); यह सिर्फ एक ही परिणाम उत्पन्न करने की जरूरत है। तो join() शायद कुछ आंतरिक मेमोरी बफर के पात्रों को जोड़ रहा है, और अंत में एक स्ट्रिंग बना रहा है। दूसरी तरफ, सूची समझ निर्माण, सूची को पहले बनाने की आवश्यकता है (hugestring के जेनरेटर को ट्रैवर्स करके), और उसके बाद केवल join() अपना काम शुरू करें।

इसके अलावा, मुझे संदेह है कि join() सूची की लंबाई को देखता है, क्योंकि यह नहीं पता कि प्रत्येक तत्व एक ही वर्ण है (ज्यादातर मामलों में, यह नहीं होगा) - यह शायद सूची से जनरेटर प्राप्त करता है ।

+2

संदर्भ दुभाषिया सी परत कोड एक खेल इस उद्देश्य के लिए पूर्ण (लेकिन निजी) '_PyUnicodeWriter' API (और अन्य समान" निर्माण स्ट्रिंग टुकड़े टुकड़े "मामलों)। जावा की 'स्ट्रिंगबिल्डर' कक्षा से तुलना करें। – ShadowRanger

+1

उसने कहा, ऐसा लगता है कि @ ब्लैकनाइट सही है; यह इनपुट को 'सूची' में आंतरिक रूप से परिवर्तित कर रहा है यदि यह पहले से ही 'सूची' या 'tuple' नहीं है। यह ऐसा लगता है कि यह अंतिम मूल्य की लंबाई की गणना करने के लिए एक प्रीकंप्यूट पास करता है ताकि '_PyUnicodeWriter' का उपयोग करने के बजाय, जितना आवश्यक हो उतना सटीक रूप से प्रीलाकेट किया जा सके। – ShadowRanger

1

कम से कम मेरी मशीन पर, सूची की समझ के मामले में सूची समझ तेज है, संभवतः ''.join स्मृति आवंटन को अनुकूलित करने में सक्षम होने के कारण।

In [18]: s = ''.join(np.random.choice(list(string.printable), 1000000)) 

In [19]: %timeit ''.join(c for c in s if c.isalpha()) 
10 loops, best of 3: 69.1 ms per loop 

In [20]: %timeit ''.join([c for c in s if c.isalpha()]) 
10 loops, best of 3: 61.8 ms per loop 
+1

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

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