पायथन में एक पुनरावृत्त में तत्वों की संख्या प्राप्त करना


138

क्या यह जानने का एक कुशल तरीका है कि पायथन में एक इट्रेटर में कितने तत्व हैं, सामान्य रूप से, प्रत्येक के माध्यम से पुनरावृत्ति और गिनती के बिना?


जवाबों:


101

नहीं, यह संभव नहीं है।

उदाहरण:

import random

def gen(n):
    for i in xrange(n):
        if random.randint(0, 1) == 0:
            yield i

iterator = gen(10)

लंबाई iteratorअज्ञात है जब तक आप इसके माध्यम से पुनरावृति नहीं करते।


14
वैकल्पिक रूप से, def gen(): yield random.randint(0, 1)अनंत है, इसलिए आप इसके माध्यम से पुनरावृति करके कभी भी लंबाई नहीं पा सकेंगे।
त्रिगुट

1
तो, स्पष्ट को मान्य करने के लिए: पुनरावृति का "आकार" प्राप्त करने का सबसे अच्छा तरीका यह है कि आप पुनरावृति से गुजरे हुए समय की संख्या को गिनें, है ना? इस मामले में, यह होगा numIters = 0 ; while iterator: numIters +=1?
माइक विलियमसन

दिलचस्प है, इसलिए यह
रुकने की

231

यह कोड काम करना चाहिए:

>>> iter = (i for i in range(50))
>>> sum(1 for _ in iter)
50

हालांकि यह प्रत्येक आइटम के माध्यम से पुनरावृति करता है और उन्हें गिनता है, यह ऐसा करने का सबसे तेज़ तरीका है।

यह भी काम करता है जब इट्रेटर के पास कोई आइटम नहीं है:

>>> sum(1 for _ in range(0))
0

बेशक, यह एक अनंत इनपुट के लिए हमेशा के लिए चलता है, इसलिए याद रखें कि पुनरावृत्तियाँ अनंत हो सकती हैं:

>>> sum(1 for _ in itertools.count())
[nothing happens, forever]

इसके अलावा, ध्यान रखें कि ऐसा करने से पुनरावृत्ति समाप्त हो जाएगी , और आगे उपयोग करने के प्रयासों में कोई तत्व नहीं दिखाई देगा । यह पायथन पुनरावृत्त डिजाइन का एक अपरिहार्य परिणाम है। यदि आप तत्वों को रखना चाहते हैं, तो आपको उन्हें किसी सूची या किसी चीज़ में संग्रहीत करना होगा।


10
मुझे ऐसा लग रहा है कि ओपी ठीक से ऐसा नहीं करना चाहता है: पुनरावृति और गणना के माध्यम से।
एडम क्रॉसलैंड

36
तत्वों को पुनरावृत्त में गिनने का यह एक अंतरिक्ष-कुशल तरीका है
कप्तान लेप्टन

9
हालांकि ऐसा नहीं है कि ओपी चाहता है, यह देखते हुए कि उसके प्रश्न का उत्तर नहीं है, यह उत्तर सूची की तात्कालिकता से बचा जाता है, और यह ऊपर सूचीबद्ध कम विधि की तुलना में एक निरंतर रूप से तेज है।
फिलिप नोर्डवॉल

5
मदद नहीं कर सकता: _पर्ल के संदर्भ है $_? :)
एलोइस महदाल

17
@AloisMahdal नहीं। पम्थन में पारंपरिक रूप से नाम का उपयोग _एक डमी चर के लिए किया जाता है, जिसके मूल्य की आपको परवाह नहीं है।
तैमून

67

नहीं, किसी भी विधि से आपको हर परिणाम को हल करने की आवश्यकता होगी। तुम कर सकते हो

iter_length = len(list(iterable))

लेकिन चल रहा है कि एक अनंत पुनरावृत्ति पर बेशक वापस नहीं लौटेगा। यह पुनरावृत्ति का उपभोग भी करेगा और यदि आप सामग्री का उपयोग करना चाहते हैं तो इसे रीसेट करने की आवश्यकता होगी।

हमें यह बताने में कि आप किस वास्तविक समस्या को हल करने का प्रयास कर रहे हैं, इससे हमें अपने वास्तविक लक्ष्य को पूरा करने का बेहतर तरीका मिल सकता है।

संपादित करें: उपयोग करने list()से एक बार में पूरे पुनरावृत्ति को मेमोरी में पढ़ा जा सकता है, जो अवांछनीय हो सकता है। एक और तरीका है

sum(1 for _ in iterable)

किसी अन्य व्यक्ति के रूप में पोस्ट किया गया। जो इसे स्मृति में रखने से बचेंगे।


समस्या यह है कि मैं "pysam" के साथ एक फ़ाइल पढ़ रहा हूँ जिसमें लाखों प्रविष्टियाँ हैं। Pysam एक पुनरावर्तक लौटाता है। एक निश्चित मात्रा की गणना करने के लिए, मुझे यह जानना होगा कि फ़ाइल में कितने रीड हैं, लेकिन मुझे प्रत्येक को पढ़ने की आवश्यकता नहीं है ... यह मुद्दा है।

6
मैं pysam उपयोगकर्ता नहीं हूँ, लेकिन यह शायद "आलसी" फ़ाइल पढ़ रहा है। इसका मतलब यह है कि आप स्मृति में बड़ी फ़ाइल नहीं चाहते हैं। तो अगर आपको पता होना चाहिए कि नहीं। पुनरावृति से पहले के रिकॉर्ड, केवल दो पुनरावृत्तियों को बनाने का तरीका है, और तत्वों को गिनने के लिए पहले एक का उपयोग करें और दूसरे को फ़ाइल पढ़ने के लिए। Btw। इसका उपयोग न करें len(list(iterable))यह सभी डेटा को मेमोरी में लोड करेगा। आप उपयोग कर सकते हैं reduce(lambda x, _: x+1, iterable, 0):। संपादित करें: राशि के साथ Zonda333 कोड भी अच्छा है।
टॉमाज़ व्यस्कोई

1
@ user248237: आप यह क्यों कहते हैं कि आपको यह जानने की आवश्यकता है कि एक निश्चित मात्रा की गणना करने के लिए कितनी प्रविष्टियाँ उपलब्ध हैं? आप उनमें से एक निश्चित राशि को पढ़ सकते हैं और उस निश्चित राशि से कम होने पर मामले का प्रबंधन कर सकते हैं (वास्तव में पुनरावृत्तियों का उपयोग करने के लिए सरल)। क्या कोई अन्य कारण है जो आपको सभी प्रविष्टियों को पढ़ना होगा?
क्रिश

1
@Tomasz ध्यान दें कि कम करना पदावनत है, और पायथन 3 और ऊपर चला जाएगा।
वाइल्डक जूल

7
@Wilduck: यह नहीं चला गया है, बस चले गएfunctools.reduce
डेन्थ

33

आप नहीं कर सकते हैं (एक विशेष पुनरावृत्ति के प्रकार को छोड़कर कुछ विशिष्ट तरीकों को लागू करता है जो इसे संभव बनाते हैं)।

आम तौर पर, आप केवल पुनरावृत् तकों का सेवन करके पुनरावृत्त वस्तुओं की गणना कर सकते हैं। शायद सबसे कुशल तरीकों में से एक:

import itertools
from collections import deque

def count_iter_items(iterable):
    """
    Consume an iterable not reading it into memory; return the number of items.
    """
    counter = itertools.count()
    deque(itertools.izip(iterable, counter), maxlen=0)  # (consume at C speed)
    return next(counter)

(पायथन 3.x के itertools.izipसाथ बदलें zip)।


3
+1: समय की तुलना में sum(1 for _ in iterator), यह लगभग दोगुना था।

1
यह कहना अधिक सटीक है कि यह प्रत्येक आइटम को मेमोरी में पढ़ने और इसे तुरंत छोड़ देने से पुनरावृत्ति करता है।
रॉकलाईट

यह नोट करना महत्वपूर्ण है (जिसे मैंने अनदेखा किया) कि मामलों के लिए तर्क का क्रमzip : यदि आप पास हो जाते हैं zip(counter, iterable), तो आपको वास्तव में पुनरावृत्त गणना से 1 अधिक मिलेगा!
केई डब्ल्यू शी

बहुत अच्छा जवाब। उस पर इनाम देना होगा।
रुत शरबानी

18

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

नहीं तो नहीं। Iterators सिर्फ एक वस्तु है जो केवल next()विधि को उजागर करती है। आप इसे आवश्यकतानुसार कई बार कॉल कर सकते हैं और वे अंततः बढ़ा सकते हैं या नहीं StopIteration। सौभाग्य से, यह व्यवहार अधिकांश समय कोडर के लिए पारदर्शी है। :)


5
पीईपी 424 और पायथन 3.4 के रूप में अब ऐसा नहीं है । __length_hint__अब प्रलेखित है, लेकिन यह एक संकेत है और सटीकता की कोई गारंटी नहीं देता है।
gsnedders

12

मुझे इसके लिए कार्डिनैलिटी पैकेज पसंद है , यह बहुत हल्का है और पुनरावृत्ति के आधार पर उपलब्ध सबसे तेज़ संभव कार्यान्वयन का उपयोग करने की कोशिश करता है।

उपयोग:

>>> import cardinality
>>> cardinality.count([1, 2, 3])
3
>>> cardinality.count(i for i in range(500))
500
>>> def gen():
...     yield 'hello'
...     yield 'world'
>>> cardinality.count(gen())
2

वास्तविक count()कार्यान्वयन इस प्रकार है:

def count(iterable):
    if hasattr(iterable, '__len__'):
        return len(iterable)

    d = collections.deque(enumerate(iterable, 1), maxlen=1)
    return d[0][0] if d else 0

मुझे लगता है कि अगर आप उस फ़ंक्शन का उपयोग करते हैं, तो आप पुन: इट्रेटर पर पुनरावृति कर सकते हैं?
jcollum

12

तो, उन लोगों के लिए जो उस चर्चा का सारांश जानना चाहते हैं। 50 मिलियन-लम्बी जेनरेटर अभिव्यक्ति का उपयोग करते हुए गिनती के लिए अंतिम शीर्ष स्कोर:

  • len(list(gen)),
  • len([_ for _ in gen]),
  • sum(1 for _ in gen),
  • ilen(gen)( more_itertool से ),
  • reduce(lambda c, i: c + 1, gen, 0),

निष्पादन के प्रदर्शन (स्मृति की खपत सहित) के आधार पर, आपको आश्चर्यचकित करेगा:

`` `

1: test_list.py:8: 0.492 KiB

gen = (i for i in data*1000); t0 = monotonic(); len(list(gen))

('सूची, सेकंड', 1.9684218849870376)

2: test_list_compr.py:8: 0.867 KiB

gen = (i for i in data*1000); t0 = monotonic(); len([i for i in gen])

('list_compr, sec', 2.5885991149989422)

3: test_sum.py:8: 0.859 KiB

gen = (i for i in data*1000); t0 = monotonic(); sum(1 for i in gen); t1 = monotonic()

('राशि, सेकंड', 3.441088170016883)

4: more_itertools / more.py: 413: 1.266 KiB

d = deque(enumerate(iterable, 1), maxlen=1)

test_ilen.py:10: 0.875 KiB
gen = (i for i in data*1000); t0 = monotonic(); ilen(gen)

('इलेन, सेकंड', 9.812256851990242)

5: test_reduce.py:8: 0.859 KiB

gen = (i for i in data*1000); t0 = monotonic(); reduce(lambda counter, i: counter + 1, gen, 0)

('कम करें, सेकंड', 13.436614598002052) `` `

तो, len(list(gen))सबसे लगातार और कम स्मृति उपभोज्य है


आपने मेमोरी खपत कैसे मापी?
14

1
क्या आप बता सकते हैं कि क्यों len(list(gen))कम होने के आधार पर दृष्टिकोण से कम मेमोरी का उपभोग करना चाहिए? पूर्व एक नया बनाता listहै जिसमें मेमोरी आवंटन शामिल होता है जबकि बाद वाला नहीं होना चाहिए। इसलिए मुझे उम्मीद है कि बाद में और अधिक मेमोरी कुशल होगी। इसके अलावा, मेमोरी की खपत तत्व प्रकार पर निर्भर करेगी।
14

FYI करें: मैं अजगर के लिए पुन: पेश कर सकते हैं 3.6.8 (एक मैकबुकप्रो पर) कि विधि 1 रनटाइम के संदर्भ में अन्य विधियों को बेहतर बनाती है (I विधि 4 छोड़ दिया)।
नॉरटैनियस

len(tuple(iterable))और भी अधिक कुशल हो सकता है: नेल्सन मिनार का आलेख
VMAtm

9

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


2
एक पुनरावृत्ति एक लिंक्ड सूची की तरह कुछ भी नहीं है। एक पुनरावृत्ति से लौटी हुई वस्तु अगली वस्तु की ओर इशारा नहीं करती है, और ये वस्तुएं (आवश्यक रूप से) मेमोरी में संग्रहीत नहीं होती हैं। बल्कि, यह एक के बाद एक वस्तु प्राप्त कर सकता है, जो कुछ भी आंतरिक तर्क के आधार पर (जो हो सकता है, लेकिन संग्रहीत सूची के आधार पर नहीं होना चाहिए)।
टॉम

1
@ मैं लिंक्डलिस्ट का उपयोग कर रहा था उदाहरण के तौर पर ज्यादातर यह कि आप नहीं जानते कि आपके पास कितना कुछ है क्योंकि आप केवल यह जानते हैं कि एक अर्थ में आगे क्या है (अगर कुछ है)। मैं माफी मांगता हूं कि अगर मेरा शब्द थोड़ा छोटा लगता है या अगर मुझे लगता है कि वे उसी में से एक हैं।
यीशु रामोस

8

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

यह देखते हुए कि आप प्रश्न pysam पुस्तकालय के एक आवेदन से प्रेरित हैं, मैं एक अधिक विशिष्ट उत्तर दे सकता हूं: मैं PySAM का एक कंटीब्यूटर हूं और निश्चित उत्तर यह है कि SAM / BAM फाइलें संरेखित रीड की सटीक गिनती प्रदान नहीं करती हैं। न ही यह जानकारी BAM इंडेक्स फ़ाइल से आसानी से उपलब्ध है। सबसे अच्छा यह कर सकते हैं कि फ़ाइल के कुल आकार के आधार पर कई संरेखण पढ़ने और एक्सट्रपॉल करने के बाद फ़ाइल पॉइंटर के स्थान का उपयोग करके संरेखण की अनुमानित संख्या का अनुमान लगाया जाए। यह एक प्रगति पट्टी को लागू करने के लिए पर्याप्त है, लेकिन निरंतर समय में संरेखण की गणना करने का एक तरीका नहीं है।


6

एक त्वरित बेंचमार्क:

import collections
import itertools

def count_iter_items(iterable):
    counter = itertools.count()
    collections.deque(itertools.izip(iterable, counter), maxlen=0)
    return next(counter)

def count_lencheck(iterable):
    if hasattr(iterable, '__len__'):
        return len(iterable)

    d = collections.deque(enumerate(iterable, 1), maxlen=1)
    return d[0][0] if d else 0

def count_sum(iterable):           
    return sum(1 for _ in iterable)

iter = lambda y: (x for x in xrange(y))

%timeit count_iter_items(iter(1000))
%timeit count_lencheck(iter(1000))
%timeit count_sum(iter(1000))

परिणाम:

10000 loops, best of 3: 37.2 µs per loop
10000 loops, best of 3: 47.6 µs per loop
10000 loops, best of 3: 61 µs per loop

यानी साधारण गणिका_परिचित व्यक्ति जाने का रास्ता है।

Python3 के लिए इसे समायोजित करना:

61.9 µs ± 275 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
74.4 µs ± 190 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
82.6 µs ± 164 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

नोट: यह परीक्षण
python2

3

कंप्यूटर पर "कुछ" की लंबाई प्राप्त करने के दो तरीके हैं।

पहला तरीका एक गिनती को स्टोर करना है - इसके लिए किसी भी चीज़ की आवश्यकता होती है जो इसे संशोधित करने के लिए फ़ाइल / डेटा को छूती है (या एक वर्ग जो केवल इंटरफेस को उजागर करता है - लेकिन यह एक ही चीज़ को उबालता है)।

दूसरा तरीका यह है कि इस पर ध्यान दें और गिनें कि यह कितना बड़ा है।


0

फ़ाइल हेडर में इस प्रकार की जानकारी डालना और आपको इस तक पहुंच प्रदान करना आम बात है। मुझे प्रारूप का पता नहीं है, लेकिन क्या आपने एपीआई की जांच की है?

जैसा कि दूसरों ने कहा है, आप पुनरावृत्त से लंबाई नहीं जान सकते।


0

यह एक इट्रेटर की बहुत परिभाषा के खिलाफ है, जो कि किसी ऑब्जेक्ट का पॉइंटर है, साथ ही अगले ऑब्जेक्ट को कैसे प्राप्त करना है, इसके बारे में जानकारी।

एक पुनरावृत्ति करने वाले को यह नहीं पता होता है कि समाप्ति तक कितनी बार यह पुनरावृति कर पाएगा। यह अनंत हो सकता है, इसलिए अनंत आपका उत्तर हो सकता है।


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

0

हालांकि यह सामान्य रूप से संभव नहीं है कि क्या पूछा गया है, यह अभी भी अक्सर उपयोगी है कि उन पर पुनरावृत्त होने के बाद कितने आइटमों की गणना की गई थी। उसके लिए, आप jaraco.itertools.Counter या इसी तरह का उपयोग कर सकते हैं । यहाँ एक उदाहरण पायथन 3 का उपयोग कर रहा है और पैकेज लोड करने के लिए rwt है।

$ rwt -q jaraco.itertools -- -q
>>> import jaraco.itertools
>>> items = jaraco.itertools.Counter(range(100))
>>> _ = list(counted)
>>> items.count
100
>>> import random
>>> def gen(n):
...     for i in range(n):
...         if random.randint(0, 1) == 0:
...             yield i
... 
>>> items = jaraco.itertools.Counter(gen(100))
>>> _ = list(counted)
>>> items.count
48


-1

संभवतः, आप इसके माध्यम से पुनरावृत्ति किए बिना मदों की संख्या की गणना करना चाहते हैं, ताकि पुनरावृत्ति समाप्त न हो, और आप बाद में फिर से उपयोग करें। copyया के साथ यह संभव हैdeepcopy

import copy

def get_iter_len(iterator):
    return sum(1 for _ in copy.copy(iterator))

###############################################

iterator = range(0, 10)
print(get_iter_len(iterator))

if len(tuple(iterator)) > 1:
    print("Finding the length did not exhaust the iterator!")
else:
    print("oh no! it's all gone")

आउटपुट " Finding the length did not exhaust the iterator!" है

वैकल्पिक रूप से (और अनजाने में), आप अंतर्निहित lenफ़ंक्शन को निम्नानुसार छाया दे सकते हैं:

import copy

def len(obj, *, len=len):
    try:
        if hasattr(obj, "__len__"):
            r = len(obj)
        elif hasattr(obj, "__next__"):
            r = sum(1 for _ in copy.copy(obj))
        else:
            r = len(obj)
    finally:
        pass
    return r

1
रेंजर्स पुनरावृत्तियों नहीं हैं। कुछ पुनरावृत्त प्रकार हैं जिन्हें कॉपी किया जा सकता है, लेकिन अन्य इस कोड का कारण TypeError (जैसे जनरेटर) के साथ विफल हो जाएंगे, और प्रतिलिपि किए गए पुनरावृत्त के माध्यम से पुनरावृत्ति करने से दो बार साइड इफेक्ट हो सकते हैं, या कोड में मनमाने ढंग से टूटने का कारण हो सकता है, जैसे कि, mapपरिणामी फ़ंक्शन कॉल की अपेक्षा कर एक पुनरावृत्तिकर्ता केवल एक बार होता है।
user2357112
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.