Stopiteration error python как исправить

Python StopIteration By Yashi Goyal Python Tutorial Python Certification Course Programming Languages Courses Angular JS Certification Training Introduction to Python StopIteration The following article provides an outline for Python StopIteration. As we are well aware of the topic ‘iterator’ and ‘iterable’ in Python. The basic idea of what the ‘iterator’ is? Iterator is basically […]

Содержание

  1. Python StopIteration
  2. Python Certification Course
  3. Programming Languages Courses
  4. Angular JS Certification Training
  5. Introduction to Python StopIteration
  6. Syntax of Python StopIteration
  7. How StopIteration works in Python?
  8. Examples of Python StopIteration
  9. Example #1
  10. Example #2
  11. How to Avoid StopIteration Exception in Python?
  12. Conclusion
  13. Recommended Articles
  14. Встроенные исключения¶
  15. Базовые классы¶
  16. Исключения¶
  17. Исключения ОС¶
  18. Предупреждения¶
  19. Иерархия исключений¶

Python StopIteration

By Yashi Goyal

Python Tutorial

Python Certification Course

Programming Languages Courses

Angular JS Certification Training

Introduction to Python StopIteration

The following article provides an outline for Python StopIteration. As we are well aware of the topic ‘iterator’ and ‘iterable’ in Python. The basic idea of what the ‘iterator’ is? Iterator is basically an object that holds a value (generally a countable number) which is iterated upon. Iterator in Python uses the __next__() method in order to traverse to the next value. In order to tell that there are no more values that need to be traversed by the __next__() method, a StopIteration statement is used. Programmers usually write a terminating condition inside __next__() method in order to stop it after the specified condition is reached.

Syntax of Python StopIteration

When the specified number of iterations are done, StopIteration is raised by the next method in case of iterators and generators (works similar to iterators except it generates a sequence of values at a time instead of a single value). If we go in deep understanding of Python, the situation of raising ‘StopIteration’ is not considered as an error. It is considered an Exception and can be handled easily by catching that exception similar to other exceptions in Python.

Web development, programming languages, Software testing & others

General syntax of using StopIteration in if and else of next() method is as follows:

How StopIteration works in Python?

StopIteration stops the iterations after the maximum limit is reached or it discontinues moving the loop forever.

Key points that needs to be keep in mind to know the working of StopIteration in Python are as follows:

  • It is raised by the method next() or __next__() which is a built-in method in python to stop the iterations or to show that no more items are left to be iterated upon.
  • We can catch the StopIteration exception by writing the code inside the try block and catching the exception using the ‘except’ keyword and printing it on screen using the ‘print’ keyword.
  • Value returned by raising the StopIteration is used as a parameter of the Exception Constructor in order to perform the desired actions.
  • next() method in both generators and iterators raises it when no more elements are present in the loop or any iterable object.

Examples of Python StopIteration

Given below are the examples mentioned:

Example #1

Stop the printing of numbers after 20 or printing numbers incrementing by 2 till 20 in the case of Iterators.

Code:

Explanation:

  • In the above example, in order to iterate through the values, two methods, i.e. iter() and next() are used. If we see, in the next() method if and else statements are used in order to check when the iteration and hence their respective actions (which is printing of values in this case) should get terminated.
  • If the iterable value is less than or equals to 20, it continues to print those values at the increment of 2. Once the value reaches greater than 20, the next() method raises a StopIteration exception.

Example #2

Finding the cubes of number and stop executing once the value becomes equal to the value passed using StopIteration in the case of generators.

Code:

Output:

Explanation:

  • In the above example, we are finding the cubes of number from 1 till the number passed in the function. We are generating multiple values at a time using the generators in Python and in order to stop the execution once the value reaches the one passed in the function, StopIteration exception is raised.
  • Different methods are created serving their respective purpose like generating the values, finding the cubes and printing the value by storing them in the output array. Basic python functions are used in the program like range, append, etc which should be clear in the initial stages of learning to the programmer.

How to Avoid StopIteration Exception in Python?

  • As seen above StopIteration is not an error in Python but an exception and is used to run the next() method for the specified number of iterations. Iterator in Python uses the two methods, i.e. iter() and next().
  • The next() method raises an StopIteration exception when the next() method is called manually.
  • The best way to avoid this exception in Python is to use normal looping or use it as a normal iterator instead of writing the next() method again and again.
  • Otherwise if not able to avoid StopIteration exception in Python, we can simply raise the exception in next() method and catch the exception like a normal exception in Python using the except keyword.

Conclusion

As discussed above in the article it must be clear to you what is the StopIteration exception and in which condition it is raised in Python. StopIteration exception could be an issue to deal with for the new programmers as it can be raised in many situations. But proper understanding of its scenarios in which it could be raised and techniques to avoid it can help them to program better.

Recommended Articles

This is a guide to Python StopIteration. Here we discuss how StopIteration works in python and how to avoid StopIteration exception with programming examples. You may also have a look at the following articles to learn more –

Источник

Встроенные исключения¶

В Python все исключения должны быть экземплярами класса, производного от BaseException . В операторе try с предложением except , в котором упоминается класс, также обрабатывает любые классы исключений, производные от этого класса (но не классы исключений, от которых он наследуется). Два класса исключений, которые не связаны через подклассы, никогда не эквивалентны, даже если у них одно и то же имя.

Перечисленные ниже встроенные исключения могут быть созданы интерпретатором или встроенными функциями. Если не указано иное, у них есть «связанное значение», указывающее подробную причину ошибки. Это может быть строка или кортеж из нескольких элементов информации (например, код ошибки и строка, объясняющая код). Связанное значение обычно передаётся в качестве аргументов конструктору класса исключения.

Код пользователя может вызывать встроенные исключения. Это можно использовать для тестирования обработчика исключений или для сообщения об ошибке «точно так же, как» ситуация, в которой интерпретатор вызывает то же исключение; но имейте в виду, что нет ничего, что могло бы помешать пользовательскому коду вызвать недопустимую ошибку.

Встроенные классы исключений могут разделяться на подклассы для определения новых исключений; программистам рекомендуется выводить новые исключения из класса Exception или одного из его подклассов, а не из BaseException . Дополнительная информация об определении исключений доступна в учебном руководстве по Python под заголовком Исключения, определенные пользователями .

При возникновении (или повторном повышении) исключения в предложении except или finally __context__ автоматически устанавливается на последнее обнаруженное исключение; если новое исключение не обрабатывается, то отображаемая в конечном итоге обратная трассировка будет включать исходное(ые) исключение(я) и последнее исключение.

При создании нового исключения (вместо использования пустого raise для повторного вызова исключения, обрабатываемого в данный момент), неявный контекст исключения может быть дополнен явной причиной с помощью from с raise :

Выражение, следующее за from , должно быть исключением или None . Оно будет установлено как __cause__ для возникшего исключения. Установка __cause__ также неявно устанавливает для атрибута __suppress_context__ значение True , так что использование raise new_exc from None эффективно заменяет старое исключение новым для целей отображения (например, преобразование KeyError в AttributeError ), оставляя старое исключение доступным в __context__ для самоанализа при отладке.

Код отображения трассировки по умолчанию показывает эти связанные исключения в дополнение к трассировке самого исключения. Явно связанное исключение в __cause__ всегда отображается, если оно присутствует. Неявно связанное исключение в __context__ отображается, только если __cause__ — это None , а __suppress_context__ — ложь.

В любом случае само исключение всегда отображается после любых связанных исключений, так что последняя строка трассировки всегда показывает последнее возникшее исключение.

Базовые классы¶

Следующие исключения используются в основном как базовые классы для других исключений.

Базовый класс для всех встроенных исключений. Он не предназначен для непосредственного наследования пользовательскими классами (для этого используйте Exception ). Если str() вызывается для экземпляра этого класса, возвращается представление аргумента(ов) экземпляру или пустая строка, когда аргументов не было.

Кортеж аргументов, передаваемых конструктору исключения. Некоторые встроенные исключения (например, OSError ) ожидают определённого количества аргументов и придают особое значение элементам этого кортежа, в то время как другие обычно вызываются только с одной строкой, дающей сообщение об ошибке.

Этот метод устанавливает tb как новую трассировку для исключения и возвращает объект исключения. Обычно он используется в таком коде обработки исключений:

Все встроенные исключения, не связанные с выходом из системы, являются производными от этого класса. Все пользовательские исключения также должны быть производными от этого класса.

Базовый класс для тех встроенных исключений, которые возникают при различных арифметических ошибках: OverflowError , ZeroDivisionError , FloatingPointError .

Вызывается, когда операция, связанная с буфером , не может быть выполнена.

Базовый класс для исключений, которые возникают, когда ключ или индекс, используемый в сопоставлении или последовательности, недопустим: IndexError , KeyError . Его можно вызвать напрямую с помощью codecs.lookup() .

Исключения¶

Далее перечислены самые частые исключения.

Возникает при сбое оператора assert .

Возникает при сбое ссылки на атрибут (см. Ссылки на атрибуты ) или присваивания. (Когда объект вообще не поддерживает ссылки на атрибуты или назначения атрибутов, вызывается TypeError .)

Вызывается, когда функция input() достигает состояния конца файла (EOF) без чтения каких-либо данных. (Примечание: методы io.IOBase.read() и io.IOBase.readline() возвращают пустую строку при достижении EOF.)

В настоящее время не используется.

Возникает при закрытии генератора или корутины ; см. generator.close() и coroutine.close() . Он напрямую наследуется от BaseException вместо Exception , поскольку технически это не ошибка.

Вызывается, когда оператор import не может загрузить модуль. Также Вызывается, когда «из списка» from . import нет имени, которое не может быть найдено.

Атрибуты name и path можно установить, используя только ключевые аргументы. Когда они установлены, они представляют имя модуля, который пытались импортировать, и путь к любому файлу, который вызвал исключение, соответственно.

Изменено в версии 3.3: Добавлены атрибуты name и path .

Подкласс ImportError , вызываемый import , когда модуль не может быть обнаружен. Он также вызывается, когда None находится в sys.modules .

Добавлено в версии 3.6.

Вызывается, когда нижний индекс последовательности выходит за пределы допустимого диапазона. (Индексы фрагментов без уведомления усекаются, чтобы попасть в допустимый диапазон; если индекс не является целым числом, вызывается TypeError .)

Вызывается, когда ключ сопоставления (словаря) не найден в множестве существующих ключей.

Вызывается, когда пользователь нажимает клавишу прерывания (обычно Control-C или Delete ). Во время выполнения регулярно производится проверка прерываний. Исключение наследуется от BaseException , чтобы его случайно не перехватил код, который перехватывает Exception и, таким образом, предотвращает выход интерпретатора.

Вызывается, когда для операции не хватает памяти, но ситуацию можно исправить (удалив некоторые объекты). Связанное значение представляет собой строку, указывающую, для какой (внутренней) операции закончилась память. Обратите внимание, что из-за базовой архитектуры управления памятью (функция C malloc() ) интерпретатор не всегда может полностью выйти из этой ситуации; тем не менее, он вызывает исключение, чтобы можно было распечатать трассировку стека, если причиной была запущенная программа.

Вызывается, когда локальное или глобальное имя не найдено. Это относится только к неквалифицированным именам. Связанное значение представляет собой сообщение об ошибке, которое включает имя, которое не удалось найти.

Это исключение получено из RuntimeError . В определяемых пользователем базовых классах абстрактные методы должны вызывать это исключение, когда они требуют, чтобы производные классы переопределяли метод, или когда класс разрабатывается, чтобы указать, что реальная реализация всё ещё нуждается в добавлении.

Его не следует использовать для обозначения того, что оператор или метод вообще не предназначен для поддержки — в этом случае либо оставьте оператор/метод неопределенным, либо, если это подкласс, установить для него значение None .

NotImplementedError и NotImplemented не взаимозаменяемы, даже если они имеют схожие названия и цели. См. NotImplemented для получения подробной информации о том, когда его использовать.

Это исключение Вызывается, когда системная функция возвращает ошибку, связанную с системой, включая сбои ввода-вывода, такие как «файл не найден» или «диск заполнен» (не для недопустимых типов аргументов или других случайных ошибок).

Вторая форма конструктора устанавливает соответствующие атрибуты, описанные ниже. Если не указано иное, атрибуты по умолчанию равны None . Для обратной совместимости, если переданы три аргумента, атрибут args содержит только кортеж из двух первых двух аргументов конструктора.

Конструктор часто фактически возвращает подкласс OSError , как описано в Исключения ОС ниже. Подкласс зависит от окончательного значения errno . Такое поведение возникает только при создании OSError напрямую или через псевдоним и не наследуется при создании подклассов.

Числовой код ошибки из C переменной errno .

В Windows предоставляет собственный код ошибки Windows. Атрибут errno в этом случае является приблизительным переводом в терминах POSIX этого собственного кода ошибки.

В Windows, если аргумент конструктора winerror является целым числом, атрибут errno определяется из кода ошибки Windows, а аргумент errno игнорируется. На других платформах аргумент winerror игнорируется, а атрибут winerror не существует.

Соответствующее сообщение об ошибке, предоставляемое операционной системой. Он форматируется функциями C perror() в POSIX и FormatMessage() в Windows.

Для исключений, которые включают путь к файловой системе (например, open() или os.unlink() ), filename — это имя файла, переданное функции. Для функций, которые включают два пути к файловой системе (например, os.rename() ), filename2 соответствует второму имени файла, переданному в функцию.

Изменено в версии 3.3: EnvironmentError , IOError , WindowsError , socket.error , select.error и mmap.error были объединены в OSError , и конструктор может вернуть подкласс.

Изменено в версии 3.4: Атрибут filename теперь является исходным именем файла, переданным функции, а не именем, закодированным или декодированным из кодировки файловой системы. Также был добавлен аргумент и атрибут конструктора filename2.

Вызывается, когда результат арифметической операции слишком велик для представления. Этого не может произойти с целыми числами (которые скорее поднимут MemoryError , чем сдадутся). Однако по историческим причинам OverflowError иногда возникает для целых чисел, выходящих за пределы требуемого диапазона. Из-за отсутствия стандартизации обработки исключений с плавающей запятой в C большинство операций с плавающей запятой не проверяются.

Это исключение получено из RuntimeError . Оно Вызывается, когда интерпретатор обнаруживает превышение максимальной глубины рекурсии (см. sys.getrecursionlimit() ).

Добавлено в версии 3.5: Раньше ставился простой RuntimeError .

Вызывается, когда слабый ссылочный прокси, созданный функцией weakref.proxy() , используется для доступа к атрибуту референта после того, как он был собран сборщиком мусора. Дополнительные сведения о слабых ссылках см. в модуле weakref .

Вызывается при обнаружении ошибки, не попадающей ни в одну из других категорий. Связанное значение представляет собой строку, указывающую, что именно пошло не так.

Вызывается встроенной функцией next() и методом __next__() итератора , чтобы сигнализировать, что итератор больше не производит элементов.

У объекта исключения есть единственный атрибут value , который задается в качестве аргумента при создании исключения и по умолчанию содержит значение None .

Когда функция генератор или корутина возвращается, создается новый экземпляр StopIteration , а значение, возвращаемое функцией, используется в качестве параметра value для конструктора исключения.

Если код генератора прямо или косвенно вызывает StopIteration , он преобразуется в RuntimeError (с сохранением StopIteration в качестве причины нового исключения).

Изменено в версии 3.3: Добавлен атрибут value и возможность функций генератора использовать его для возврата значения.

Изменено в версии 3.5: Введено преобразование RuntimeError через from __future__ import generator_stop , см. PEP 479.

Изменено в версии 3.7: Включите PEP 479 для всего кода по умолчанию: ошибка StopIteration , возникшая в генераторе, преобразуется в RuntimeError .

Вызывается методом __anext__() объекта асинхронного итератора , чтобы остановить итерацию.

Добавлено в версии 3.5.

Вызывается, когда парсер обнаруживает синтаксическую ошибку. Это может происходить в операторе import , при вызове встроенных функций exec() или eval() или при чтении исходного сценария или стандартного ввода (также в интерактивном режиме).

У экземпляров этого класса есть атрибуты filename , lineno , offset и text для облегчения доступа к деталям. str() экземпляра исключения возвращает только сообщение.

Базовый класс для синтаксических ошибок, связанных с неправильным отступом. Это подкласс SyntaxError .

Вызывается, когда отступ содержит непоследовательное использование табуляции и пробелов. Это подкласс IndentationError .

Вызывается, когда интерпретатор обнаруживает внутреннюю ошибку, но ситуация не выглядит настолько серьезной, чтобы терять всякую надежду. Связанное значение представляет собой строку, указывающую, что пошло не так (в терминах низкого уровня).

Вы должны сообщить об этом автору или сопровождающему вашего интерпретатора Python. Обязательно сообщите версию интерпретатора Python ( sys.version ; он также печатается в начале интерактивного сеанса Python), точное сообщение об ошибке (значение, связанное с исключением) и, если возможно, источник программы, вызвавшей ошибку.

Это исключение вызывается функцией sys.exit() . Оно наследуется от BaseException вместо Exception , чтобы случайно не перехватить код, который перехватывает Exception . Позволяет исключению должным образом распространяться и вызвать выход интерпретатора. Когда он не обрабатывается, интерпретатор Python завершает работу; трассировка стека не печатается. Конструктор принимает тот же необязательный аргумент, который передан sys.exit() . Если значение является целым числом, оно указывает статус выхода из системы (передаётся в функцию C exit() ); если это None , статус выхода равен нулю; если у него другой тип (например, строка), значение объекта печатается, а статус выхода — один.

Вызов sys.exit() преобразуется в исключение, чтобы можно было выполнить обработчики очистки (предложения finally операторов try ) и чтобы отладчик мог выполнить сценарий, не рискуя потерять контроль. Функцию os._exit() можно использовать, если абсолютно необходимо немедленно выйти (например, в дочернем процессе после вызова os.fork() ).

Статус выхода или сообщение об ошибке, которое передаётся конструктору. (По умолчанию None .)

Вызывается, когда операция или функция применяется к объекту несоответствующего типа. Связанное значение представляет собой строку с подробными сведениями о несоответствии типов.

Это исключение может быть вызвано пользовательским кодом, чтобы указать, что попытка выполнения операции над объектом не поддерживается и не предназначена для этого. Если объект предназначен для поддержки данной операции, но ещё не предоставил реализацию, NotImplementedError является правильным исключением для вызова.

Передача аргументов неправильного типа (например, передача list , когда ожидается int ) должна привести к TypeError , но передача аргументов с неправильным значением (например, число за пределами ожидаемых границ) должна привести к ValueError .

Вызывается, когда делается ссылка на локальную переменную в функции или методе, но с этой переменной не привязано никакого значения. Это подкласс NameError .

Вызывается при возникновении ошибки кодирования или декодирования, связанной с Юникодом. Это подкласс ValueError .

У UnicodeError есть атрибуты, определяющие ошибку кодирования или декодирования. Например, err.object[err.start:err.end] предоставляет недопустимый ввод, на котором произошёл сбой кодека.

Имя кодировки, вызвавшей ошибку.

Строка, определяющая ошибку кодека.

Объект, который кодек пытался кодировать или декодировать.

Первый индекс неверных данных в object .

Индекс после последних недопустимых данных в object .

Вызывается, когда во время кодирования возникает ошибка, связанная с Юникодом. Это подкласс UnicodeError .

Вызывается, когда во время декодирования возникает ошибка, связанная с Юникодом. Это подкласс UnicodeError .

Вызывается, когда во время перевода возникает ошибка, связанная с Юникодом. Это подкласс UnicodeError .

Вызывается, когда операция или функция получает аргумент с правильным типом, но с несоответствующим значением, и ситуация не определяется более точным исключением, например IndexError .

Вызывается, когда второй аргумент операции деления или операции по модулю равен нулю. Связанное значение представляет собой строку, указывающую тип операндов и операцию.

Следующие исключения сохранены для совместимости с предыдущими версиями; начиная с Python 3.3, они являются псевдонимами OSError .

exception EnvironmentError ¶ exception IOError ¶ exception WindowsError ¶

Доступно только в Windows.

Исключения ОС¶

Следующие исключения являются подклассами OSError , они возникают в зависимости от кода системной ошибки.

Вызывается, когда операция блокируется для объекта (например, сокета), настроенного для неблокирующей операции. Соответствует errno EAGAIN , EALREADY , EWOULDBLOCK и EINPROGRESS .

Помимо OSError , BlockingIOError может иметь ещё один атрибут:

Целое число, содержащее количество символов, записанных в поток до его блокировки. Этот атрибут доступен при использовании классов буферизованного ввода-вывода из модуля io .

Вызывается при сбое операции над дочерним процессом. Соответствует errno ECHILD .

Базовый класс для проблем, связанных с подключением.

Подкласс ConnectionError , возникающий при попытке записи в конвейер, когда другой конец был закрыт, или при попытке записи в сокет, который был отключен для записи. Соответствует errno EPIPE и ESHUTDOWN .

Подкласс ConnectionError , возникающий, когда попытка подключения прерывается одноранговым узлом. Соответствует errno ECONNABORTED .

Подкласс ConnectionError , возникающий, когда одноранговый узел отклоняет попытку подключения. Соответствует errno ECONNREFUSED .

Подкласс ConnectionError , возникающий при сбросе соединения одноранговым узлом. Соответствует errno ECONNRESET .

Возникает при попытке создать уже существующий файл или каталог. Соответствует errno EEXIST .

Вызывается, когда файл или каталог запрашиваются, но не существуют. Соответствует errno ENOENT .

Вызывается, когда системный вызов прерывается входящим сигналом. Соответствует errno EINTR .

Изменено в версии 3.5: Python теперь повторяет системные вызовы, когда системный вызов прерывается сигналом, за исключением случаев, когда обработчик сигнала вызывает исключение (обоснование см. в PEP 475), вместо того, чтобы вызывать InterruptedError .

Вызывается, когда для каталога запрашивается файловая операция (например, os.remove() ). Соответствует errno EISDIR .

Вызывается, когда операция с каталогом (например, os.listdir() ) запрашивается для чего-то, что не является каталогом. Соответствует errno ENOTDIR .

Вызывается при попытке запустить операцию без соответствующих прав доступа — например, разрешений файловой системы. Соответствует errno EACCES и EPERM .

Вызывается, когда данный процесс не существует. Соответствует errno ESRCH .

Вызывается, когда истекло время системной функции на системном уровне. Соответствует errno ETIMEDOUT .

Добавлено в версии 3.3: Были добавлены все вышеперечисленные подклассы OSError .

PEP 3151 — переработка иерархии исключений ОС и ввода-вывода

Предупреждения¶

Следующие исключения используются как категории предупреждений; см. документацию Категории предупреждений для получения более подробной информации.

Базовый класс для категорий предупреждений.

Базовый класс для предупреждений, генерируемых пользовательским кодом.

Базовый класс для предупреждений об устаревших функциях, когда эти предупреждения предназначены для других разработчиков Python.

Базовый класс для предупреждений об устаревших функциях, которые, как ожидается, будут исключены в будущем, но в настоящее время не являются устаревшими.

Этот класс используется редко, т. к. выдача предупреждения о предстоящем прекращении поддержки является необычным явлением, а DeprecationWarning предпочтительнее для уже активных прекращений поддержки.

Базовый класс для предупреждений о сомнительном синтаксисе.

Базовый класс для предупреждений о сомнительном поведении во время выполнения.

Базовый класс для предупреждений об устаревших функциях, когда эти предупреждения предназначены для конечных пользователей приложений, написанных на Python.

Базовый класс для предупреждений о возможных ошибках при импорте модулей.

Базовый класс для предупреждений, связанных с Юникодом.

Базовый класс для предупреждений, связанных с bytes и bytearray .

Базовый класс для предупреждений, связанных с использованием ресурсов. Игнорируется фильтрами предупреждений по умолчанию.

Добавлено в версии 3.2.

Иерархия исключений¶

Иерархия классов для встроенных исключений:

Источник

Python StopIteration

Introduction to Python StopIteration

The following article provides an outline for Python StopIteration. As we are well aware of the topic ‘iterator’ and ‘iterable’ in Python. The basic idea of what the ‘iterator’ is? Iterator is basically an object that holds a value (generally a countable number) which is iterated upon. Iterator in Python uses the __next__() method in order to traverse to the next value. In order to tell that there are no more values that need to be traversed by the __next__() method, a StopIteration statement is used. Programmers usually write a terminating condition inside __next__() method in order to stop it after the specified condition is reached.

Syntax of Python StopIteration

When the specified number of iterations are done, StopIteration is raised by the next method in case of iterators and generators (works similar to iterators except it generates a sequence of values at a time instead of a single value). If we go in deep understanding of Python, the situation of raising ‘StopIteration’ is not considered as an error. It is considered an Exception and can be handled easily by catching that exception similar to other exceptions in Python.

General syntax of using StopIteration in if and else of next() method is as follows:

class classname:

def __iter__(self):
…
…     #set of statements
return self;

def __next__(self):

if …. #condition till the loop needs to be executed
….   #set of statements that needs to be performed till the traversing needs to be done
return …

else
raise StopIteration #it will get raised when all the values of iterator are traversed 

How StopIteration works in Python?

StopIteration stops the iterations after the maximum limit is reached or it discontinues moving the loop forever.

Key points that needs to be keep in mind to know the working of StopIteration in Python are as follows:

  • It is raised by the method next() or __next__() which is a built-in method in python to stop the iterations or to show that no more items are left to be iterated upon.
  • We can catch the StopIteration exception by writing the code inside the try block and catching the exception using the ‘except’ keyword and printing it on screen using the ‘print’ keyword.
  • Value returned by raising the StopIteration is used as a parameter of the Exception Constructor in order to perform the desired actions.
  • next() method in both generators and iterators raises it when no more elements are present in the loop or any iterable object.

Examples of Python StopIteration

Given below are the examples mentioned:

Example #1

Stop the printing of numbers after 20 or printing numbers incrementing by 2 till 20 in the case of Iterators.

Code:

class printNum:
  def __iter__(self):
    self.z = 2
    return self

  def __next__(self):
    if self.z <= 20:   #performing the action like printing the value on console till the value reaches 20
      y = self.z
      self.z += 2
      return y
    else:
      raise StopIteration   #raising the StopIteration exception once the value gets              increased from 20

obj = printNum()
value_passed = iter(obj)

for u in value_passed:
  print(u)

Output:

Python StopIteration 1

Explanation:

  • In the above example, in order to iterate through the values, two methods, i.e. iter() and next() are used. If we see, in the next() method if and else statements are used in order to check when the iteration and hence their respective actions (which is printing of values in this case) should get terminated.
  • If the iterable value is less than or equals to 20, it continues to print those values at the increment of 2. Once the value reaches greater than 20, the next() method raises a StopIteration exception.

Example #2

Finding the cubes of number and stop executing once the value becomes equal to the value passed using StopIteration in the case of generators.

Code:

def values():     #list of integer values with no limits
    x = 1             #initializing the value of integer to 1   
    while True:
        yield x
        x+=  1

def findingcubes():    
    for x in values():      
        yield x * x *x     #finding the cubes of value ‘x’

def func(y, sequence):    
    
    sequence = iter(sequence) 
    output = [ ]    #creating an output blank array 
    try:
        for x in range(y):   #using the range function of python to use for loop
            output.append(next(sequence))   #appending the output in the array
    except StopIteration:    #catching the exception
        pass
    return output   

print(func(5, findingcubes()))  #passing the value in the method ‘func’

Output:

Python StopIteration 2

Explanation:

  • In the above example, we are finding the cubes of number from 1 till the number passed in the function. We are generating multiple values at a time using the generators in Python and in order to stop the execution once the value reaches the one passed in the function, StopIteration exception is raised.
  • Different methods are created serving their respective purpose like generating the values, finding the cubes and printing the value by storing them in the output array. Basic python functions are used in the program like range, append, etc which should be clear in the initial stages of learning to the programmer.

How to Avoid StopIteration Exception in Python?

  • As seen above StopIteration is not an error in Python but an exception and is used to run the next() method for the specified number of iterations. Iterator in Python uses the two methods, i.e. iter() and next().
  • The next() method raises an StopIteration exception when the next() method is called manually.
  • The best way to avoid this exception in Python is to use normal looping or use it as a normal iterator instead of writing the next() method again and again.
  • Otherwise if not able to avoid StopIteration exception in Python, we can simply raise the exception in next() method and catch the exception like a normal exception in Python using the except keyword.

Conclusion

As discussed above in the article it must be clear to you what is the StopIteration exception and in which condition it is raised in Python. StopIteration exception could be an issue to deal with for the new programmers as it can be raised in many situations. But proper understanding of its scenarios in which it could be raised and techniques to avoid it can help them to program better.

Recommended Articles

This is a guide to Python StopIteration. Here we discuss how StopIteration works in python and how to avoid StopIteration exception with programming examples. You may also have a look at the following articles to learn more –

  1. Python Regex Tester
  2. Python Rest Server
  3. Python String Operations
  4. Python Counter

Следующие исключения являются исключениями, которые обычно возникают во время исполнения программы.

Содержание:

  • Исключение StopIteration
  • Исключение StopAsyncIteration
  • Исключение ArithmeticError
  • Исключение AssertionError
  • Исключение AttributeError
  • Исключение BufferError
  • Исключение EOFError
  • Исключение ImportError
    • Исключение ModuleNotFoundError
  • Исключение LookupError
    • Исключение IndexError
    • Исключение KeyError
  • Исключение MemoryError
  • Исключение NameError
    • Исключение UnboundLocalError
  • Исключение OSError
  • Исключение ReferenceError
  • Исключение RuntimeError
    • Исключение NotImplementedError
    • Исключение RecursionError
  • Исключение SyntaxError
    • Исключение IndentationError
    • Исключение TabError
  • Исключение SystemError
  • Исключение TypeError
  • Исключение ValueError
  • Исключение UnicodeError
  • Исключение EnvironmentError
  • Исключение IOError
  • Исключение WindowsError

StopIteration:

Исключение StopIteration вызывается встроенной функцией next() и методом итератора __next__(), чтобы сигнализировать, что итератор больше не производит никаких элементов.

Объект исключения имеет единственный атрибут value, который задается в качестве аргумента при создании исключения и по умолчанию равен None.

Когда функция генератора или сопрограммы возвращается, создается новый экземпляр StopIteration, и значение, возвращаемое функцией, используется в качестве параметра value для конструктора исключения.

Если код генератора прямо или косвенно поднимает StopIteration, он преобразуется в RuntimeError, сохраняя StopIteration как причину нового исключения.

StopAsyncIteration:

Исключение StopAsyncIteration вызывается методом __next__() объекта асинхронного итератора, чтобы остановить итерацию.

ArithmeticError:

AssertionError:

Исключение AssertionError вызывается когда оператор assert терпит неудачу.

AttributeError:

Исключение AttributeError вызывается при сбое ссылки на атрибут или присвоения. Если объект не поддерживает ссылки на атрибуты или назначения атрибутов вообще, вызывается TypeError.

BufferError:

Исключение BufferError вызывается когда операция, связанная с буфером, не может быть выполнена.

EOFError:

Исключение EOFError вызывается когда функция input() попадает в состояние конца файла без чтения каких-либо данных. Когда методы io.IOBase.read() and io.IOBase.readline() возвращают пустую строку при попадании в EOF.

ImportError:

Исключение ImportError вызывается когда оператор import имеет проблемы при попытке загрузить модуль. Также ImportError поднимается, когда “из списка» в конструкция from ... import имеет имя, которое не может быть найдено.

Атрибуты name и path можно задать с помощью аргументов конструктора, содержащих только ключевые слова. При установке они представляют имя модуля, который был предпринят для импорта, и путь к любому файлу, который вызвал исключение, соответственно.

  • ModuleNotFoundError:

    Исключение ModuleNotFoundError подкласс ImportError, который вызывается оператором import, когда модуль не может быть найден. Он также вызывается, когда в sys.modules имеет значение None.

LookupError:

Исключение LookupError — базовый класс для исключений, возникающих при недопустимости ключа или индекса, используемого в сопоставлении или последовательности: IndexError, KeyError. Исключение LookupError может быть вызван непосредственно codecs.lookup().

  • IndexError:

    Исключение IndexError вызывается когда индекс последовательности находится вне диапазона. Индексы среза усекаются без каких либо предупреждений, чтобы попасть в допустимый диапазон. Если индекс не является целым числом, поднимается исключение TypeError.

  • KeyError:

    Исключение KeyError вызывается когда ключ сопоставления словаря не найден в наборе существующих ключей.

MemoryError:

Исключение MemoryError вызывается, когда операции не хватает памяти, но ситуация все еще может быть спасена путем удаления некоторых объектов. Значение представляет собой строку, указывающую какой внутренней операции не хватило памяти. Обратите внимание, что из-за базовой архитектуры управления памятью интерпретатор не всегда может полностью восстановиться в этой ситуации. Тем не менее, возникает исключение, чтобы можно было напечатать трассировку стека.

NameError:

Исключение NameError вызывается, когда локальное или глобальное имя не найдено. Значение — это сообщение об ошибке, содержащее имя, которое не удалось найти.

  • UnboundLocalError:

    Исключение UnboundLocalError вызывается, когда ссылка сделана на локальную переменную в функции или методе, но никакое значение не было привязано к этой переменной. Это подкласс NameError.

OSError:

ReferenceError:

Исключение ReferenceError вызывается, когда слабый эталонный прокси-сервер, созданный функцией weakref.proxy() используется для доступа к атрибуту референта после сбора его мусора.

RuntimeError:

Исключение RuntimeError вызывается при обнаружении ошибки, которая не попадает ни в одну из других категорий. Связанное значение является строкой, указывающей, что именно пошло не так.

  • NotImplementedError:

    Исключение NotImplementedError получено из RuntimeError. В определяемых пользователем базовых классах абстрактные методы должны вызывать это исключение, когда им требуется, чтобы производные классы переопределяли метод, или когда класс разрабатывается, чтобы указать, что реальная реализация все еще должна быть добавлена.

    Заметки:

    1. Его не следует использовать для указания того, что оператор или метод вообще не предполагается поддерживать — в этом случае либо оставьте оператор/метод неопределенным, либо, установите его в None.
    2. NotImplementedError и NotImplemented не являются взаимозаменяемыми, даже если они имеют схожие имена и цели. Смотрите подробности NotImplemented о том, когда его использовать.
  • RecursionError:

    Исключение RecursionError получено из RuntimeError. Исключение RecursionError вызывается, когда интерпретатор обнаруживает, что максимальная глубина рекурсии sys.getrecursionlimit() превышена.

SyntaxError:

Исключение SyntaxError вызывается, когда синтаксический анализатор обнаруживает синтаксическую ошибку. Ошибка данного типа может произойти в инструкции import, при вызове встроенной функции exec() или eval(), или при чтении первоначального сценария или стандартный ввода, также в интерактивном режиме.

Экземпляры этого класса имеют атрибуты filename, lineno, offset и text для облегчения доступа к информации. Функция str() экземпляра исключения возвращает только сообщение.

  • IndentationError:

    Исключение IndentationError служит базовым классом для синтаксических ошибок, связанных с неправильным отступом. Это подкласс SyntaxError.

  • TabError:

Исключение TabError вызывается, когда отступ содержит несоответствующее использование символов табуляции и пробелов. Это подкласс IndentationError.

SystemError:

Исключение SystemError вызывается, когда интерпретатор обнаруживает внутреннюю ошибку, но ситуация не выглядит настолько серьезной, чтобы заставить его отказаться от всякой надежды. Ассоциированное значение — это строка, указывающая, что пошло не так (в терминах низкого уровня).

TypeError:

Исключение TypeError вызывается, когда операция или функция применяется к объекту неподходящего типа. Связанное значение представляет собой строку, содержащую сведения о несоответствии типов.

Исключение TypeError может быть вызвано пользовательским кодом, чтобы указать, что попытка выполнения операции над объектом не поддерживается и не должна поддерживаться. Если объект предназначен для поддержки данной операции, но еще не предоставил реализацию, то вызывайте исключение NotImplementedError.

Передача аргументов неправильного типа, например передача списка, когда ожидается целое число, должна привести к TypeError, но передача аргументов с неправильным значением, например число вне ожидаемых границ, должна привести к ValueError.

ValueError:

Исключение ValueError вызывается, когда операция или функция получает аргумент, который имеет правильный тип, но недопустимое значение, и ситуация не описывается более точным исключением, таким как IndexError.

UnicodeError:

EnvironmentError:

Доступно только в Windows.

IOError:

Доступно только в Windows.

WindowsError:

Доступно только в Windows.

«Напиши, пожалуйста, кастомный итератор,» — такое задание довольно часто дают на собеседованиях, и я раз за разом вижу обреченные глаза кандидата, когда он сталкивается с подобной просьбой. Опыт участия в собеседованиях показал мне, что большинство начинающих разработчиков бегут от этой темы, потому что она кажется слишком запутанной и непонятной. А ведь ничего сложного в ней нет, если подобраться к ней правильным образом — в чём я и постараюсь помочь дорогим читателям.

Наше путешествие мы начнем с того, что вообще такое итератор. Итератор — это некий объект, который в себе реализует интерфейс перебора чего-либо. А говоря рабоче-крестьянским языком — это такая штука, которая в себе описывает правило, по которому мы будем перебирать содержимое той или иной коробки.

Давайте представим, что у нас есть тумбочка. В этой тумбочке лежат несколько предметов: ножницы, карандаш, яблоко и книга. И перед нами, как перед разработчиками ПО, поставили задачу: описать с помощью кода тумбочку, которая могла бы принимать на хранение некие объекты и выдавать содержимое по требованию.

Конечно же, мы можем хранить наше содержимое тумбочки в любой удобной коллекции, например, в списке:

tumb = ["ножницы", "карандаш", "яблоко", "книга"]

И решать задачу добавления объектов посредством методов списка (через append, например), а задачу перебора — с помощью цикла for:

for obj in tumb:
		print(obj)

«Ну и при чём тут какой-то там итератор?» — спросите меня вы. А что, если я скажу вам, что цикл for работает не совсем так, как вы думаете?

Во многих статьях и книгах пишут про то, что цикл for позволяет перебирать объекты коллекций. Это правда. Однако часто из внимания упускают то, как именно он это делает. А ведь это фундаментально важно для понимания нашей темы.

На самом деле цикл for взаимодействует не с самим целевым объектом перебора, а с его итератором! В нашем случае — с итератором списка. То есть он как бы говорит: «Эй, объект! Я хочу тебя перебрать, поэтому дай мне то, что описывает правило твоего перебора!» Если объект сможет ответить на это «Вот, держи!» и вернет циклу for некий объект, то объект называется итерируемым и его можно перебирать. Если же он отвечает что-то вроде «Я не понимаю, о чём ты», то программа выдаст ошибку, и это будет означать, что мы попытались перебрать объект, который для этого не предназначен.

Весь фокус в том, что итерируемый объект в случае успеха в качестве ответа на запрос «Дай правило итерации!» возвращает объект итератор.

Как цикл for получает объект-итератор от целевого итерируемого объекта? С помощью неявного вызова встроенной функции iter, в которую в качестве аргумента он передаёт как раз этот самый целевой итерируемый объект. И если в результате будет получен некий объект, то дальнейшая работа будет производиться уже с ним. Давайте посмотрим, что мы получим в качестве результата, если передадим наш список в метод iter:

>>> print(iter(tumb))
<list_iterator object at 0x10d4c53d0>

Видите? Мы получили объект типа list_iterator, инкапсулирующий в себе то самое правило перебора, которое сейчас будет применяться.

После успешного получения итератора цикл for начинает взаимодействовать с ним тупым нажиманием кнопки «давай следующее значение» до тех пор, пока эти значения не будут исчерпаны. Представьте себе, что вам дали в руки пульт с нопкой и сказали нажимать на неё до тех пор, пока вы не получите на экране сообщение «Хватит!»

Как цикл for нажимет эту воображаемую кнопку у итератора? С помощью ещё одной встроенной функцииnext, аргументом которой является объект-итератор, полученный на предыдущем шаге. Результатом этого в штатном случае будет получение очередного значения. Такая процедура будет повторяться многократно до тех пор, пока цикл for не получит сообщение о том, что все значения уже закончились. Что это за сообщение? Это raise ошибки StopIteration.

А теперь давайте взглянем на аналог цикла for, написанный через while:

tumb = ["ножницы", "карандаш", "яблоко", "книга"]

# получаем итератор для итерируемого объекта
it = iter(tumb)

try:
    while True:
        next_val = next(it)
        print("Очередное значение:", next_val)
except StopIteration:
    # явно напечатаем сообщение об окончании итерации,
    # хотя цикл for этого не делает и ошибка просто подавляется
    print("Итерация закончена")
print("Программа завершена")

То есть ответственность за перебор лежит не на цикле for (он просто запрашивает итератор и жмëт в нëм кнопку) и не на самом итерируемом объекте (он лишь должен отдавать свой объект-итератор по запросу), а на итераторе!

Хорошо, с этим понятно, но что там с каким-то кастомным итератором? Это про что вообще история? А эта история про ситуации, когда вам необходимо, чтобы объекты ваших самописных классов тоже можно было итерировать. Давайте представим, что нам пришлось написать для тумбочки отдельный класс, чтобы навесить там много разных дополнительных методов, суть которых нам не важна в описываемом контексте, но важно то, что мы хотели бы иметь возможность перебирать нашу тумбочку, как если бы это был простой список.

Пускай у нашей тумбочки предметы хранятся в составе списковых полей, каждое из которых будет соответствовать тому или иному ящику. У тумбочки будут методы добавления и удаления элементов в ящики. Наш класс будет выглядеть как-то так:

class Tumbochka:
		"""Волшебная тумбочка с тремя ящиками для чего угодно"""

    def __init__(self):
        self.boxes = {
            1: [],
            2: [],
            3: []
        }

    def add_to_box(self, obj, box_num):
        if box_num not in {1, 2, 3}:
            print("Вы ввели неправильный номер ящика!")
        else:
            self.boxes[box_num].append(obj)

    def remove_from_box(self, box_num):
        if box_num not in {1, 2, 3}:
            print("Вы ввели неправильный номер ящика!")
        else:
            return self.boxes[box_num].pop()

    def __str__(self):
        boxes_items = self.boxes[1] + self.boxes[2] + self.boxes[3]
        return ", ".join(boxes_items)

Создадим тумбочку, нагрузим её предметами и выведем информацию на экран:

tumb = Tumbochka()
tumb.add_to_box("ножницы", 1)
tumb.add_to_box("карандаш", 2)
tumb.add_to_box("яблоко", 3)
tumb.add_to_box("книга", 1)
print(tumb)

А теперь вопрос: как нам сделать так, чтобы нашу тумбочку можно было итерировать? Можно, конечно, взять и сделать что-то вроде нового списка, который будет хранить в себе сумму элементов трех ящиков: tumb.boxes[1] + tumb.boxes[2] + tumb.boxes[3] и итерировать его, но это не очень хороший подход. Почему? Давайте представим себе, что у нас есть список с несколькими итерируемыми объектами: списком, множеством и строкой.

my_shiny_list = [
    ["Это", "список", "внутри", "списка"],
    {"Это", "множество", "внутри", "списка"},
    "Это строка внутри списка",
]

И у нас вознкает необходимость добавить к этим товарищам ещё и нашу тумбочку (а может даже и не одну):

my_shiny_list = [
    ["Это", "список", "внутри", "списка"],
    {"Это", "множество", "внутри", "списка"},
    "Это строка внутри списка",
    tumb,
]

Согласитесь, что писать сумму списков внутри вложенного списка — не самое изящное решение, да и сам класс в процессе развития нашего проекта может измениться: например, уберутся или добавятся новые ящики. Придется помнить про все подобные места и вносить правки в них.

Теперь давайте представим, что по логике работы нашей программы у нас предполагается последовательное итерирование каждого из элементов нашего списка:

for some_collection in my_shiny_list:
    for el in some_collection:
        print(el)

В какой-то момент мы получим ошибку TypeError: 'Tumbochka' object is not iterable. Python говорит нам, что мы попытались проитерировать объект, который на приказ «Дай мне свой итератор!» отвечает что-то вроде «Я не понимаю, о чëм ты!».

Почему так произошло? Дело в том, что наш класс тумбочки понятия не имеет, кто отвечает за правило перебора элементов в ней.

Как это устранить? Нужно сделать так, чтобы встроенная функция iter получала от нашего объекта тумбочки её итератор. Для этого нам потребуется дописать в классе тумбочки магический метод __iter__, назначение которого как раз и состоит в том, чтобы создавать и возвращать в результате своей работы некий объект-итератор.

Но давайте посмотрим повнимательнее — для решения нашей задачи достаточно, чтобы в качестве итератора тумбочки выступал итератор списка суммы трëх ящиков. Для его получения нам нужно будет просто передать эту сумму во встроенную функцию iter и уже результат по работы вернуть в качестве результата магического метода __iter__! Вот как это будет выглядеть:

def __iter__(self):
    # получаем сумму предметов всех ящиков
    boxes_items = self.boxes[1] + self.boxes[2] + self.boxes[3]
    # получаем итератор от списка и возвращаем его
    it = iter(boxes_items)
    return it

Теперь наша тумбочка без проблем сможет быть перебранной наравне с известными встроенными коллекциями и по праву будет являться итерируемым объектом.

И что, это и есть кастомный итератор? Нет! В вышеупомянутом примере мы воспользовались итератором списка в качестве итератора нашей тумбочки. То есть сам итератор не имеет ни малейшего понятия, что его вернули как результат работы какой-то там тумбочки; его задача состоит лишь в том, чтобы перебирать.

А теперь давайте с вами представим, что мы хотим, чтобы наша тумбочка при итерации возвращала не просто объекты, а ещё и их адреса в памяти. Тут возникает проблема: итератор списка нам уже не поможет. Нужно что-то помощнее.

Можно обратиться за помощью к генераторам (это такая разновидность итератора, про которую вы можете прочесть замечательную статью моего товарища, а также посмотреть серию его видео по теме). И реализация будет выглядеть примерно так:

def __iter__(self):
    # получаем сумму предметов всех ящиков
    boxes_items = self.boxes[1] + self.boxes[2] + self.boxes[3]
    # возвращаем очередное значение
    # (пару "объект в ящике тумбочки + адрес в памяти") с помощью yield
    for el in boxes_items:
        yield el, id(el)

Результатом работы магического метода __iter__ будет generator_object, из которого по запросу мы сможем получать очередные пары значений.

Ну, а теперь это кастомный итератор? Всë ещë нет! Необходимость написания кастомного итератора возникает тогда, когда мы хотим тонко управлять процессом перебора. Например, иметь возможность при каком-то событии начать итерацию с самого начала или установить значение указателя перебора на определённый элемент. То есть не перебирать всё подряд, как в цикле for, а управлять вручную нашим процессом через непосредственное взаимодействие с итератором.

Как решить эту задачу? Объект-генератор нам не подходит, использовать итераторы коллекций — тоже. Придëтся писать что-то своё :) Это самое «что-то своё» и будет называться кастомным итератором. Это отдельный класс, объект которого будет возвращаться в качестве результата работы метода __iter__. Давайте напишем простой класс:

class TumbochkaIterator:
    pass

А в классе тумбочки поменяем магический метод __iter__ на вот такую реализацию:

def __iter__(self):
    return TumbochkaIterator()

И попробуем получить итератор тумбочки через встроенную функцию iter:

iter(tumb)

Мы увидим ошибку TypeError: iter() returned non-iterator of type 'TumbochkaIterator', которая любезно сообщает о том, что тот объект, который мы вернули, итератором на самом деле не является.

А что является, спросите вы? А является итератором то, что обладает специальным магическим методом, способным возвращать очередное значение. Именно возвращать очередное значение! Таким магическим методом является метод __next__. Этот метод будет отрабатывать каждый раз, когда объект итератора будет передаваться во встроенную функцию next.

Давайте добавим пустой метод __next__ в наш класс-итератор:

class TumbochkaIterator:
    def __next__(self):
        pass

Теперь посмотрим на результат, который выведет этот код:

>>> print(iter(tumb))
<main.TumbochkaIterator object at 0x10acecee0>

Видите? Мы теперь получаем в качестве результата работы функции iter от нашей тумбочки самый настоящий объект итератор! При этом он не имеет никакого понятия из чего он вернулся — из тумбочки, ящика или грузовика. Ему об этом знать не нужно, его задача будет состоять лишь в том, чтобы возвращать очередное значение, когда его передадут в next!

Давайте теперь попробуем вручную получить несколько очередных значений из итератора:

it = iter(tumb)
print(next(it))
print(next(it))
print(next(it))
print(next(it))

None
None
None
None

Мы получили четыре объектаNone, потому что именно их возвращает нам метод __next__. Давайте сделаем так, чтобы __next__ возвращал единичку:

class TumbochkaIterator:
    def __next__(self):
        return 1

Теперь при попытке запустить код выше мы получим четыре единички.

1
1
1
1

А теперь давайте свяжем наши объекты в тумбочке с итератором: пусть итератор при инициализации принимает в себя ссылку на все объекты трëх ящиков. Также заведëм счетчик, который поможет нам всегда знать, на каком именно объекте перебора мы сейчас находимся. А метод __next__ изменим таким образом, чтобы на каждый его вызов мы возвращали следующий элемент и увеличивали при этом значение счетчика на единицу для того, чтобы при следующем вызове __next__ вернуть очередное значение. Общий код классов будет выглядеть так:

class TumbochkaIterator:
    def __init__(self, some_objects):
				self.some_objects = some_objects
				self.current = 0

    def __next__(self):
        if self.current < len(self.some_objects):
            result = self.some_objects[self.current]
            self.current += 1
            return result
class Tumbochka:
    """Волшебная тумбочка с тремя ящиками для чего угодно"""

    def __init__(self):
        self.boxes = {
            1: [],
            2: [],
            3: []
        }

    def add_to_box(self, obj, box_num):
        if box_num not in {1, 2, 3}:
            print("Вы ввели неправильный номер ящика!")
        else:
            self.boxes[box_num].append(obj)

    def remove_from_box(self, box_num):
        if box_num not in {1, 2, 3}:
            print("Вы ввели неправильный номер ящика!")
        else:
            return self.boxes[box_num].pop()

    def __str__(self):
        boxes_items = self.boxes[1] + self.boxes[2] + self.boxes[3]
        return ", ".join(boxes_items)

    def __iter__(self):
        return TumbochkaIterator(self.boxes[1] + self.boxes[2] + self.boxes[3])

А теперь давайте запустим вот этот код:

tumb = Tumbochka()
tumb.add_to_box("ножницы", 1)
tumb.add_to_box("карандаш", 2)
tumb.add_to_box("яблоко", 3)
tumb.add_to_box("книга", 1)
it = iter(tumb)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it))

Вот, что мы получим в результате:

ножницы
книга
карандаш
яблоко
None
None

Что это за None? Откуда это всë взялось? Дело в том, что встроенной функции next не важно, что вернулось в качестве результата работы __next__. Пусть даже это будет None.

А теперь давайте добавим пару методов. Один будет возвращать значение current на старт, другой — на выбранную позицию, но в пределах списка наших элементов. Выглядеть эти методы будут вот так:

def to_start(self):
    self.current = 0

def to_current(self, val):
    if val >= len(self.some_objects) or val < 0:
        print("Неверное значение для курсора!")
    else:
        self.current = val

Теперь мы можем при ручной итерации через next обнулять нашу итерацию или перемещать курсор к какому-то другому значению. Например, мы можем сказать, что если тот элемент, который я достал из тумбочки — это ножницы, то следующий элемент я буду пропускать и перемещать курсор на шаг вперед. Можете попробовать написать такое условие в качестве домашнего задания к этой статье ;)

А сейчас я предлагаю запустить процесс итерации нашей тумбочки в цикле for и посмотреть на результат:

for el in tumb:
    print(el)

Давайте взглянем на результат (скорее всего, у вас будет просто None, потому что значения будут лететь очень быстро, я рекомендую добавить sleep(.1) перед вызовом print):

ножницы
книга
карандаш
яблоко
None
None
None
None
None
None

Программа ушла в бесконечный цикл и остановить её можно только с помощью ручного останова. Почему так произошло? Да потому что циклу for тоже не важно, что вернулось в качестве очередного значения из __next__ — он будет жать кнопку «Дай!» до тех пор, пока не возникнет исключение StopIteration. Возникновение этого исключения мы и должны теперь предусмотреть в методе __next__. Давайте сделаем это:

def __next__(self):
    if self.current < len(self.some_objects):
        result = self.some_objects[self.current]
        self.current += 1
        return result
    raise StopIteration 

Перезапустим наш код и взглянем на результат:

ножницы
книга
карандаш
яблоко

Теперь всё работает замечательно! Остался последний штрих: по соглашению объекты-итераторы также должны являться итерируемыми объектами. Принято в качестве итераторов для самих итераторов использовать их самих. Звучит зубодробительно, но я думаю, что взглянув на метод __iter__ в составе нашего итератора вопросы уйдут:

class TumbochkaIterator:
		def __init__(self, some_objects):
				self.some_objects = some_objects
        self.current = 0

    def to_start(self):
        self.current = 0

    def to_current(self, val):
        if val >= len(self.some_objects) or val < 0:
            print("Неверное значение для курсора!")
        else:
            self.current = val

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < len(self.some_objects):
            result = self.some_objects[self.current]
            self.current += 1
            return result
        raise StopIteration

То есть объект возвращает сам себя в качестве итератора, если мы хотим проитерировать сам итератор. Это сделано для того, чтобы у мы могли перебирать объекты кастомных итераторов в цикле for точно так же, как это можно делать с generator_objects или итераторами коллекций, вроде list_iterator.

Подытожим:

  • Итерируемый объект — это объект, который можно перебирать.

  • За правило перебора отвечает итератор, а не сам объект.

  • Итерируемый объект при попытке его перебрать должен уметь возвращать свой итератор, чтобы уже с ним продолжалась работа.

  • Метод, который возвращает итератор, называется __iter__.

  • Объект-итератор должен иметь метод __next__, который возвращает очередное значение.

  • Цикл for будет вызывать функцию next от итератора до тех пор, пока не получит исключение StopIteration.

  • Возникновение StopIteration — это ответственность итератора, а именно его метода __next__.

  • Если StopIteration не возникнет никогда, то мы получим бесконечный цикл.

  • Написание кастомного итератора может понадобиться в том случае, если необходимо тонко управлять процессом итерации. Для стандартных случаев зачастую достаточно использовать итераторы стандартных коллекций или объекты-генераторы.

Благодарю за внимание.

Понравилась статья? Поделить с друзьями:
  • Stopcode video scheduler internal error
  • Stream write error openiv
  • Stopcode video dxgkrnl fatal error
  • Stream write error jsgme
  • Stopcode registry error