2017-06-15 12 views
20

मैं एक ऐसी स्क्रिप्ट पर काम कर रहा था जो फाइलों का एक फ़ोल्डर (20 एमबी से 100 एमबी तक के प्रत्येक आकार) को पढ़ता है, प्रत्येक पंक्ति में कुछ डेटा संशोधित करता है, और वापस लिखता है फ़ाइल की एक प्रति।पायथन लेखसूची() और लिखना() विशाल समय अंतर

with open(inputPath, 'r+') as myRead: 
    my_list = myRead.readlines() 
    new_my_list = clean_data(my_list) 
with open(outPath, 'w+') as myWrite: 
    tempT = time.time() 
    myWrite.writelines('\n'.join(new_my_list) + '\n') 
    print(time.time() - tempT) 
print(inputPath, 'Cleaning Complete.') 

एक 90 एमबी फ़ाइल (~ 900,000 लाइनों) के साथ इस कोड चलाने पर, यह फ़ाइल पर लिखने के लिए ले जाया समय लिया के रूप में 140 सेकंड छपी। यहां मैंने writelines() का उपयोग किया। इसलिए मैंने फ़ाइल लेखन गति को बेहतर बनाने के विभिन्न तरीकों की खोज की, और मैंने जो लेख पढ़े, उनमें से write() और writelines() में कोई अंतर नहीं दिखाना चाहिए क्योंकि मैं एक एकल संयोजित स्ट्रिंग लिख रहा हूं। मैं भी समय केवल निम्नलिखित बयान के लिए ले जाया जाँच:

new_string = '\n'.join(new_my_list) + '\n' 

और यह केवल 0.4 दूसरा ले लिया है, इतनी बड़ी लिया समय क्योंकि सूची बनाने का नहीं था। बस बाहर write() कोशिश करने के लिए मैं इस कोड की कोशिश की:

with open(inputPath, 'r+') as myRead: 
    my_list = myRead.readlines() 
    new_my_list = clean_data(my_list) 
with open(outPath, 'w+') as myWrite: 
    tempT = time.time() 
    myWrite.write('\n'.join(new_my_list) + '\n') 
    print(time.time() - tempT) 
print(inputPath, 'Cleaning Complete.') 

और यह 2.5 सेकंड छपी। write() और writelines() के लिए फ़ाइल लेखन समय में इतना बड़ा अंतर क्यों है, भले ही यह वही डेटा हो? क्या यह सामान्य व्यवहार है या मेरे कोड में कुछ गड़बड़ है? आउटपुट फ़ाइल दोनों मामलों के लिए समान प्रतीत होती है, इसलिए मुझे पता है कि डेटा में कोई हानि नहीं है।

+2

वोट दें। –

+0

इसके अलावा मेरा clean_data() फ़ंक्शन प्रत्येक पंक्ति को स्ट्रिप्स करता है, इसलिए अतिरिक्त न्यूलाइन हटा दी जाती है। –

उत्तर

37

file.writelines()अक्षरों तारों की अपेक्षा करता है। इसके बाद यह लूप तक पहुंच जाता है और प्रत्येक स्ट्रिंग के लिए file.write() पर कॉल करता है। अजगर में, विधि इस करता है:

def writelines(self, lines) 
    for line in lines: 
     self.write(line) 

आप एक बड़ी स्ट्रिंग में से गुजर रहे हैं, और एक स्ट्रिंग बहुत तार का एक iterable है। जब आप इसे व्यक्तिगत वर्ण प्राप्त करते हैं, तो लंबाई 1 के तार होते हैं। तो असल में आप len(data)file.write() पर अलग-अलग कॉल कर रहे हैं। और यह धीमा है, क्योंकि आप एक समय में एक ही अक्षर को लिखने वाले बफर का निर्माण कर रहे हैं।

file.writelines() पर एक स्ट्रिंग में पास न करें। इसके बजाय किसी सूची या टुपल या अन्य पुनरावर्तनीय में पास करें।

आप एक जनरेटर अभिव्यक्ति में जोड़ा न्यू लाइन के साथ अलग-अलग लाइनों में भेज सकता है, उदाहरण के लिए:

myWrite.writelines(line + '\n' for line in new_my_list) 

अब, अगर आप से clean_data() एक डेटा जनरेटर, साफ लाइनों उपज है, तो आप धारा सकता है कर सकता है

with open(inputPath, 'r+') as myRead, open(outPath, 'w+') as myWrite: 
    myWrite.writelines(line + '\n' for line in clean_data(myRead)) 
: इनपुट फ़ाइल, अपने डेटा सफाई जनरेटर के माध्यम से, और किसी भी अधिक स्मृति का उपयोग करने से पढ़ने के लिए आवश्यक और बफ़र्स लिख सकते हैं और फिर भी ज्यादा राज्य अपने लाइनों को साफ करने की जरूरत है है बिना आउटपुट फ़ाइल के लिए बाहर 10

इसके अलावा, मैं न्यूलाइन के साथ लाइनों को उत्सर्जित करने के लिए clean_data() अद्यतन करने पर विचार करना चाहूंगा।

+0

'myWrite.writelines (' \ n'.join (my_list) + '\ n') 'मेरी mylist में x के लिए' myWrite.writelines ("{} \ n" .format (x) हो सकता है) 'तो यह होगा और भी तेज हो; निर्माण करने के लिए कोई सूची नहीं है। –

+0

@ जीन-फ्रैंकोइसफैबर: यही कारण है कि मैं एक सूची या टुपल * या अन्य पुनरावर्तनीय * में गुजरने के लिए कहता हूं। :-) –

+0

@ जीन-फ्रैंकोइसफैबर: हालांकि यह केवल स्मृति-बचत उपाय हो सकता है, क्योंकि बफर अभी भी उन पंक्तियों को तब तक जोड़ता है जब तक कि यह पूर्ण न हो जाए। इससे मदद मिलेगी कि 'clean_data() 'जनरेटर था। –

2

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

क्या यह स्पष्ट है?

+0

लेकिन यह अभी भी एक स्ट्रिंग है ना? यह 1 मूल्य से अधिक हो जाएगा? यह लिखने की गति को कैसे प्रभावित करेगा? –

+1

हाँ, आप 'myWrite.writelines ([' \ n'.join (my_list) + '\ n'] जैसे कुछ सुझाव देना चाहेंगे) ' – mgilson

+3

@ अर्जुन बाल्गोविंद: एक स्ट्रिंग अलग-अलग वर्णों का एक पुनरावृत्ति है। –

5
Martijn जवाब देने के लिए एक पूरक के रूप

, सबसे अच्छा तरीका

बस writelines करने के लिए एक जनरेटर समझ पारित पहली जगह में join का उपयोग कर सूची बनाने के लिए से बचने के लिए हो सकता है, अंत में न्यू लाइन जोड़ने: कोई अनावश्यक स्मृति आवंटन और कोई पाश (समझ के अलावा) अपेक्षित परिणाम के साथ writelines का उपयोग कर और एक अप्रत्याशित चेतावनी पाने की एक मुड़ राह तलाशने के लिए

myWrite.writelines("{}\n".format(x) for x in my_list) 
संबंधित मुद्दे