Một lớp lót để kiểm tra xem một trình lặp có mang lại ít nhất một phần tử hay không?


101

Hiện tại tôi đang làm việc này:

try:
    something = iterator.next()
    # ...
except StopIteration:
    # ...

Nhưng tôi muốn một biểu thức mà tôi có thể đặt bên trong một ifcâu lệnh đơn giản . Có điều gì được tích hợp sẵn sẽ làm cho mã này trông bớt vụng về hơn không?

any()trả về Falsenếu một mục có thể lặp lại trống, nhưng nó sẽ có khả năng lặp lại trên tất cả các mục nếu không. Tôi chỉ cần nó để kiểm tra mục đầu tiên.


Ai đó hỏi tôi đang cố gắng làm gì. Tôi đã viết một hàm thực thi truy vấn SQL và thu được kết quả của nó. Đôi khi khi tôi gọi hàm này, tôi chỉ muốn biết liệu truy vấn có trả lại bất kỳ điều gì hay không và đưa ra quyết định dựa trên đó.


2
Ngoài ra, một vấn đề với mã đó là bạn không thể đóng gói nó thành một hàm, vì nó sẽ ăn phần tử đầu tiên. Câu hỏi hay.
andrewrk

2
Trong trường hợp của tôi, tôi không cần phần tử nào cả, tôi chỉ muốn biết có ít nhất một phần tử.
Bastien Léonard

2
hả! Trường hợp sử dụng tương tự của tôi trong việc cố gắng tìm ra giải pháp tương tự!
Daniel


Câu trả lời:


134

anysẽ không vượt ra ngoài yếu tố đầu tiên nếu nó Đúng. Trong trường hợp trình lặp cho ra kết quả sai, bạn có thể viết any(True for _ in iterator).


Điều này dường như hiệu quả với tôi, với một re.finditer. Bạn có thể kiểm tra xem bất kỳ điểm dừng nào ở thành công đầu tiên một cách dễ dàng: chạy any((x > 100 for x in xrange(10000000)))và sau đó chạy any((x > 10000000 for x in xrange(100000000)))- lần thứ hai sẽ mất nhiều thời gian hơn.
chbrown

1
Này hoạt động đối với trường hợp "ít nhất x"sum(1 for _ in itertools.islice(iterator, max_len)) >= max_len
Dave Butler

11
Tương tự, nếu bạn cần kiểm tra xem trình vòng lặp có trống hay không, người ta có thể sử dụng all(False for _ in iterator)sẽ kiểm tra xem trình vòng lặp có trống không. (tất cả đều trả về True nếu trình vòng lặp trống, nếu không, nó sẽ dừng khi nhìn thấy phần tử False đầu tiên)
KGardevoir

22
Vấn đề lớn với giải pháp này là bạn không thể thực sự sử dụng giá trị trả về từ trình lặp nếu nó không trống, phải không?
Ken Williams

42

Trong Python 2.6+, nếu tên sentinelđược liên kết với một giá trị mà trình lặp không thể mang lại,

if next(iterator, sentinel) is sentinel:
    print('iterator was empty')

Nếu bạn không biết trình lặp có thể mang lại kết quả gì, hãy tạo trạm gác của riêng bạn (ví dụ: ở đầu mô-đun của bạn) với

sentinel = object()

Nếu không, bạn có thể sử dụng, trong vai trò giám sát, bất kỳ giá trị nào mà bạn "biết" (dựa trên các cân nhắc ứng dụng) mà trình lặp không thể mang lại.


1
Đẹp! Đối với trường hợp sử dụng của tôi, if not next(iterator, None):là đủ vì tôi chắc chắn Không có sẽ không phải là một phần của các mục. Cám ơn đã chỉ tôi hướng đi đúng!
wasabigeek

1
@wasabi Hãy nhớ rằng điều đó notsẽ trả về True cho bất kỳ đối tượng giả mạo nào, chẳng hạn như danh sách trống, Sai và 0. is not Nonelà an toàn hơn, và theo tôi rõ ràng hơn.
Caagr98

21

Điều này không thực sự rõ ràng hơn, nhưng nó cho thấy một cách để đóng gói nó trong một chức năng một cách dễ dàng:

def has_elements(iter):
  from itertools import tee
  iter, any_check = tee(iter)
  try:
    any_check.next()
    return True, iter
  except StopIteration:
    return False, iter

has_el, iter = has_elements(iter)
if has_el:
  # not empty

Điều này không thực sự khó hiểu và đối với những trường hợp cụ thể, có lẽ có những giải pháp tốt hơn (nhưng ít chung chung hơn), như mặc định tiếp theo .

first = next(iter, None)
if first:
  # Do something

Điều này không chung chung vì Không có có thể là một phần tử hợp lệ trong nhiều vòng lặp.


Đây có lẽ là cách tốt nhất để làm điều này. Tuy nhiên, sẽ hữu ích nếu biết OP đang cố gắng làm gì? Có lẽ có một giải pháp thanh lịch hơn (sau tất cả là IS Python).
rossipedia

Cảm ơn, tôi nghĩ tôi sẽ sử dụng next().
Bastien Léonard

1
@Bastien, tốt thôi, nhưng hãy làm như vậy với một lính canh thích hợp (xem câu trả lời của tôi).
Alex Martelli

3
Có một lỗ hổng bộ nhớ lớn trong giải pháp này. Trong teeitertools sẽ phải giữ mọi phần tử đơn lẻ từ trình lặp ban đầu trong trường hợp any_checkcần nâng cao. Điều này tệ hơn là chỉ chuyển đổi trình lặp ban đầu thành một danh sách.
Rafał Dowgird ngày

1
@ RafałDowgird Điều này còn tệ hơn là chỉ chuyển đổi trình lặp ban đầu thành một danh sách. Không hẳn - hãy nghĩ về chuỗi vô hạn.
Piotr Dobrogost

6

bạn có thể dùng:

if zip([None], iterator):
    # ...
else:
    # ...

nhưng nó hơi phi lý đối với trình đọc mã


2
.. (bạn có thể sử dụng bất kỳ 1 mục iterable thay vì [Không])
mykhal

5

Cách tốt nhất để làm điều đó là với peekabletừ more_itertools.

from more_itertools import peekable
iterator = peekable(iterator)
if iterator:
    # Iterator is non-empty.
else:
    # Iterator is empty.

Chỉ cần lưu ý nếu bạn giữ các tham chiếu đến trình lặp cũ, trình lặp đó sẽ được nâng cao. Từ đó trở đi, bạn phải sử dụng trình lặp có thể nhìn thấy mới. Tuy nhiên, thực sự, peekable hy vọng sẽ là bit mã duy nhất sửa đổi trình lặp cũ đó, vì vậy bạn không nên giữ các tham chiếu cho trình lặp cũ nằm xung quanh.


3

Thế còn:

In [1]: i=iter([])

In [2]: bool(next(i,False))
Out[2]: False

In [3]: i=iter([1])

In [4]: bool(next(i,False))
Out[4]: True

4
Một điều thú vị! Nhưng điều gì sẽ xảy ra nếu next () trả về False vì nó thực sự mang lại kết quả?
Bastien Léonard

@ BastienLéonard Tạo một lớp học class NotSet: pass, sau đó kiểm traif next(i, NotSet) is NotSet: print("Iterator is empty")
Elijas

-1

__length_hint__ ước tính độ dài của list(it)- đó là phương pháp riêng tư, mặc dù:

x = iter( (1, 2, 3) )
help(x.__length_hint__)
      1 Help on built-in function __length_hint__:
      2 
      3 __length_hint__(...)
      4     Private method returning an estimate of len(list(it)).

4
không được đảm bảo cho mọi trình lặp. >>> def it (): ... nhường 1 ... lợi nhuận 2 ... năng suất 3 ... >>> i = it () >>> i .__ length_hint__ Traceback (lần gọi gần đây nhất): Tệp " <stdin> ", dòng 1, trong <module> AttributeError: đối tượng ' maker ' không có thuộc tính ' length_hint '
andrewrk

3
Nó cũng có thể hợp pháp khi nó trả về 0 cho một trình lặp có nhiều hơn 0 mục nhập, vì nó chỉ là một gợi ý.
Glenn Maynard

-1

Đây là một trình bao bọc trình lặp quá mức cần thiết thường cho phép kiểm tra xem có mục tiếp theo hay không (thông qua chuyển đổi sang boolean). Tất nhiên là khá kém hiệu quả.

class LookaheadIterator ():

    def __init__(self, iterator):
        self.__iterator = iterator
        try:
            self.__next      = next (iterator)
            self.__have_next = True
        except StopIteration:
            self.__have_next = False

    def __iter__(self):
        return self

    def next (self):
        if self.__have_next:
            result = self.__next
            try:
                self.__next      = next (self.__iterator)
                self.__have_next = True
            except StopIteration:
                self.__have_next = False

            return result

        else:
            raise StopIteration

    def __nonzero__(self):
        return self.__have_next

x = LookaheadIterator (iter ([]))
print bool (x)
print list (x)

x = LookaheadIterator (iter ([1, 2, 3]))
print bool (x)
print list (x)

Đầu ra:

False
[]
True
[1, 2, 3]

-2

Hơi muộn, nhưng ... Bạn có thể biến trình lặp thành một danh sách và sau đó làm việc với danh sách đó:

# Create a list of objects but runs out the iterator.
l = [_ for _ in iterator]

# If the list is not empty then the iterator had elements; else it was empty.
if l :
    pass # Use the elements of the list (i.e. from the iterator)
else :
    pass # Iterator was empty, thus list is empty.

4
Điều này không hiệu quả vì nó liệt kê toàn bộ danh sách. Sẽ không hoạt động đối với máy phát vô hạn.
becko

@becko: Đồng ý. Nhưng điều đó dường như không phải là trường hợp trong câu hỏi ban đầu.
Jens

3
Một vấn đề khác là các iterator có thể tạo ra một số lượng vô hạn của các đối tượng mà có thể gây ra lỗi tràn bộ nhớ, và thực tế rằng chương trình sẽ không bao giờ đạt được báo cáo kết quả tiếp theo
Willem Van Onsem
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.