क्या यह जानने का एक कुशल तरीका है कि पायथन में एक इट्रेटर में कितने तत्व हैं, सामान्य रूप से, प्रत्येक के माध्यम से पुनरावृत्ति और गिनती के बिना?
क्या यह जानने का एक कुशल तरीका है कि पायथन में एक इट्रेटर में कितने तत्व हैं, सामान्य रूप से, प्रत्येक के माध्यम से पुनरावृत्ति और गिनती के बिना?
जवाबों:
नहीं, यह संभव नहीं है।
उदाहरण:
import random
def gen(n):
for i in xrange(n):
if random.randint(0, 1) == 0:
yield i
iterator = gen(10)
लंबाई iterator
अज्ञात है जब तक आप इसके माध्यम से पुनरावृति नहीं करते।
def gen(): yield random.randint(0, 1)
अनंत है, इसलिए आप इसके माध्यम से पुनरावृति करके कभी भी लंबाई नहीं पा सकेंगे।
numIters = 0 ; while iterator: numIters +=1
?
यह कोड काम करना चाहिए:
>>> 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]
इसके अलावा, ध्यान रखें कि ऐसा करने से पुनरावृत्ति समाप्त हो जाएगी , और आगे उपयोग करने के प्रयासों में कोई तत्व नहीं दिखाई देगा । यह पायथन पुनरावृत्त डिजाइन का एक अपरिहार्य परिणाम है। यदि आप तत्वों को रखना चाहते हैं, तो आपको उन्हें किसी सूची या किसी चीज़ में संग्रहीत करना होगा।
_
पर्ल के संदर्भ है $_
? :)
_
एक डमी चर के लिए किया जाता है, जिसके मूल्य की आपको परवाह नहीं है।
नहीं, किसी भी विधि से आपको हर परिणाम को हल करने की आवश्यकता होगी। तुम कर सकते हो
iter_length = len(list(iterable))
लेकिन चल रहा है कि एक अनंत पुनरावृत्ति पर बेशक वापस नहीं लौटेगा। यह पुनरावृत्ति का उपभोग भी करेगा और यदि आप सामग्री का उपयोग करना चाहते हैं तो इसे रीसेट करने की आवश्यकता होगी।
हमें यह बताने में कि आप किस वास्तविक समस्या को हल करने का प्रयास कर रहे हैं, इससे हमें अपने वास्तविक लक्ष्य को पूरा करने का बेहतर तरीका मिल सकता है।
संपादित करें: उपयोग करने list()
से एक बार में पूरे पुनरावृत्ति को मेमोरी में पढ़ा जा सकता है, जो अवांछनीय हो सकता है। एक और तरीका है
sum(1 for _ in iterable)
किसी अन्य व्यक्ति के रूप में पोस्ट किया गया। जो इसे स्मृति में रखने से बचेंगे।
len(list(iterable))
यह सभी डेटा को मेमोरी में लोड करेगा। आप उपयोग कर सकते हैं reduce(lambda x, _: x+1, iterable, 0)
:। संपादित करें: राशि के साथ Zonda333 कोड भी अच्छा है।
functools.reduce
आप नहीं कर सकते हैं (एक विशेष पुनरावृत्ति के प्रकार को छोड़कर कुछ विशिष्ट तरीकों को लागू करता है जो इसे संभव बनाते हैं)।
आम तौर पर, आप केवल पुनरावृत् तकों का सेवन करके पुनरावृत्त वस्तुओं की गणना कर सकते हैं। शायद सबसे कुशल तरीकों में से एक:
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
)।
sum(1 for _ in iterator)
, यह लगभग दोगुना था।
zip
: यदि आप पास हो जाते हैं zip(counter, iterable)
, तो आपको वास्तव में पुनरावृत्त गणना से 1 अधिक मिलेगा!
किंडा। आप विधि की जांच कर सकते हैं __length_hint__
, लेकिन चेतावनी दी जा सकती है कि (कम से कम पायथॉन 3.4 तक, जैसा कि gsnedders सहायक रूप से इंगित करता है) यह एक अनियोजित कार्यान्वयन विवरण ( थ्रेड में संदेश का अनुसरण ) है, जो इसके बजाय बहुत अच्छी तरह से गायब हो सकता है या नासिक राक्षसों को बुला सकता है।
नहीं तो नहीं। Iterators सिर्फ एक वस्तु है जो केवल next()
विधि को उजागर करती है। आप इसे आवश्यकतानुसार कई बार कॉल कर सकते हैं और वे अंततः बढ़ा सकते हैं या नहीं StopIteration
। सौभाग्य से, यह व्यवहार अधिकांश समय कोडर के लिए पारदर्शी है। :)
मुझे इसके लिए कार्डिनैलिटी पैकेज पसंद है , यह बहुत हल्का है और पुनरावृत्ति के आधार पर उपलब्ध सबसे तेज़ संभव कार्यान्वयन का उपयोग करने की कोशिश करता है।
उपयोग:
>>> 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
तो, उन लोगों के लिए जो उस चर्चा का सारांश जानना चाहते हैं। 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)
, निष्पादन के प्रदर्शन (स्मृति की खपत सहित) के आधार पर, आपको आश्चर्यचकित करेगा:
`` `
gen = (i for i in data*1000); t0 = monotonic(); len(list(gen))
('सूची, सेकंड', 1.9684218849870376)
gen = (i for i in data*1000); t0 = monotonic(); len([i for i in gen])
('list_compr, sec', 2.5885991149989422)
gen = (i for i in data*1000); t0 = monotonic(); sum(1 for i in gen); t1 = monotonic()
('राशि, सेकंड', 3.441088170016883)
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)
gen = (i for i in data*1000); t0 = monotonic(); reduce(lambda counter, i: counter + 1, gen, 0)
('कम करें, सेकंड', 13.436614598002052) `` `
तो, len(list(gen))
सबसे लगातार और कम स्मृति उपभोज्य है
len(list(gen))
कम होने के आधार पर दृष्टिकोण से कम मेमोरी का उपभोग करना चाहिए? पूर्व एक नया बनाता list
है जिसमें मेमोरी आवंटन शामिल होता है जबकि बाद वाला नहीं होना चाहिए। इसलिए मुझे उम्मीद है कि बाद में और अधिक मेमोरी कुशल होगी। इसके अलावा, मेमोरी की खपत तत्व प्रकार पर निर्भर करेगी।
len(tuple(iterable))
और भी अधिक कुशल हो सकता है: नेल्सन मिनार का आलेख
एक इटैलर सिर्फ एक ऑब्जेक्ट होता है, जिसमें किसी तरह के बफर या स्ट्रीम द्वारा पढ़ी जाने वाली अगली ऑब्जेक्ट के लिए एक पॉइंटर होता है, यह एक लिंक्डलिस्ट की तरह होता है जहां आपको पता नहीं होता है कि आपके पास इन तक पहुंचने के लिए कितनी चीजें हैं। Iterators कुशल होने के लिए होते हैं क्योंकि वे सभी करते हैं आपको बताते हैं कि अनुक्रमणिका का उपयोग करने के बजाय संदर्भों के द्वारा आगे क्या है (लेकिन जैसा कि आपने देखा कि आप यह देखने की क्षमता खो देते हैं कि अगली प्रविष्टियाँ कितनी हैं)।
अपने मूल प्रश्न के संबंध में, इसका उत्तर अभी भी है कि पायथन में एक पुनरावृत्त की लंबाई जानने के लिए सामान्य रूप से कोई रास्ता नहीं है।
यह देखते हुए कि आप प्रश्न pysam पुस्तकालय के एक आवेदन से प्रेरित हैं, मैं एक अधिक विशिष्ट उत्तर दे सकता हूं: मैं PySAM का एक कंटीब्यूटर हूं और निश्चित उत्तर यह है कि SAM / BAM फाइलें संरेखित रीड की सटीक गिनती प्रदान नहीं करती हैं। न ही यह जानकारी BAM इंडेक्स फ़ाइल से आसानी से उपलब्ध है। सबसे अच्छा यह कर सकते हैं कि फ़ाइल के कुल आकार के आधार पर कई संरेखण पढ़ने और एक्सट्रपॉल करने के बाद फ़ाइल पॉइंटर के स्थान का उपयोग करके संरेखण की अनुमानित संख्या का अनुमान लगाया जाए। यह एक प्रगति पट्टी को लागू करने के लिए पर्याप्त है, लेकिन निरंतर समय में संरेखण की गणना करने का एक तरीका नहीं है।
एक त्वरित बेंचमार्क:
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)
कंप्यूटर पर "कुछ" की लंबाई प्राप्त करने के दो तरीके हैं।
पहला तरीका एक गिनती को स्टोर करना है - इसके लिए किसी भी चीज़ की आवश्यकता होती है जो इसे संशोधित करने के लिए फ़ाइल / डेटा को छूती है (या एक वर्ग जो केवल इंटरफेस को उजागर करता है - लेकिन यह एक ही चीज़ को उबालता है)।
दूसरा तरीका यह है कि इस पर ध्यान दें और गिनें कि यह कितना बड़ा है।
यह एक इट्रेटर की बहुत परिभाषा के खिलाफ है, जो कि किसी ऑब्जेक्ट का पॉइंटर है, साथ ही अगले ऑब्जेक्ट को कैसे प्राप्त करना है, इसके बारे में जानकारी।
एक पुनरावृत्ति करने वाले को यह नहीं पता होता है कि समाप्ति तक कितनी बार यह पुनरावृति कर पाएगा। यह अनंत हो सकता है, इसलिए अनंत आपका उत्तर हो सकता है।
हालांकि यह सामान्य रूप से संभव नहीं है कि क्या पूछा गया है, यह अभी भी अक्सर उपयोगी है कि उन पर पुनरावृत्त होने के बाद कितने आइटमों की गणना की गई थी। उसके लिए, आप 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
संभवतः, आप इसके माध्यम से पुनरावृत्ति किए बिना मदों की संख्या की गणना करना चाहते हैं, ताकि पुनरावृत्ति समाप्त न हो, और आप बाद में फिर से उपयोग करें। 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
map
परिणामी फ़ंक्शन कॉल की अपेक्षा कर एक पुनरावृत्तिकर्ता केवल एक बार होता है।