Чому ітератори в Python створюють виняток?


48

Ось синтаксис ітераторів на Java (дещо схожий синтаксис у C #):

Iterator it = sequence.iterator();

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

Що має сенс. Ось еквівалентний синтаксис у Python:

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

Я вважав, що Винятки повинні використовуватися лише у, виняткових обставинах.

Чому Python використовує винятки, щоб зупинити ітерацію?


Відповіді:


50

Існує дуже пітонічний спосіб записати цей вираз без явного написання випробувального блоку для 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)

Ви можете ознайомитися з відповідними PEPs 234 255, якщо хочете дізнатися більше, чому StopIterationбуло введено та логіку, що стоїть за ітераторами.

Загальний принцип в пітоні - це мати один спосіб зробити щось (див. import this), І бажано його красиве, явне, читабельне та просте, що задовольняє пітонічний метод. Ваш еквівалентний код необхідний лише тому, що python не дає ітераторам hasNextфункції члена; віддаючи перевагу людям просто перебирати через ітератори безпосередньо (а якщо вам потрібно зробити щось інше, просто спробуйте прочитати його та знайти виняток).

Це автоматичне StopIterationвилучення виключення в кінці ітератора має сенс і є аналогом EOFErrorпіднятого, якщо ви читаєте минулий кінець файлу.


6
Упевнений спосіб "Pythonic" здається набагато більше схожим на "для значення в послідовності:", а не "для значення в ітері (послідовності):" .. Оновити пост?
Ям Маркович

15
@Yam: Я згоден. Не пітонічно приймати існуючу послідовність і перетворювати її в ітератор лише для прикладання до неї циклу; послідовність уже є ітерабельною, тому перетворення a listв a listiteratorє безглуздим. Я зберіг першу сходинку тільки слідувати за відправну точкою NullUserException, в пояснити , як ви повинні перебирає итератор, який є таким же чином , ви повинні перебирає будь-яку ітерацію ( list, set, str, tuple, dict, file, generatorі т.д.). Я міг би зробити щось на кшталт, it = itertools.combinations("ABCDE", 2)щоб отримати кращий приклад змістовного ітератора.
dr jimbob

1
it = iter(sequence)не потрібна.
Caridorc

2
@Caridorc - Якщо ви читаєте коментарі, ви б вирішили свою думку. Це не потрібно, і це робилося для того, щоб дотримуватися початкового пункту питання (де вони прямо запитували iterators), і вам потрібно iterявно генерувати iterator(спробувати type([])( list) vs type(iter([]))( listiterator)).
д-р Джимбоб

//, @drjimbob, ви ставите відмінну крапку у другому коментарі до цього питання. Я трохи новачок у вдосконалених функціях ітерабелів, і я б не зрозумів цього, якби не читав коментарі. Я думаю, що багатьом з нас бідних самовиховних душ було б корисно, якби ми могли бачити той пункт про те, як саме питання можна змінити на «Шлях Пітона» як першу, основну частину відповіді.
Натан Басанес

28

Причина, по якій python використовує виняток для зупинки ітерації, задокументована в PEP 234 :

Було поставлено питання, чи не є винятком дорогий виняток для завершення ітерації. Запропоновано декілька альтернатив для виключення StopIteration: спеціальне значення End для передачі сигналу про кінець, функція end () для перевірки того, чи завершено ітератор, навіть повторне використання виключення IndexError.

  • Особливе значення має проблему, що якщо послідовність коли-небудь містить це спеціальне значення, цикл над цією послідовністю закінчується передчасно без будь-якого попередження. Якщо досвід роботи зі строками, що закінчуються нулем C, не навчив нас проблем, які це може спричинити, уявіть собі проблему, у якої інструмент для самоаналізу Python матиме ітерацію над списком усіх вбудованих імен, припускаючи, що спеціальне значення End було вбудованим, в ім'я!

  • Виклик функції end () вимагає двох викликів за ітерацію. Два дзвінки значно дорожчі, ніж один дзвінок плюс тест на виняток. Особливо критичний для циклу час може випробувати дуже дешево на виняток.

  • Повторне використання IndexError може призвести до плутанини, оскільки це може бути справжня помилка, яку можна замаскувати, передчасно закінчивши цикл.

Примітка: ідіоматичний спосіб пітона переходити через послідовність виглядає так:

for value in sequence:
    print (value)

20

Це різниця у філософії. Філософія дизайну Pythonic - це EAFP :

Простіше просити пробачення, ніж дозволу. Цей загальний стиль кодування Python передбачає існування дійсних ключів або атрибутів та фіксує винятки, якщо припущення виявиться помилковим. Цей чистий і швидкий стиль характеризується наявністю багатьох tryі exceptвисловлювань. Метод контрастує зі стилем LBYL, загальним для багатьох інших мов, таких як C ...


7

Просто в реалізації Java є hasNext()метод, щоб ви могли перевірити наявність порожнього ітератора перед тим, як виконати next(). Коли ви зателефонуєте next()на ітератор Java, в якому немає елементів, NoSuchElementExceptionвикидається a .

Настільки ефективно, ви можете зробити спробу .. Ловити на Java, як, наприклад, спробувати .. крім у Python. І так, згідно з попередньою відповіддю, філософія є дуже важливою у піфонічному світі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.