hasNext di iterators Python?

Jawaban:

106

Tidak, tidak ada metode seperti itu. Akhir dari iterasi ditandai dengan pengecualian. Lihat dokumentasi .

avakar
sumber
71
"Lebih mudah untuk meminta maaf daripada izin."
118
"Lebih mudah untuk meminta pengampunan daripada izin.": Memeriksa apakah iterator memiliki elemen selanjutnya tidak meminta izin. Ada beberapa situasi di mana Anda ingin menguji keberadaan elemen berikutnya tanpa mengkonsumsinya. Saya akan menerima solusi try catch jika ada unnext()metode untuk mengembalikan elemen pertama setelah saya memeriksa apakah ada dengan memanggil next().
Giorgio
15
@ Giorgio, tidak ada cara untuk mengetahui apakah ada elemen lain tanpa mengeksekusi kode yang menghasilkannya (Anda tidak tahu apakah generator akan mengeksekusi yieldatau tidak). Tentu saja tidak sulit untuk menulis adaptor yang menyimpan hasil next()dan menyediakan has_next()dan move_next().
avakar
5
Gagasan yang sama dapat digunakan untuk mengimplementasikan hasNext()metode (untuk menghasilkan, cache dan mengembalikan true on success, atau return false on failure). Kemudian keduanya hasNext()dan next()akan tergantung pada getNext()metode yang mendasari umum dan item di-cache. Saya benar-benar tidak mengerti mengapa next()tidak ada di perpustakaan standar jika sangat mudah untuk mengimplementasikan adaptor yang menyediakannya.
Giorgio
3
@ LarsH: Maksud Anda misalnya iterator yang membaca dari file yang dapat diubah saat membaca dari itu? Saya setuju bahwa ini bisa menjadi masalah (yang mempengaruhi penyediaan next()dan hasNext()metode pustaka , bukan hanya pustaka Python hipotetis). Jadi ya, next()dan hasNext()menjadi rumit jika konten aliran yang dipindai tergantung pada saat elemen dibaca.
Giorgio
239

Ada alternatif StopIterationdengan menggunakan next(iterator, default_value).

Untuk contoh:

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

Jadi, Anda dapat mendeteksi Noneatau nilai yang ditentukan sebelumnya untuk akhir iterator jika Anda tidak menginginkan cara pengecualian.

Derrick Zhang
sumber
70
jika Anda menggunakan None sebagai "sentinel", Anda sebaiknya pastikan iterator Anda tidak memiliki Nones. Anda juga bisa melakukan sentinel = object()dan next(iterator, sentinel)dan uji dengan is.
sam boosalis
1
mengikuti @samboosalis Saya lebih suka menggunakan unittest.mock.sentinelobjek bawaan yang memungkinkan Anda untuk menulis secara eksplisit next(a, sentinel.END_OF_ITERATION)dan kemudianif next(...) == sentinel.END_OF_ITERATION
ClementWalter
ini lebih cantik dari pengecualian
datdinhquoc
Masalahnya adalah, dengan cara ini, Anda MENGKONSUMSI nilai selanjutnya dari iterator juga. hasNext di Java tidak mengkonsumsi nilai berikutnya.
Alan Franzoni
39

Jika Anda benar-benar membutuhkan sebuah has-nextfungsi (karena Anda hanya setia menyalin sebuah algoritma dari implementasi referensi di Jawa, misalnya, atau karena Anda sedang menulis sebuah prototipe yang akan perlu untuk dapat dengan mudah ditranskripsi ke Jawa ketika itu selesai), mudah untuk dapatkan dengan kelas pembungkus kecil. Sebagai contoh:

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

sekarang sesuatu seperti

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

memancarkan

c
i
a
o

seperti yang dipersyaratkan.

Perhatikan bahwa penggunaan next(sel.it)sebagai built-in membutuhkan Python 2.6 atau lebih baik; jika Anda menggunakan versi Python yang lebih lama, gunakan self.it.next()saja (dan juga untuk next(x)penggunaan contoh). [[Anda mungkin berpikir bahwa catatan ini berlebihan, karena Python 2.6 telah ada selama lebih dari satu tahun sekarang - tetapi lebih sering daripada tidak ketika saya menggunakan fitur Python 2.6 sebagai tanggapan, beberapa komentator atau lainnya merasa berkewajiban untuk menunjukkannya. bahwa itu adalah fitur 2.6, jadi saya mencoba untuk mencegah komentar semacam itu untuk sekali ;-)]]

Alex Martelli
sumber
9
"dengan setia menyalin algoritma dari implementasi referensi di Jawa" adalah alasan terburuk untuk membutuhkan suatu has_nextmetode. Desain Python tidak memungkinkan untuk, katakanlah, gunakan filteruntuk memeriksa apakah array berisi elemen yang cocok dengan predikat yang diberikan. Kesombongan dan kepicikan komunitas Python sangat mengejutkan.
Jonathan Cast
jawaban yang bagus, saya menyalin ini untuk ilustrasi beberapa pola desain yang diambil dari kode Java
madtyn
Saya dengan Python3 dan kode ini memberi sayaTypeError: iter() returned non-iterator
madtyn
1
@ JonathanCast tidak yakin saya mengikuti. Dalam Python, Anda biasanya akan menggunakan mapdan anybukannya filter, tetapi Anda bisa menggunakan SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELatau melupakan SENTINELdan hanya menggunakan try: exceptdan menangkap StopIteration.
juanpa.arrivillaga
13

Selain semua penyebutan StopIteration, loop Python "for" hanya melakukan apa yang Anda inginkan:

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

Coba metode __length_hint __ () dari objek iterator apa pun:

iter(...).__length_hint__() > 0
juj
sumber
5
Saya selalu bertanya-tanya mengapa di bumi python memiliki semua metode __ xxx __? Mereka tampak sangat jelek.
mP.
6
Pertanyaan yang sah! Biasanya itu sintaks untuk metode yang diekspos oleh fungsi builtin (misalnya len, sebenarnya memanggil len ). Fungsi builtin seperti itu tidak ada untuk length_hint, tetapi sebenarnya adalah proposal yang menunggu keputusan (PEP424).
fulmicoton
1
@ MP fungsi-fungsi ini ada di sana, karena kadang-kadang dibutuhkan. Mereka sengaja jelek, karena mereka dianggap sebagai metode terakhir: Jika Anda menggunakannya, Anda tahu bahwa Anda melakukan sesuatu yang non-pythonic dan berpotensi berbahaya (yang juga mungkin berhenti bekerja di titik mana pun).
Arne Babenhauserheide
Suka __init__dan __main__? Imho, ini agak berantakan tidak peduli Anda mencoba membenarkannya.
user1363990
5

hasNextagak diterjemahkan sebagai StopIterationpengecualian, misalnya:

>>> 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
miku
sumber
4

Anda dapat teemenggunakan iterator itertools.tee,, dan memeriksa StopIterationiterator yang diolah.

sykora
sumber
3

Tidak. Konsep yang paling mirip adalah pengecualian StopIteration.

James Thompson
sumber
10
Apa yang Python gunakan pengecualian untuk aliran kontrol? Kedengarannya sangat aneh.
mP.
5
Kanan: pengecualian harus digunakan untuk menangani kesalahan, bukan untuk menentukan aliran kontrol yang normal.
Giorgio
1

Kasus penggunaan yang mengarahkan saya untuk mencari ini adalah sebagai berikut

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 

di mana hasnext () tersedia, orang bisa melakukannya

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)

yang bagi saya lebih bersih. Jelas Anda dapat mengatasi masalah dengan mendefinisikan kelas utilitas, tetapi yang terjadi kemudian adalah Anda memiliki dua puluh berbagai solusi yang hampir sama dengan masing-masing dengan quirks mereka, dan jika Anda ingin menggunakan kembali kode yang menggunakan workarounds yang berbeda, Anda harus memiliki beberapa yang hampir setara dalam aplikasi tunggal Anda, atau berkeliling memilih dan menulis ulang kode untuk menggunakan pendekatan yang sama. Pepatah 'lakukan sekali dan lakukan dengan baik' gagal buruk.

Selanjutnya, iterator itu sendiri perlu memiliki pemeriksaan 'hasnext' internal untuk menjalankan untuk melihat apakah perlu memunculkan pengecualian. Pemeriksaan internal ini kemudian disembunyikan sehingga perlu diuji dengan mencoba mendapatkan item, menangkap pengecualian dan menjalankan pawang jika dilemparkan. Ini tidak perlu menyembunyikan IMO.

John Allsup
sumber
1
Untuk kasus penggunaan ini, Anda dapat menggunakan itertools.cycle
eaglebrain
0

Cara yang disarankan adalah StopIteration . Silakan lihat contoh Fibonacci dari 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()
Ramin Darvishov
sumber
-2

Cara saya memecahkan masalah saya adalah dengan menjaga jumlah objek yang diulangi sejauh ini. Saya ingin mengulangi set menggunakan panggilan ke metode contoh. Karena saya tahu panjang set, dan jumlah item yang dihitung sejauh ini, saya secara efektif punya hasNextmetode.

Versi sederhana dari kode saya:

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

Tentu saja, contohnya adalah mainan, tetapi Anda mendapatkan idenya. Ini tidak akan berfungsi dalam kasus di mana tidak ada cara untuk mendapatkan panjang iterable, seperti generator dll.

forumulator
sumber