hasNext trong trình lặp Python?


Câu trả lời:


106

Không, không có phương pháp như vậy. Sự kết thúc của phép lặp được chỉ định bởi một ngoại lệ. Xem tài liệu .


71
"Dễ xin tha thứ hơn là cho phép."

118
"Dễ dàng hơn để xin sự tha thứ hơn là sự cho phép.": Kiểm tra xem một trình vòng lặp có phần tử tiếp theo không không yêu cầu sự cho phép. Có những tình huống mà bạn muốn kiểm tra sự tồn tại của một yếu tố tiếp theo mà không tiêu thụ nó. Tôi sẽ chấp nhận giải pháp thử bắt nếu có một unnext()phương pháp để đưa phần tử đầu tiên trở lại sau khi tôi đã kiểm tra xem nó có tồn tại không bằng cách gọi next().
Giorgio

15
@Giorgio, không có cách nào để biết liệu một phần tử khác có tồn tại mà không thực thi mã tạo ra nó hay không (bạn không biết liệu trình tạo sẽ thực thi yieldhay không). Tất nhiên, không khó để viết một bộ chuyển đổi lưu trữ kết quả next()và cung cấp has_next()move_next().
avakar

5
Ý tưởng tương tự có thể được sử dụng để thực hiện hasNext()phương thức (để tạo, bộ đệm và trả về giá trị true khi thành công hoặc trả về false khi thất bại). Sau đó, cả hai hasNext()next()sẽ phụ thuộc vào một getNext()phương thức cơ bản phổ biến và mục được lưu trữ. Tôi thực sự không hiểu tại sao next()không nên ở trong thư viện tiêu chuẩn nếu việc triển khai bộ điều hợp cung cấp nó rất dễ dàng.
Giorgio

3
@LarsH: Ý bạn là, ví dụ như một trình vòng lặp đọc từ một tệp có thể thay đổi trong khi đọc từ nó? Tôi đồng ý rằng đây có thể là một vấn đề (ảnh hưởng đến bất kỳ thư viện cung cấp next()hasNext()phương pháp nào, không chỉ là thư viện Python giả định). Vì vậy, có, next()hasNext()trở nên khó khăn nếu nội dung của luồng được quét phụ thuộc vào thời điểm các phần tử được đọc.
Giorgio

238

Có một sự thay thế cho StopIterationbằng cách sử dụng next(iterator, default_value).

Ví dụ:

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

Vì vậy, bạn có thể phát hiện Nonehoặc giá trị được chỉ định trước khác cho phần cuối của trình vòng lặp nếu bạn không muốn cách ngoại lệ.


69
nếu bạn sử dụng Không làm "sentinel", tốt nhất bạn nên chắc chắn rằng trình lặp của bạn không có bất kỳ Nones nào. bạn cũng có thể làm sentinel = object()next(iterator, sentinel)và thử nghiệm với is.
sam boosalis

1
theo dõi @slynsalis Tôi thà sử dụng unittest.mock.sentinelđối tượng tích hợp cho phép bạn viết một bản next(a, sentinel.END_OF_ITERATION)if next(...) == sentinel.END_OF_ITERATION
tường

cái này đẹp hơn ngoại lệ
datdinhquoc

Vấn đề là, bằng cách này, bạn TIÊU THỤ giá trị tiếp theo từ iterator. hasNext trong Java không tiêu thụ giá trị tiếp theo.
Alan Franzoni

39

Nếu bạn thực sự cần một has-nextchức năng (vì bạn chỉ cần sao chép một cách trung thực một thuật toán từ một triển khai tham chiếu trong Java, hoặc bởi vì bạn đang viết một nguyên mẫu sẽ dễ dàng được sao chép sang Java khi hoàn thành), thật dễ dàng để có được nó với một lớp bao bọc nhỏ. Ví dụ:

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

bây giờ một cái gì đó như

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

phát ra

c
i
a
o

theo yêu cầu.

Lưu ý rằng việc sử dụng next(sel.it)dưới dạng tích hợp yêu cầu Python 2.6 trở lên; nếu bạn đang sử dụng phiên bản Python cũ hơn, hãy sử dụng self.it.next()thay thế (và tương tự như next(x)trong cách sử dụng ví dụ). [[Bạn có thể nghĩ rằng ghi chú này là dư thừa, vì Python 2.6 đã tồn tại hơn một năm nay - nhưng thường xuyên hơn là khi tôi sử dụng các tính năng của Python 2.6 trong một phản hồi, một số người bình luận hoặc người khác cảm thấy bị ràng buộc phải chỉ ra rằng chúng 2,6 tính năng, do đó tôi đang cố gắng kiểm tra các nhận xét như vậy một lần ;-)]]


9
"trung thực sao chép một thuật toán từ một triển khai tham chiếu trong Java" là lý do tồi tệ nhất để cần một has_nextphương thức. Thiết kế của Python làm cho không thể sử dụng filterđể kiểm tra xem một mảng có chứa phần tử khớp với một vị từ đã cho hay không. Sự kiêu ngạo và thiển cận của cộng đồng Python là đáng kinh ngạc.
Jonathan Cast

Câu trả lời hay, tôi đang sao chép điều này vì sự bất hợp lý của một số mẫu thiết kế được lấy từ mã Java
madtyn

Tôi với Python3 và mã này mang lại cho tôiTypeError: iter() returned non-iterator
madtyn

1
@JonathanCast không chắc tôi làm theo. Trong Python, bạn thường sử dụng mapanythay vì filter, nhưng bạn có thể sử dụng SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELhoặc quên SENTINELvà chỉ sử dụng try: exceptvà bắt StopIteration.
juanpa.arrivillaga

13

Ngoài tất cả các đề cập đến StopIteration, vòng lặp "for" của Python chỉ đơn giản là làm những gì bạn muốn:

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

7

Hãy thử phương thức __length_hint __ () từ bất kỳ đối tượng lặp nào:

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

5
Tôi luôn tự hỏi tại sao trên trái đất python có tất cả các phương thức __ xxx __ đó? Họ có vẻ rất xấu xí.
mP.

6
Câu hỏi chính đáng! Thông thường, đó là cú pháp cho các phương thức được hiển thị bởi hàm dựng sẵn (ví dụ: len, thực sự đang gọi len ). Hàm dựng sẵn như vậy không tồn tại trong length_hint, nhưng thực ra đây là một đề xuất đang chờ xử lý (PEP424).
Fulmicoton

1
@mP. những chức năng này là có, bởi vì đôi khi chúng là cần thiết. Chúng cố tình xấu, bởi vì chúng được coi là phương pháp cuối cùng: Nếu bạn sử dụng chúng, bạn biết rằng bạn làm một việc gì đó không phải là pythonic và có khả năng nguy hiểm (cũng có thể ngừng hoạt động tại bất kỳ thời điểm nào).
Arne Babenhauserheide

Thích __init____main__không? Imho, đó là một chút lộn xộn cho dù bạn cố gắng biện minh cho nó.
dùng1363990

5

hasNextphần nào chuyển thành StopIterationngoại lệ, ví dụ:

>>> 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

Bạn có thể teesử dụng trình lặp itertools.tee, và kiểm tra StopIterationtrên trình lặp teed.


3

Không. Khái niệm tương tự nhất rất có thể là ngoại lệ StopIteration.


10
Python sử dụng ngoại lệ nào cho luồng điều khiển? Âm thanh khá hay.
mP.

5
Phải: ngoại lệ nên được sử dụng để xử lý lỗi, không xác định luồng điều khiển thông thường.
Giorgio


1

Trường hợp sử dụng dẫn tôi tìm kiếm này là như sau

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 

nơi hasnext () có sẵn, người ta có thể làm

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)

mà với tôi là sạch hơn. Rõ ràng là bạn có thể giải quyết các vấn đề bằng cách xác định các lớp tiện ích, nhưng điều xảy ra là bạn có một cách giải quyết hai cách giải quyết khác nhau gần như tương đương nhau với các quirks của chúng và nếu bạn muốn sử dụng lại mã sử dụng các cách giải quyết khác nhau, bạn phải có nhiều ứng dụng gần tương đương trong một ứng dụng của bạn hoặc đi xung quanh chọn và viết lại mã để sử dụng cùng một cách tiếp cận. Câu châm ngôn 'làm một lần và làm tốt' thất bại nặng nề.

Hơn nữa, bản thân trình lặp cần phải có kiểm tra 'hasnext' nội bộ để chạy để xem liệu nó có cần đưa ra một ngoại lệ hay không. Kiểm tra nội bộ này sau đó được ẩn để nó cần được kiểm tra bằng cách thử lấy một vật phẩm, bắt ngoại lệ và chạy trình xử lý nếu bị ném. Đây là IMO ẩn không cần thiết.


1
Đối với trường hợp sử dụng này, bạn có thể sử dụng itertools
Motorcycle

0

Cách đề xuất là StopIteration . Vui lòng xem ví dụ về Fibonacci từ hướng dẫn

#!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

Cách tôi giải quyết vấn đề của mình là giữ số lượng đối tượng lặp đi lặp lại cho đến nay. Tôi muốn lặp lại một tập hợp bằng cách sử dụng các cuộc gọi đến một phương thức cá thể. Vì tôi biết độ dài của tập hợp và số lượng vật phẩm được tính cho đến nay, tôi thực sự có một hasNextphương pháp.

Một phiên bản đơn giản của mã của tôi:

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

Tất nhiên, ví dụ là một món đồ chơi, nhưng bạn có ý tưởng. Điều này sẽ không hoạt động trong trường hợp không có cách nào để có được độ dài của vòng lặp, như máy phát điện, v.v.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.