Tại sao các trình vòng lặp trong Python đưa ra một ngoại lệ?


48

Đây là cú pháp cho các trình vòng lặp trong Java (cú pháp hơi giống trong C #):

Iterator it = sequence.iterator();

while (it.hasNext()) {
    System.out.println(it.next());
}

Mà có ý nghĩa. Đây là cú pháp tương đương trong Python:

it = iter(sequence)
while True:
    try:
        value = it.next() 
    except StopIteration:
        break
    print(value)

Tôi nghĩ rằng Ngoại lệ chỉ được sử dụng trong những trường hợp đặc biệt.

Tại sao Python sử dụng ngoại lệ để dừng lặp lại?


Câu trả lời:


50

Có một cách rất Pythonic để viết biểu thức đó mà không viết rõ ràng một khối thử ngoại trừ cho một StopIteration:

# some_iterable is some collection that can be iterated over
# e.g., a list, sequence, dict, set, itertools.combination(...)

for value in some_iterable:
    print(value)

Bạn có thể đọc các PEP liên quan 234 255 nếu bạn muốn biết thêm về lý do tại sao StopIterationđược giới thiệu và logic đằng sau các trình vòng lặp.

Một nguyên tắc chung trong python là có một cách để làm một cái gì đó (xem import this), và tốt nhất là nó đẹp, rõ ràng, dễ đọc và đơn giản, mà phương pháp pythonic đáp ứng. Mã tương đương của bạn chỉ cần thiết vì python không cung cấp cho iterators một hasNexthàm thành viên; thích mọi người chỉ cần lặp qua các vòng lặp trực tiếp (và nếu bạn cần làm gì khác chỉ cần thử đọc nó và bắt một ngoại lệ).

Việc tự động bắt một StopIterationngoại lệ ở phần cuối của một trình vòng lặp có ý nghĩa và là một sự tương tự của việc EOFErrornêu ra nếu bạn đọc qua phần cuối của tệp.


6
Cách "Pythonic" chắc chắn giống như "cho giá trị theo trình tự:" hơn là "cho giá trị trong iter (trình tự):" .. Cập nhật bài đăng?
Yam Marcovic

15
@Yam: Tôi đồng ý. Nó không phải là pythonic để lấy một chuỗi hiện có và chuyển đổi nó thành một trình vòng lặp chỉ để áp dụng một vòng lặp for cho nó; chuỗi đã là một lần lặp, vì vậy việc chuyển đổi từ a listthành a listiteratorlà vô nghĩa. Tôi tiếp tục dòng đầu tiên chỉ làm theo điểm bắt đầu NullUserException, để giải thích làm thế nào bạn nên lặp trên một iterator, mà là giống như cách bạn nên lặp trên bất kỳ iterable ( list, set, str, tuple, dict, file, generator, vv). Tôi có thể đã làm một cái gì đó như it = itertools.combinations("ABCDE", 2)để có được một ví dụ tốt hơn về một trình vòng lặp có ý nghĩa.
dr jimbob

1
it = iter(sequence)không cần thiết
Caridorc

2
@Caridorc - Nếu bạn đọc các bình luận, quan điểm của bạn đã được giải quyết. Điều đó là không cần thiết và đã được thực hiện để làm theo điểm bắt đầu của câu hỏi (nơi họ hỏi rõ ràng về vấn đề này iterators) và bạn cần phải itertạo một cách rõ ràng iterator(thử type([])( list) so với type(iter([]))( listiterator)).
dr jimbob

//, @drjimbob, bạn nêu lên một điểm xuất sắc trong bình luận thứ hai cho câu hỏi này. Tôi hơi mới với các tính năng nâng cao của iterables và tôi sẽ không nhận ra rằng nếu tôi không đọc các bình luận. Tôi nghĩ rằng nó sẽ có lợi cho nhiều người trong chúng ta những linh hồn tự giáo dục kém nếu chúng ta có thể thấy điểm đó về cách câu hỏi có thể được thay đổi thành "Cách Python" như là phần đầu tiên, chính của câu trả lời.
Nathan Basan

28

Lý do tại sao python sử dụng Ngoại lệ để dừng lặp lại được ghi lại trong PEP 234 :

Người ta đã đặt câu hỏi liệu một ngoại lệ để báo hiệu sự kết thúc của phép lặp không quá đắt. Một số lựa chọn thay thế cho ngoại lệ StopIteration đã được đề xuất: một giá trị đặc biệt Kết thúc để báo hiệu kết thúc, kết thúc hàm () để kiểm tra xem iterator đã kết thúc hay chưa, thậm chí sử dụng lại ngoại lệ IndexError.

  • Một giá trị đặc biệt có vấn đề là nếu một chuỗi bao gồm giá trị đặc biệt đó, một vòng lặp trên chuỗi đó sẽ kết thúc sớm mà không có bất kỳ cảnh báo nào. Nếu trải nghiệm với các chuỗi C kết thúc null không dạy cho chúng ta những vấn đề có thể gây ra, hãy tưởng tượng sự cố mà công cụ hướng nội Python sẽ lặp lại trong danh sách tất cả các tên dựng sẵn, giả sử rằng giá trị End đặc biệt là tích hợp nhân danh

  • Gọi một hàm end () sẽ yêu cầu hai cuộc gọi mỗi lần lặp. Hai cuộc gọi đắt hơn nhiều so với một cuộc gọi cộng với một bài kiểm tra ngoại lệ. Đặc biệt là thời gian quan trọng cho vòng lặp có thể kiểm tra rất rẻ cho một ngoại lệ.

  • Việc sử dụng lại IndexError có thể gây nhầm lẫn vì đây có thể là lỗi chính hãng, sẽ được che dấu bằng cách kết thúc vòng lặp sớm.

Lưu ý: cách python thành ngữ để lặp qua một chuỗi như sau:

for value in sequence:
    print (value)

20

Đó là một sự khác biệt trong triết học. Triết lý thiết kế của Pythonic là EAFP :

Dễ dàng hơn để yêu cầu sự tha thứ hơn sự cho phép. Kiểu mã hóa Python phổ biến này giả định sự tồn tại của các khóa hoặc thuộc tính hợp lệ và bắt ngoại lệ nếu giả định chứng minh là sai. Phong cách sạch sẽ và nhanh chóng này được đặc trưng bởi sự hiện diện của nhiều tryexcepttuyên bố. Kỹ thuật này tương phản với phong cách LBYL phổ biến đối với nhiều ngôn ngữ khác như C ...


7

Chỉ là việc triển khai Java có một hasNext()phương thức để bạn có thể kiểm tra một trình vòng lặp trống trước khi bạn thực hiện một next(). Khi bạn thực hiện cuộc gọi next()trên Trình lặp Java không còn phần tử nào, a NoSuchElementExceptionsẽ bị ném .

Vì vậy, hiệu quả, bạn có thể thực hiện thử..catch trong Java giống như try..except trong Python. Và vâng, theo như một câu trả lời trước đó, triết học rất quan trọng trong thế giới Pythonic.

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.