2011-12-22 12 views
9

जब मैं लूप में जनरेटर का उपयोग करता हूं, तो ऐसा लगता है कि जब कोई और तत्व उत्पन्न नहीं होता है, तो ऐसा लगता है। अब, मुझे लूप के बिना जनरेटर का उपयोग करना होगा, और अगले तत्व प्राप्त करने के लिए अगले() का उपयोग करें। मेरी समस्या यह है कि, अगर कोई और तत्व नहीं हैं, तो मुझे कैसे पता चलेगा?पायथन 3.x: यदि जनरेटर के पास शेष तत्व हैं

मुझे पता है: अगले() कोई अपवाद नहीं है (स्टॉपइटरेशन), अगर कुछ भी नहीं बचा है, लेकिन इस तरह की एक साधारण समस्या के लिए अपवाद थोड़ा "भारी" नहीं है? क्या है_इनक्स्ट() या ऐसा कोई तरीका नहीं है? सभी को

--- TEST A (for loop) --- 
abc 
123 
None 
True 
456.789 

--- TEST B (try-except) --- 
abc 
123 
None 
True 
456.789 

--- GENERATOR MEMBERS --- 
__class__, __delattr__, __doc__, __eq__, __format__, __ge__, __getattribute__, __gt__, __hash__, __init__, __iter__, __le__, __lt__, __name__, __ne__, __new__, __next__, __reduce__, __reduce_ex__, __repr__, __setattr__, __sizeof__, __str__, __subclasshook__, close, gi_code, gi_frame, gi_running, send, throw 

धन्यवाद, और आपका दिन शुभ हो:

निम्नलिखित लाइनों स्पष्ट करना चाहिए, मैं क्या मतलब है:

#!/usr/bin/python3 

# define a list of some objects 
bar = ['abc', 123, None, True, 456.789] 

# our primitive generator 
def foo(bar): 
    for b in bar: 
     yield b 

# iterate, using the generator above 
print('--- TEST A (for loop) ---') 
for baz in foo(bar): 
    print(baz) 
print() 

# assign a new iterator to a variable 
foobar = foo(bar) 

print('--- TEST B (try-except) ---') 
while True: 
    try: 
     print(foobar.__next__()) 
    except StopIteration: 
     break 
print() 

# assign a new iterator to a variable 
foobar = foo(bar) 

# display generator members 
print('--- GENERATOR MEMBERS ---') 
print(', '.join(dir(foobar))) 

उत्पादन इस प्रकार है! :)

उत्तर

5

आपके द्वारा लिखे गए दो बयान जेनरेटर के अंत में उसी तरह से ढूंढने के साथ सौदा करते हैं। फॉर-लूप बस स्टॉपइटरेशन अपवाद उठाए जाने तक .next() को कॉल करता है और फिर यह समाप्त हो जाता है।

http://docs.python.org/tutorial/classes.html#iterators

इस तरह मुझे नहीं लगता कि StopIteration अपवाद के लिए इंतज़ार कर एक 'भारी' जिस तरह से समस्या से निपटने के है, यह तरीका है कि जनरेटर उपयोग के लिए डिज़ाइन कर रहे हैं।

16

यह एक अच्छा सवाल है। मैं आपको यह दिखाने की कोशिश करूंगा कि हम उत्तर पाने के लिए पाइथन की आत्मनिर्भर क्षमताओं और खुले स्रोत का उपयोग कैसे कर सकते हैं। हम पर्दे के पीछे देखने के लिए dis मॉड्यूल का उपयोग कर सकते हैं और देख सकते हैं कि कैसे सीपीथन दुभाषिया एक इटरेटर पर लूप के लिए लागू करता है।

>>> def for_loop(iterable): 
...  for item in iterable: 
...   pass # do nothing 
...  
>>> import dis 
>>> dis.dis(for_loop) 
    2   0 SETUP_LOOP    14 (to 17) 
       3 LOAD_FAST    0 (iterable) 
       6 GET_ITER    
     >> 7 FOR_ITER     6 (to 16) 
      10 STORE_FAST    1 (item) 

    3   13 JUMP_ABSOLUTE   7 
     >> 16 POP_BLOCK    
     >> 17 LOAD_CONST    0 (None) 
      20 RETURN_VALUE   

रसदार बिट FOR_ITER ऑपोड प्रतीत होता है। हम dis का उपयोग करके किसी भी गहराई से गोता नहीं लगा सकते हैं, तो चलिए सीपीथॉन दुभाषिया के स्रोत कोड में FOR_ITER देखें। यदि आप चारों ओर पोक करते हैं, तो आप इसे Python/ceval.c में पाएंगे; आप इसे here देख सकते हैं। यहां पूरी बात है:

TARGET(FOR_ITER) 
     /* before: [iter]; after: [iter, iter()] *or* [] */ 
     v = TOP(); 
     x = (*v->ob_type->tp_iternext)(v); 
     if (x != NULL) { 
      PUSH(x); 
      PREDICT(STORE_FAST); 
      PREDICT(UNPACK_SEQUENCE); 
      DISPATCH(); 
     } 
     if (PyErr_Occurred()) { 
      if (!PyErr_ExceptionMatches(
          PyExc_StopIteration)) 
       break; 
      PyErr_Clear(); 
     } 
     /* iterator ended normally */ 
     x = v = POP(); 
     Py_DECREF(v); 
     JUMPBY(oparg); 
     DISPATCH(); 

क्या आप देखते हैं कि यह कैसे काम करता है? हम इटरेटर से एक आइटम पकड़ने की कोशिश करते हैं; अगर हम असफल होते हैं, तो हम जांचते हैं कि कौन सा अपवाद उठाया गया था। यदि यह StopIteration है, तो हम इसे साफ़ करते हैं और इटेटरेटर थक जाते हैं।

तो एक लेटर को समाप्त होने पर लूप के लिए "बस पता" कैसे होता है? उत्तर: ऐसा नहीं है - इसे एक तत्व को आजमाने और पकड़ना है। पर क्यों?

उत्तर का हिस्सा सादगी है। इटरेटर्स को लागू करने की सुंदरता का एक हिस्सा यह है कि आपको केवल एक ऑपरेशन को परिभाषित करना होगा: अगले तत्व को पकड़ें। लेकिन सबसे महत्वपूर्ण बात यह है कि यह इटरेटर आलसी बनाता है: वे केवल उन्हीं मानों का उत्पादन करेंगे जो उन्हें पूरी तरह से करना है।

अंत में, यदि आप वास्तव में इस सुविधा को याद कर रहे हैं, तो इसे स्वयं लागू करने के लिए यह छोटा है।

class LookaheadIterator: 

    def __init__(self, iterable): 
     self.iterator = iter(iterable) 
     self.buffer = [] 

    def __iter__(self): 
     return self 

    def __next__(self): 
     if self.buffer: 
      return self.buffer.pop() 
     else: 
      return next(self.iterator) 

    def has_next(self): 
     if self.buffer: 
      return True 

     try: 
      self.buffer = [next(self.iterator)] 
     except StopIteration: 
      return False 
     else: 
      return True 


x = LookaheadIterator(range(2)) 

print(x.has_next()) 
print(next(x)) 
print(x.has_next()) 
print(next(x)) 
print(x.has_next()) 
print(next(x)) 
+1

मुझे अभी एहसास हुआ है कि मैं 'numpy' का अध्ययन करने के लिए 'dis' करना चाहता हूं। ;) – n611x007

0

यह पहले से अंत के इटरेटर सामान्य मामले में के बारे में पता करने के लिए, मनमाने ढंग से कोड अंत के बारे में फैसला करने के लिए चलाने के लिए हो सकता है क्योंकि संभव नहीं है: यहाँ एक उदाहरण है।बफरिंग तत्व लागत पर चीजों को प्रकट करने में मदद कर सकते हैं - लेकिन यह शायद ही कभी उपयोगी है।

प्रैक्टिस में सवाल उठता है जब कोई अब के लिए केवल एक या कुछ तत्व लेना चाहता है, लेकिन उस बदसूरत अपवाद हैंडलिंग कोड (जैसा कि प्रश्न में दर्शाया गया है) लिखना नहीं चाहता। दरअसल यह सामान्य अनुप्रयोग कोड में "StopIteration" अवधारणा को रखने के लिए गैर-पायथनिक है। और पायथन स्तर पर अपवाद हैंडलिंग बल्कि समय लेने वाली है - खासकर जब यह केवल एक तत्व लेने के बारे में है।

तरह for .. break [.. else] का उपयोग कर उन स्थितियों सबसे अच्छा या तो है संभाल करने के लिए pythonic तरीका:

for x in iterator: 
    do_something(x) 
    break 
else: 
    it_was_exhausted() 

या

x = next(iterator, default_value) 

तरह डिफ़ॉल्ट के साथ निर्मित next() समारोह का उपयोग कर या जैसे इटरेटर सहायकों का उपयोग कर जैसे rewiring बातों के लिए itertools मॉड्यूल से:

max_3_elements = list(itertools.islice(iterator, 3)) 

कुछ iterators लेकिन एक "लंबाई संकेत" का पर्दाफाश (PEP424):

>>> gen = iter(range(3)) 
>>> gen.__length_hint__() 
3 
>>> next(gen) 
0 
>>> gen.__length_hint__() 
2 

नोट: iterator.__next__() सामान्य एप्लिकेशन कोड द्वारा नहीं किया जाना चाहिए। यही कारण है कि उन्होंने इसे Python2 में iterator.next() से बदल दिया। और डिफ़ॉल्ट रूप से next() का उपयोग करना बेहतर नहीं है ...

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