มีต่อไปในตัวทำซ้ำ Python?


คำตอบ:


106

ไม่ไม่มีวิธีการดังกล่าว จุดสิ้นสุดของการวนซ้ำถูกระบุโดยข้อยกเว้น ดูเอกสาร


71
"ง่ายกว่าที่จะขอการอภัยมากกว่าการอนุญาต"

119
"ง่ายกว่าที่จะขอการให้อภัยมากกว่าการอนุญาต": ตรวจสอบว่าตัววนซ้ำมีองค์ประกอบถัดไปไม่ได้ขออนุญาตหรือไม่ มีสถานการณ์ที่คุณต้องการทดสอบการมีอยู่ขององค์ประกอบถัดไปโดยไม่ต้องบริโภคมัน ฉันจะยอมรับการแก้ปัญหาลองจับหากมีวิธีการที่จะนำกลับองค์ประกอบแรกหลังจากที่ผมได้ตรวจสอบว่ามันมีอยู่โดยการโทรunnext() next()
Giorgio

15
@Giorgio ไม่มีวิธีที่จะทราบว่ามีองค์ประกอบอื่นโดยไม่ต้องเรียกใช้งานโค้ดที่สร้างมันขึ้นมา (คุณไม่รู้ว่าตัวกำเนิดจะทำงานyieldหรือไม่) มันเป็นของแน่นอนไม่ยากที่จะเขียนอะแดปเตอร์ที่เก็บผลมาจากการnext()ให้บริการและhas_next() move_next()
avakar

5
แนวคิดเดียวกันนี้สามารถนำมาใช้ในการใช้hasNext()วิธีการ (ผลิตแคชและคืนค่าจริงเมื่อสำเร็จหรือคืนเท็จเมื่อล้มเหลว) จากนั้นทั้งสองhasNext()และnext()จะขึ้นอยู่กับวิธีการทั่วไปgetNext()และรายการแคช ฉันไม่เห็นว่าทำไมnext()ไม่ควรอยู่ในไลบรารี่มาตรฐานหากใช้งานอะแดปเตอร์ที่ให้มาได้ง่าย
จอร์โจ

3
@ LarsH: คุณหมายถึงเช่นตัววนซ้ำที่อ่านจากไฟล์ที่สามารถเปลี่ยนแปลงได้ในขณะที่อ่านจากมัน ฉันยอมรับว่านี่อาจเป็นปัญหา (ซึ่งมีผลต่อการให้บริการnext()และhasNext()วิธีการของไลบรารีใด ๆไม่ใช่เพียงแค่ไลบรารี Python แบบสมมุติฐาน) ดังนั้นใช่next()และhasNext()จะกลายเป็นเรื่องยุ่งยากหากเนื้อหาของกระแสการสแกนขึ้นอยู่กับเมื่อองค์ประกอบที่จะอ่าน
Giorgio

239

มีทางเลือกที่จะเป็นได้โดยใช้StopIterationnext(iterator, default_value)

สำหรับ exapmle:

>>> a = iter('hi')
>>> print next(a, None)
h
>>> print next(a, None)
i
>>> print next(a, None)
None

ดังนั้นคุณสามารถตรวจหาNoneหรือค่าอื่น ๆ ที่ระบุไว้ล่วงหน้าสำหรับการสิ้นสุดของตัววนซ้ำหากคุณไม่ต้องการวิธีการยกเว้น


70
หากคุณใช้ None เป็น "sentinel" คุณจะต้องแน่ใจว่าตัววนซ้ำของคุณไม่มี Nones ใด ๆ คุณยังสามารถทำsentinel = object()และและการทดสอบด้วยnext(iterator, sentinel) is
sam boosalis

1
ติดตาม @samboosalis ฉันอยากจะใช้unittest.mock.sentinelวัตถุในตัวที่ช่วยให้คุณเขียนอย่างชัดเจนnext(a, sentinel.END_OF_ITERATION)แล้วif next(...) == sentinel.END_OF_ITERATION
ClementWalter

สิ่งนี้ดีกว่าข้อยกเว้น
datdinhquoc

ปัญหาคือว่าด้วยวิธีนี้คุณใช้ค่าต่อไปจาก iterator เช่นกัน hasNext ใน Java ไม่ใช้ค่าถัดไป
Alan Franzoni

39

หากคุณจริงๆต้องhas-nextการทำงาน (เพราะคุณเพียงแค่นับถือถ่ายทอดขั้นตอนวิธีการจากการดำเนินงานที่อ้างอิงใน Java พูดหรือเพราะคุณเขียนต้นแบบที่จะต้องมีการคัดลอกได้อย่างง่ายดายเพื่อ Java เมื่อมันเสร็จแล้ว) มันเป็นเรื่องง่ายที่จะ รับกับคลาส wrapper เล็กน้อย ตัวอย่างเช่น:

class hn_wrapper(object):
  def __init__(self, it):
    self.it = iter(it)
    self._hasnext = None
  def __iter__(self): return self
  def next(self):
    if self._hasnext:
      result = self._thenext
    else:
      result = next(self.it)
    self._hasnext = None
    return result
  def hasnext(self):
    if self._hasnext is None:
      try: self._thenext = next(self.it)
      except StopIteration: self._hasnext = False
      else: self._hasnext = True
    return self._hasnext

ตอนนี้สิ่งที่ต้องการ

x = hn_wrapper('ciao')
while x.hasnext(): print next(x)

ส่งเสียง

c
i
a
o

ตามความจำเป็น.

โปรดทราบว่าการใช้ next(sel.it)ในตัวต้องใช้ Python 2.6 หรือดีกว่า หากคุณใช้ Python เวอร์ชันเก่าให้ใช้self.it.next()แทน (และคล้ายกันnext(x)ในการใช้ตัวอย่าง) [[คุณอาจคิดว่าข้อความนี้ซ้ำซ้อนเนื่องจาก Python 2.6 มีมานานกว่าหนึ่งปีแล้ว - แต่บ่อยครั้งกว่าที่ฉันไม่ใช้ฟีเจอร์ Python 2.6 ในการตอบกลับผู้วิจารณ์หรือผู้อื่นรู้สึกผูกพันกับหน้าที่เพื่อชี้ให้เห็น ว่ามันเป็นฟีเจอร์ 2.6 ดังนั้นฉันจึงพยายามขัดขวางความคิดเห็นดังกล่าวเพียงครั้งเดียว ;-)]]


9
"การถ่ายทอดอัลกอริทึมอย่างซื่อสัตย์จากการใช้งานอ้างอิงใน Java" เป็นเหตุผลที่แย่ที่สุดที่จะต้องใช้has_nextวิธีการ การออกแบบของ Python ทำให้เป็นไปไม่ได้ที่จะพูดใช้filterเพื่อตรวจสอบว่าอาร์เรย์มีองค์ประกอบที่ตรงกับภาคแสดงหรือไม่ ความเย่อหยิ่งและความสายตาสั้นของชุมชน Python กำลังส่าย
โจนาธานแคสต์

คำตอบที่ดีผมคัดลอกนี้ ilustration บางรูปแบบการออกแบบที่นำมาจากรหัส Java
madtyn

ฉันกับ Python3 และรหัสนี้ให้ฉันTypeError: iter() returned non-iterator
madtyn

1
@ JonathanCast ไม่แน่ใจว่าฉันติดตาม ในหลามคุณมักจะใช้mapและanyแทนfilterแต่คุณสามารถใช้SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELหรือลืมSENTINELและใช้เพียงและจับtry: except StopIteration
juanpa.arrivillaga

13

นอกจากการกล่าวถึง StopIteration ทั้งหมดแล้ว Python "for" loop ยังทำสิ่งที่คุณต้องการ:

>>> it = iter("hello")
>>> for i in it:
...     print i
...
h
e
l
l
o

7

ลองใช้วิธี __length_hint __ () จากวัตถุตัววนซ้ำใด ๆ :

iter(...).__length_hint__() > 0

5
ฉันมักจะสงสัยว่าทำไมบนโลก ธ นั้นมีวิธีการ __ xxx __ ทั้งหมด? พวกเขาดูน่าเกลียดมาก
mP

6
คำถามที่ถูกกฎหมาย! โดยปกติจะเป็นไวยากรณ์สำหรับวิธีการที่เปิดเผยโดยฟังก์ชัน builtin (เช่น len เรียกว่าlenจริง ๆ) ฟังก์ชัน builtin ดังกล่าวไม่มีอยู่สำหรับ length_hint แต่จริงๆแล้วเป็นข้อเสนอที่รอดำเนินการ (PEP424)
fulmicoton

1
@mP ฟังก์ชั่นเหล่านี้อยู่ที่นั่นเพราะบางครั้งจำเป็น พวกเขาน่าเกลียดเพราะพวกเขาถือว่าเป็นวิธีสุดท้าย: ถ้าคุณใช้พวกคุณรู้ว่าคุณทำสิ่งที่ไม่เป็นพิษเป็นภัยและอาจเป็นอันตราย (ซึ่งอาจหยุดทำงานในเวลาใดก็ได้)
Arne Babenhauserheide

กดไลค์__init__และ __main__? อิมโฮมันยุ่งเหยิงไปไม่ว่าคุณจะให้เหตุผลอะไรก็ตาม
user1363990

5

hasNextค่อนข้างแปลเป็นStopIterationข้อยกเว้นเช่น:

>>> it = iter("hello")
>>> it.next()
'h'
>>> it.next()
'e'
>>> it.next()
'l'
>>> it.next()
'l'
>>> it.next()
'o'
>>> it.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

4

คุณสามารถteeใช้ตัววนซ้ำitertools.teeและตรวจสอบStopIterationตัวเอียงตัวเอียง


3

ไม่แนวคิดที่คล้ายกันมากที่สุดน่าจะเป็นข้อยกเว้น StopIteration


10
Python ใช้ข้อยกเว้นสำหรับโฟลว์การควบคุมอย่างไร ฟังดูงี่เง่า
mP

5
ขวา: ข้อยกเว้นควรใช้เพื่อจัดการข้อผิดพลาดไม่ใช่เพื่อกำหนดการไหลปกติของการควบคุม
จอร์โจ


1

กรณีการใช้งานที่ทำให้ฉันค้นหาสิ่งต่อไปนี้

def setfrom(self,f):
    """Set from iterable f"""
    fi = iter(f)
    for i in range(self.n):
        try:
            x = next(fi)
        except StopIteration:
            fi = iter(f)
            x = next(fi)
        self.a[i] = x 

โดยที่ hasnext () พร้อมใช้งานหนึ่งสามารถทำได้

def setfrom(self,f):
    """Set from iterable f"""
    fi = iter(f)
    for i in range(self.n):
        if not hasnext(fi):
            fi = iter(f) # restart
        self.a[i] = next(fi)

สิ่งที่ฉันสะอาดกว่า เห็นได้ชัดว่าคุณสามารถแก้ไขปัญหาต่าง ๆ ได้โดยการกำหนดคลาสยูทิลิตี้ แต่สิ่งที่เกิดขึ้นคือคุณมีวิธีแก้ปัญหาที่เทียบเท่ากันเกือบยี่สิบแบบที่แตกต่างกันสองแบบด้วยกัน มีหลายอย่างที่ใกล้เคียงกันในแอปพลิเคชั่นเดียวของคุณหรือเลือกผ่านและเขียนรหัสใหม่เพื่อใช้วิธีการเดียวกัน 'ทำครั้งเดียวและทำมันได้ดี' maxim ล้มเหลวไม่ดี

นอกจากนี้ตัววนซ้ำเองจำเป็นต้องมีการตรวจสอบ 'hasnext' ภายในเพื่อเรียกใช้เพื่อดูว่าจำเป็นต้องเพิ่มข้อยกเว้นหรือไม่ การตรวจสอบภายในนี้จะถูกซ่อนไว้เพื่อที่จะต้องมีการทดสอบโดยพยายามรับรายการจับข้อยกเว้นและเรียกใช้ตัวจัดการถ้าโยน นี่เป็นการซ่อนที่ไม่จำเป็นของ IMO


1
สำหรับกรณีการใช้งานนี้คุณสามารถใช้itertools.cycle
eaglebrain

0

วิธีที่แนะนำคือStopIteration โปรดดูตัวอย่างฟีโบนักชีจากtutorialspoint

#!usr/bin/python3

import sys
def fibonacci(n): #generator function
   a, b, counter = 0, 1, 0
   while True:
      if (counter > n): 
         return
      yield a
      a, b = b, a + b
      counter += 1
f = fibonacci(5) #f is iterator object

while True:
   try:
      print (next(f), end=" ")
   except StopIteration:
      sys.exit()

-2

วิธีที่ฉันแก้ไขปัญหาของฉันคือการนับจำนวนของวัตถุที่วนซ้ำจนถึงตอนนี้ ฉันต้องการวนซ้ำชุดโดยใช้การเรียกไปยังวิธีการอินสแตนซ์ เนื่องจากฉันรู้ว่าความยาวของฉากและจำนวนของสิ่งของนับจนถึงตอนนี้ฉันจึงมีhasNextวิธีการอย่างมีประสิทธิภาพ

รหัสง่าย ๆ ของฉัน:

class Iterator:
    # s is a string, say
    def __init__(self, s):
        self.s = set(list(s))
        self.done = False
        self.iter = iter(s)
        self.charCount = 0

    def next(self):
        if self.done:
            return None
        self.char = next(self.iter)
        self.charCount += 1
        self.done = (self.charCount < len(self.s))
        return self.char

    def hasMore(self):
        return not self.done

แน่นอนตัวอย่างคือของเล่นชิ้นหนึ่ง แต่คุณเข้าใจแล้ว สิ่งนี้จะไม่ทำงานในกรณีที่ไม่มีวิธีที่จะทำให้ความยาวของ iterable เหมือนเครื่องกำเนิดไฟฟ้าเป็นต้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.