How to check if an object is iterable in Python
An iterable is an object which can be traversed. Data structures like array, string, and tuples are iterables.
In certain cases, e.g., when dealing with custom data types, we may not know whether an object is iterable, but for this there should be some kind of mechanism through which a user can estimate its iterable status.
In this article, we will see how we can identify if an object is iterable using three techniques.
1. Iterating over the object¶
This is the simplest way to check if an object is iterable.
This style of programming is called duck typing («If it looks like a duck and quacks like a duck, it must be a duck.»).
2. Using iter() ¶
If an object is iterable, it will not throw an error when used with iter() . It checks if the passed object has __iter__() or __getitem__() method available, which are methods that iterables possess.
3. Using collection.abc.Iterable¶
The collection library houses specialized container datatypes and abstract base classes to test if a class provides particular interfaces. Coupled with built-in isinstance() method we can get a boolean response. This technique does not work with classes that implement __getitem__() dunder method.
FREE VS Code / PyCharm Extensions I Use
✅ Write cleaner code with Sourcery, instant refactoring suggestions: Link*
How can I check if an object is an iterator in Python?
I can check for a next() method, but is that enough? Is there an idiomatic way?
![]()
10 Answers 10
In Python 2.6 or better, the designed-in idiom for such behavioral checks is a «membership check» with the abstract base class in the collections module of the standard library:
Indeed, this kind of checks is the prime design reason for the new abstract base classes (a second important one is to provide «mixin functionality» in some cases, which is why they’re ABCs rather than just interfaces — but that doesn’t apply to collections.Iterable , it exists strictly to allow such checks with isinstance or issubclass ). ABCs allow classes that don’t actually inherit from them to be «registered» as subclasses anyway, so that such classes can be «subclasses» of the ABC for such checks; and, they can internally perform all needed checks for special methods ( __iter__ in this case), so you don’t have to.
If you’re stuck with older releases of Python, «it’s better to ask forgiveness than permission»:
but that’s not as fast and concise as the new approach.
Note that for this special case you’ll often want to special-case strings (which are iterable but most application contexts want to treat as «scalars» anyway). Whatever approach you’re using to check iterableness, if you need such special casing just prepend a check for isinstance(x, basestring) — for example:
Функция iter() в Python
Функция iter() возвращает итератор заданного объекта:
Аргументом функции iter() должен быть итерируемый объект или последовательность. В общем случае аргументом может быть любой объект, поддерживающий протокол итерации или последовательности.
Когда вы вызываете функцию iter() для определенного объекта, функция сначала ищет метод __iter__() этого объекта.
- Если метод __iter__() существует, функция iter() вызывает его для получения итератора. В противном случае функция iter() будет искать метод __getitem__() .
- Если метод __getitem__() существует, функция iter() создаст объект итератора и вернет этот объект.
- В противном случае возникает исключение TypeError.
Пример использования iter()
Давайте создадим простой класс Counter и используем функцию iter() для получения итератора объекта counter :
Объект Counter не является итерируемым объектом, поэтому возникнет исключение TypeError: ‘Counter’ object is not iterable .
Теперь добавим метод __getitem__() в класс Counter :
Поскольку в Counter есть метод __getitem__() , который возвращает элемент на основе индекса, он является последовательностью.
Теперь вы можете использовать функцию iter() , чтобы получить итератор counter :
Вывод
В этом случае Python создает объект-итератор и возвращает его. Следовательно, вы можете использовать объект итератора для перебора counter :
Теперь давайте класс CounterIterator к классу Counter и реализуем протокол итерируемого объекта:
Как это работает
- В классе Counter есть метод __iter__() , который возвращает итератор. Возвращаемый итератор является новым экземпляром CounterIterator .
- Класс CounterIterator поддерживает протокол итераторов, реализуя методы __iter__() и __next__() .
Когда существуют оба метода __iter__() и __getitem__() , функция iter() всегда использует метод __iter__() :
Вывод
В этом примере функция iter() вызывает метод __iter__() вместо метода __getitem__() . Поэтому в выводе вы видите итератор CounterIterator .
Вторая форма функции iter()
Функцию iter() можно использовать и так:
- callable — вызываемый объект,
- sentinel — специальное значение, которое используется для представления конца последовательности.
Функция iter(callable, sentinel) вызовет callable-переменную, когда будет вызван метод next() .
Функция вернет значение, возвращенное вызываемой переменной, или вызовет исключение StopIteration, если результат будет равен значению sentinel .
Давайте рассмотрим, как работает iter(callable, sentinel) , на конкретном примере.
1. Сначала создадим функцию, которая возвращает замыкание:
Функция counter() возвращает замыкание. А замыкание при вызове возвращает новое целое число, начиная с единицы.
2. Используем функцию counter() , чтобы показать числа от 1 до 3:
Вывод
Чтобы сделать ее более общей, вместо нее можно использовать итератор.
3. Определим новый итератор счетчика:
Конструктор CounterIterator принимает вызываемую fn и sentinel .
Метод __next__() возвращает значение, возвращаемое вызываемой переменной fn , или вызывает исключение StopIteration, если возвращаемое значение равно sentinel .
Вот, как использовать итератор CounterIterator :
Вывод
Вместо того чтобы определять новый итератор каждый раз, когда вы хотите перебрать значения, возвращаемые вызываемой переменной, вы можете использовать функцию iter(callable, sentinel) :
Вывод
iter(), чтобы проверить, является ли объект итерируемым
Чтобы определить, является ли объект итерируемым, можно проверить, реализует ли он метод __iter__() или __getitem__() .
Однако вы можете использовать функцию iter() , чтобы проверить, является ли объект итерируемым, следующим образом:
Если у объекта нет ни метода __iter__() , ни метода __getitem__() , функция iter() вызывает исключение TypeError.
Как в Python определить, является ли объект итерируемым?
FWIW: iter(myObj) преуспеет, если isinstance(myObj, dict) , поэтому, если вы смотрите на myObj это может быть последовательность dict s или сингл dict , вы добьетесь успеха в обоих случаях. Тонкость, важная, если вы хотите знать, что является последовательностью, а что нет. (в Python 2) — Ben Mosher
__getitem__ также достаточно, чтобы сделать объект повторяемым . если он начинается с нулевого индекса. — Carlos A. Gómez
21 ответы
Проверка на __iter__ работает с типами последовательностей, но не работает, например, со строками в Python 2. Я тоже хотел бы знать правильный ответ, а пока вот одна возможность (которая также будет работать со строками):
Наблюдения и советы этой статьи мы подготовили на основании опыта команды iter встроенные проверки для __iter__ метод или в случае строк __getitem__ метод.
Другой общий питонический подход состоит в том, чтобы предположить итерацию, а затем изящно выйти из строя, если она не работает с данным объектом. Глоссарий Python:
Стиль программирования Pythonic, который определяет тип объекта путем проверки его метода или сигнатуры атрибута, а не путем явной связи с некоторым типом объекта («Если он выглядит как утка и шарлатаны, как уткаэто должно быть утка. «) Делая упор на интерфейсы, а не на конкретные типы, хорошо разработанный код улучшает его гибкость, позволяя полиморфную замену. Утиная типизация позволяет избежать тестов с использованием type () или isinstance (). Вместо этого обычно используется стиль программирования EAFP (проще просить прощения, чем разрешения).
.
Наблюдения и советы этой статьи мы подготовили на основании опыта команды collections Модуль предоставляет некоторые абстрактные базовые классы, которые позволяют запрашивать классы или экземпляры, предоставляют ли они определенные функции, например:
Однако это не проверяет классы, которые можно повторять через __getitem__ .
ответ дан 11 мар ’20, в 16:03
[e for e in my_object] может вызвать исключение по другим причинам, т. е. my_object не определено или возможные ошибки в my_object выполнение. — Ник Дандулакис
Строка is последовательность ( isinstance(», Sequence) == True ) и как любая последовательность это is итерируемый ( isinstance(», Iterable) ). Хотя hasattr(», ‘__iter__’) == False и это может сбивать с толку. — JFS
If my_object очень большой (скажем, бесконечный, как itertools.count() ) понимание вашего списка займет много времени / памяти. Лучше сделать генератор, который никогда не будет пытаться построить (потенциально бесконечный) список. — Крис Лутц
Что если некоторый_объект выдает ошибку TypeError по другой причине (ошибки и т. д.)? Как мы можем отличить это от «Не повторяемой ошибки TypeError»? — Шаунг
Обратите внимание, что в Python 3: hasattr(u»hello», ‘__iter__’) Возвращает True — Чарли
Утка печатать
Проверка типа
Использовать Абстрактные базовые классы. Им нужен как минимум Python 2.6, и они работают только с классами нового стиля.
Однако, iter() немного более надежен, как описано по документации:
Контроль isinstance(obj, Iterable) обнаруживает классы, зарегистрированные как Iterable или имеющие __iter__() метод, но он не обнаруживает классы, которые повторяются с __getitem__() метод. Единственный надежный способ определить, является ли объект итерируемым, — это вызвать iter(obj) .
ответ дан 04 окт ’18, 13:10
Из «Fluent Python» Лучано Рамальо: Начиная с Python 3.4, наиболее точный способ проверить, является ли объект x итерируемым, — это вызвать iter (x) и обработать исключение TypeError, если это не так. Это более точно, чем использование isinstance (x, abc.Iterable), потому что iter (x) также учитывает устаревшие получить предмет метод, а Iterable ABC — нет. — РдБ
Если вы думаете: «О, я просто isinstance(x, (collections.Iterable, collections.Sequence)) вместо iter(x) «, обратите внимание, что это по-прежнему не обнаружит итерируемый объект, который реализует только __getitem__ но не __len__ . Использовать iter(x) и поймать исключение. — Dale
Ваш второй ответ не работает. В ПЮНО если сделаю iter(slide1) , все идет хорошо, однако isinstance(slide1, Iterable) бросает TypeError: issubclass() arg 1 must be a class . — Привет, ангел
Вызов iter () над объектом может быть дорогостоящей операцией (см. DataLoader в Pytorch, который разветвляет / порождает несколько процессов на iter ()). — сали
Кажется, что перечислить () имеет тот же эффект, что и iter () (Python 3), что может немного упростить ситуацию, если следующее, что вы хотите сделать, это перечислить последовательность — нет необходимости в явном iter () заранее, поскольку enumerate () вызовет соответствующее исключение сам по себе при необходимости. — Марцин Войнарски
Я хотел бы пролить немного света на взаимодействие iter , __iter__ и __getitem__ и что происходит за занавесками. Вооружившись этими знаниями, вы сможете понять, почему лучшее, что вы можете сделать, — это
Сначала я перечислю факты, а затем быстро напомню, что происходит, когда вы используете for цикл в Python с последующим обсуждением, чтобы проиллюстрировать факты.
Факты
Вы можете получить итератор из любого объекта o позвонив по телефону iter(o) если выполняется хотя бы одно из следующих условий:
a) o имеет __iter__ метод, который возвращает объект-итератор. Итератор — это любой объект с __iter__ и еще один __next__ (Питон 2: next ) метод.
b) o имеет __getitem__ метод.
Проверка экземпляра Iterable or Sequence , или проверка атрибута __iter__ недостаточно.
Если объект o только орудия __getitem__ , Но не __iter__ , iter(o) создаст итератор, который пытается получить элементы из o по целочисленному индексу, начиная с индекса 0. Итератор поймает любой IndexError (но никаких других ошибок), который возникает, а затем вызывает StopIteration себя.
В самом общем смысле невозможно проверить, возвращается ли итератор iter разумно, кроме как попробовать.
Если объект o инвентарь __iter__ , iter функция будет следить за тем, чтобы объект, возвращаемый __iter__ итератор. Проверка работоспособности отсутствует, если объект реализует только __getitem__ .
__iter__ побеждает. Если объект o реализует как __iter__ и __getitem__ , iter(o) позвоню __iter__ .
Если вы хотите, чтобы ваши собственные объекты были итерируемыми, всегда реализуйте __iter__ метод.
for петли
Чтобы следовать по тексту, вам нужно понимать, что происходит, когда вы используете for цикл в Python. Не стесняйтесь переходить к следующему разделу, если вы уже знаете.
Когда вы используете весы для поддона for item in o для некоторого итеративного объекта o , Python вызывает iter(o) и ожидает, что в качестве возвращаемого значения будет объект-итератор. Итератор — это любой объект, реализующий __next__ (или next в Python 2) и __iter__ метод.
По условию __iter__ метод итератора должен возвращать сам объект (т.е. return self ). Затем Python вызывает next на итераторе до тех пор, пока StopIteration Поднялся. Все это происходит неявно, но следующая демонстрация делает это видимым:
Итерация над DemoIterable :
Обсуждение и иллюстрации
По пунктам 1 и 2: получение итератора и ненадежные проверки
Рассмотрим следующий класс:
призвание iter с примером BasicIterable вернет итератор без проблем, потому что BasicIterable инвентарь __getitem__ .
Тем не менее, важно отметить, что b не имеет __iter__ атрибут и не считается экземпляром Iterable or Sequence :
Вот почему Свободный Python Лучано Рамальо рекомендует позвонить iter и использование потенциала TypeError как наиболее точный способ проверить, является ли объект повторяемым. Цитата прямо из книги:
Начиная с Python 3.4, наиболее точный способ проверить, x повторяется — это вызвать iter(x) и справиться с TypeError исключение, если это не так. Это более точно, чем использование isinstance(x, abc.Iterable) , потому как iter(x) также рассматривает наследие __getitem__ метод, в то время как Iterable ABC — нет.
По пункту 3: перебор объектов, которые только предоставляют __getitem__ , Но не __iter__
Итерация по экземпляру BasicIterable работает должным образом: Python создает итератор, который пытается получить элементы по индексу, начиная с нуля, до тех пор, пока IndexError Поднялся. Демонстрационный объект __getitem__ метод просто возвращает item который был предоставлен в качестве аргумента __getitem__(self, item) итератором, возвращенным iter .
Обратите внимание, что итератор вызывает StopIteration когда он не может вернуть следующий элемент и что IndexError который поднимается для item == 3 обрабатывается внутри. Вот почему перебирая BasicIterable с for цикл работает как ожидалось:
Вот еще один пример, чтобы понять, как итератор возвращается iter пытается получить доступ к элементам по индексу. WrappedDict не наследуется от dict , что означает, что у экземпляров не будет __iter__ метод.
Обратите внимание, что звонки на __getitem__ делегированы dict.__getitem__ для которого обозначение квадратных скобок является просто сокращением.
По пунктам 4 и 5: iter проверяет наличие итератора при вызове __iter__ :
После появления iter(o) вызывается для объекта o , iter гарантирует, что возвращаемое значение __iter__ , если метод присутствует, является итератором. Это означает, что возвращаемый объект должен реализовывать __next__ (или next в Python 2) и __iter__ . iter не может выполнять какие-либо проверки работоспособности для объектов, которые только предоставляют __getitem__ , потому что у него нет возможности проверить, доступны ли элементы объекта по целочисленному индексу.
Обратите внимание, что создание итератора из FailIterIterable экземпляры сразу же терпят неудачу, при построении итератора из FailGetItemIterable успешно, но вызовет исключение при первом вызове __next__ .
По пункту 6: __iter__ выигрывает
Это просто. Если объект реализует __iter__ и __getitem__ , iter позвоню __iter__ . Рассмотрим следующий класс
и вывод при обходе экземпляра:
По пункту 7: ваши итерируемые классы должны реализовывать __iter__
Вы можете спросить себя, почему большинство встроенных последовательностей, таких как list реализовать __iter__ метод, когда __getitem__ было бы достаточно.
В конце концов, итерация по экземплярам вышеупомянутого класса, который делегирует вызовы __getitem__ в list.__getitem__ (используя обозначение квадратных скобок), будет работать нормально:
Причины, по которым ваши пользовательские итерации должны реализовывать __iter__ заключаются в следующем:
- Если вы реализуете __iter__ , экземпляры будут считаться итерациями, и isinstance(o, collections.abc.Iterable) вернется True .
- Если объект, возвращенный __iter__ не итератор, iter немедленно потерпит неудачу и вызовет TypeError .
- Особое обращение с __getitem__ существует по причинам обратной совместимости. Еще раз процитируем Fluent Python:
Вот почему любая последовательность Python повторяется: все они реализуют __getitem__ . Фактически, стандартные последовательности также реализуют __iter__ , и ваш тоже должен, потому что особая обработка __getitem__ существует по причинам обратной совместимости и может исчезнуть в будущем (хотя, когда я пишу это, он не является устаревшим).