2008-12-24 18 views
34

उदाहरण के लिए, पाइथन में फ़ाइलें, पुनरावृत्त हैं - वे फ़ाइल में लाइनों पर फिर से चलती हैं। मैं लाइनों की संख्या गिनना चाहता हूँ।क्या अजगर में एक पुनरावृत्त की लंबाई प्राप्त करने के लिए कोई अंतर्निहित तरीका है?

एक त्वरित तरीका यह है है:

lines = len(list(open(fname))) 

बहरहाल, यह स्मृति में पूरे फाइल लोड (एक ही बार में)। यह एक पुनरावर्तक के उद्देश्य को हरा देता है (जिसे केवल वर्तमान पंक्ति को स्मृति में रखना आवश्यक है)।

यह काम नहीं करता:

lines = len(line for line in open(fname)) 

के रूप में जनरेटर लंबाई जरूरत नहीं है।

क्या कोई गिनती फ़ंक्शन परिभाषित करने के लिए ऐसा करने का कोई तरीका है?

def count(i): 
    c = 0 
    for el in i: c += 1 
    return c 

संपादित करें: स्पष्ट करने के लिए, मैं समझता हूँ कि पूरे फ़ाइल को पढ़ने करना होगा! मैं बस इसे स्मृति में एक बार में नहीं चाहता =)।

+0

लाइनों की संख्या को गिनने के लिए आप स्मृति में फ़ाइल को लोड करेंगे! – hasen

+0

सूचियां (सभी अनुक्रम प्रकार) भी पुनरावृत्त हैं।आपका मतलब क्या है "इटरेटर" – hop

+4

@ हसन: हाँ, लेकिन सभी एक बार में नहीं। – Claudiu

उत्तर

53

कम नहीं । यही कारण है कि यह एक पुनरावृत्ति बनाता है और एक सूची नहीं है। यह वास्तव में एक अजगर-विशिष्ट समस्या भी नहीं है। क्लासिक लिंक्ड-सूची डेटा संरचना को देखें। लंबाई ढूंढना एक ओ (एन) ऑपरेशन है जिसमें तत्वों की संख्या को खोजने के लिए पूरी सूची को पुन: सक्रिय करना शामिल है।

def count_iterable(i): 
    return sum(1 for e in i) 

बेशक, आप अपने खुद के iterable वस्तु को परिभाषित कर रहे हैं तो आप हमेशा अपने आप को __len__ लागू कर सकते हैं और एक तत्व कहीं गिनती रखें:

mcrute ऊपर उल्लेख किया है, तो आप शायद अपने कार्य करने के लिए कम कर सकते हैं।

+0

इसे itertools.tee() – hop

+0

@hop के साथ बेहतर किया जा सकता है: कैसे समझाया जाए? –

+0

@ मैट जॉइनर: 'count_iterable' को कॉल करने से इटरेटर का उपभोग होता है, इसलिए आप इसके साथ और कुछ भी करने में सक्षम नहीं होंगे। 'I, i2 = itertools.tee (i)' के साथ इटेटरेटर की प्रतिलिपि बनाना पहले से ही उस समस्या को हल करेगा, लेकिन यह फ़ंक्शन के भीतर काम नहीं करता है, क्योंकि 'count_iterable' साइड इफेक्ट के रूप में अपना तर्क नहीं बदल सकता है (लेकिन परिभाषित करता है एक साधारण 'sum()' के लिए फ़ंक्शन मुझे अनावश्यक रूप से रोकता है ...)। मुझे लगता है कि 2 साल पहले मेरी तर्क कम थी। इसके बारे में सोचते हुए, मैं शायद '.seek (0)' का उपयोग करता हूं (और फ़ंक्शन का नाम बदलता हूं, क्योंकि यह अब मनमाना इटरेटर के लिए काम नहीं करेगा)। – hop

18

आप लाइनों की गिनती आप यह कर सकते हैं की जरूरत है, मैं यह करने के लिए किसी भी बेहतर तरीका के बारे में पता नहीं है:

iterable के माध्यम से पुनरावृत्ति और पुनरावृत्तियों, की संख्या की गणना के
line_count = sum(1 for line in open("yourfile.txt")) 
0

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

+3

मैं पूरी फाइल को पढ़ना चाहता हूं, मैं बस इसे स्मृति में नहीं चाहता – Claudiu

7

बिल्कुल नहीं, सरल कारण यह है कि पुनरावृत्तियों को सीमित होने की गारंटी नहीं है।

पर विचार करें यह पूरी तरह से कानूनी जनरेटर समारोह:

def forever(): 
    while True: 
     yield "I will run forever" 

len([x for x in forever()]) के साथ इस समारोह की अवधि की गणना करने स्पष्ट रूप से काम नहीं करेगा प्रयास कर रहा है।

जैसा कि आपने देखा है, इटरेटर/जेनरेटर का अधिकांश उद्देश्य इसे सभी डेटा को स्मृति में लोड किए बिना बड़े डेटासेट पर काम करने में सक्षम होना है। तथ्य यह है कि आपको तत्काल लंबाई नहीं मिलनी चाहिए, इसे ट्रेडऑफ माना जाना चाहिए।

+19

यह योग(), अधिकतम() और न्यूनतम() के लिए भी सच है लेकिन यह कुल कार्य पुनरावृत्तियों को लेता है। – ttepasse

+3

मैंने इसे मुख्य रूप से "बिल्कुल" के लिए डाउनवॉइड किया, जो कि अभी सत्य नहीं है। कुछ भी जो __len __() लागू करता है, लंबाई है - अनंत, या नहीं। – hop

+0

@hop, सवाल सामान्य मामले में पुनरावृत्तियों के बारे में है। __len__ लागू करने वाले पुनरावृत्तियों एक विशेष मामला हैं। – Triptych

8

मैं अब कुछ समय के लिए इस परिभाषा का उपयोग किया है:

def len(thingy): 
    try: 
     return thingy.__len__() 
    except AttributeError: 
     return sum(1 for item in iter(thingy)) 
+0

यह कभी वापस नहीं आ सकता है ... Triptych का उदाहरण देखें। – bortzmeyer

+0

हाँ, देखभाल के साथ उपयोग – ttepasse

+2

"देखभाल के साथ उपयोग करें" उर्फ ​​"हम सभी वयस्कों की सहमति कर रहे हैं", पायथन के सिद्धांतों में से एक। कम से कम यह एक बार था। –

5

cardinality पैकेज गिनती और किसी भी iterable के आकार की जाँच करने के लिए एक कुशल count() समारोह और कुछ संबंधित कार्यों प्रदान करता है: http://cardinality.readthedocs.org/

import cardinality 

it = some_iterable(...) 
print(cardinality.count(it)) 

आंतरिक रूप से यह सभी वास्तविक लूपिंग और सी स्तर पर तर्क की गणना करने के लिए enumerate() और collections.deque() का उपयोग करता है, जिसके परिणामस्वरूप for पर काफी गतिशीलता होती है पायथन में लूप।

2

यह पता चला है कि इस common problem के लिए एक कार्यान्वित समाधान है। more_itertools से ilen() फ़ंक्शन का उपयोग करने पर विचार करें।

more_itertools.ilen(iterable) 

एक फ़ाइल की पंक्तियों के एक नंबर मुद्रण (हम with संदर्भ प्रबंधक का उपयोग सुरक्षित रूप से फ़ाइलों को बंद करने को संभालने के लिए) का एक उदाहरण:

# Example 
import more_itertools 

with open("foo.py", "r+") as f: 
    print(more_itertools.ilen(f)) 

# Output: 433 

यह उदाहरण के लिए पहले प्रस्तुत समाधान के रूप में ही परिणाम देता है एक फ़ाइल में कुल लाइनों:

# Equivalent code 
with open("foo.py", "r+") as f: 
    print(sum(1 for line in f)) 

# Output: 433 
0

मैं जो n पर कितने रेखांकन पाता है मेरा कुछ कोड, में दो सामान्य प्रक्रियाओं के बीच एक परीक्षण कोने था देखते हैं , यह देखने के लिए कि जेनरेट की गई सूची के तत्वों की गणना करने की कौन सी विधि तेज हो जाती है। ऋषि में जनरेटर ग्राफ (एन) है जो एन अक्षरों पर सभी ग्राफ उत्पन्न करता है। मैंने दो फ़ंक्शंस बनाए जो एक इटरेटर द्वारा दो अलग-अलग तरीकों से प्राप्त सूची की लंबाई प्राप्त करते हैं और टाइम.टाइम() फ़ंक्शन का उपयोग करते हुए उनमें से प्रत्येक (औसत 100 से अधिक परीक्षण रन) का समय प्राप्त करते हैं। कार्यों इस प्रकार थे:

def test_code_list(n): 
    l = graphs(n) 
    return len(list(l)) 

और

def test_code_sum(n): 
    S = sum(1 for _ in graphs(n)) 
    return S 
अब

मैं समय प्रत्येक विधि

import time 

t0 = time.time() 
for i in range(100): 
    test_code_list(5) 
t1 = time.time() 

avg_time = (t1-t0)/10 

print 'average list method time = %s' % avg_time 


t0 = time.time() 
for i in range(100): 
    test_code_sum(5) 
t1 = time.time() 

avg_time = (t1-t0)/100 

print "average sum method time = %s" % avg_time 

औसत सूची विधि समय = 0,0391882109642

औसत योग विधि समय = .0418473792076

तो इस तरह एन = 5 कोष्ठकों पर ग्राफ की संख्या की गणना करना, सूची विधि थोड़ा तेज है (हालांकि 100 टेस्ट रन एक महान नमूना आकार नहीं है)। लेकिन जब मैं वृद्धि हुई सूची की लंबाई एन = 7 कोने पर रेखांकन की कोशिश कर रहा द्वारा गणना की जा रही (रेखांकन यानी बदलते (5) रेखांकन करने के लिए (7)), परिणाम यह था:

औसत सूची विधि समय = ४.१४७५३०५१९९६

औसत योग विधि समय = 3.96504004002

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

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

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