В Python, как я могу определить, если объект является итерируемым?
есть ли такой метод, как isiterable
? Единственное решение, которое я нашел до сих пор, - это вызвать
hasattr(myObj, '__iter__')
но я не уверен,насколько это доказательство.
19 ответов
-
проверка
__iter__
работает на типах последовательностей, но это приведет к сбою, например, strings в Python 2. Я также хотел бы знать правильный ответ, до тех пор, вот одна возможность (которая будет работать и на строках):try: some_object_iterator = iter(some_object) except TypeError as te: print some_object, 'is not iterable'
на
iter
встроенные проверки для__iter__
метод или в случае строк__getitem__
метод. -
еще одно общее для Python подход-предположить, повторяемое, то не изящно, если это не работает на данном объекте. Глоссарий Python:
Pythonic стиль программирования, который определяет тип объекта путем проверки его метода или подписи атрибута, а не явным отношением к некоторому объекту типа ("если он выглядит как утка и крякает как утка, должно быть утка.") Подчеркивая интерфейсы, а не конкретные типы, хорошо разработанный код повышает свою гибкость за счет допускает полиморфное замещение. Duck-typing позволяет избежать тестов с использованием type () или isinstance (). вместо этого он обычно использует стиль программирования EAFP (легче попросить прощения, чем разрешения).
...
try: _ = (e for e in my_object) except TypeError: print my_object, 'is not iterable'
-
на
collections
модуль предоставляет некоторые абстрактные базовые классы, которые позволяют задавать классы или экземпляры, если они предоставляют определенную функциональность, для пример:import collections if isinstance(e, collections.Iterable): # e is iterable
однако это не проверяет классы, которые можно перебирать через
__getitem__
.
утиной типизацией
try:
iterator = iter(theElement)
except TypeError:
# not iterable
else:
# iterable
# for obj in iterator:
# pass
тип проверки
использовать Абстрактные Базовые Классы. Им нужен хотя бы Python 2.6 и они работают только для классов нового стиля.
from collections.abc import Iterable # import directly from collections for Python < 3.3
if isinstance(theElement, Iterable):
# iterable
else:
# not iterable
, iter()
немного более надежный, как описано документация:
проверка
isinstance(obj, Iterable)
обнаруживает классов зарегистрирован как Iterable или что есть__iter__()
способ, но он не обнаруживает классы, которые итерируют с__getitem__()
метод. Единственный надежный способ определить, является ли объект является iterable является вызовiter(obj)
.
я хотел бы пролить немного больше света на игру iter
, __iter__
и __getitem__
и что происходит за кулисами. Вооружившись этим знанием, вы сможете понять, почему лучшее, что вы можете сделать, это
try:
iter(maybe_iterable)
print('iteration will probably work')
except TypeError:
print('not iterable')
сначала я перечислю факты, а затем последую за быстрым напоминанием о том, что происходит, когда вы используете for
цикл в python, а затем обсуждение, чтобы проиллюстрировать факты.
факты
вы можете получить итератор из любого объекта
o
по телефонуiter(o)
если хотя бы одно из следующих условий верно:
a)o
есть__iter__
метод, который возвращает объект iterator. Итератор-это любой объект с__iter__
и__next__
(Python 2:next
) метод.
b)o
есть__getitem__
метод.проверка по пример
Iterable
илиSequence
, или проверка атрибут__iter__
не хватает.если объект
o
реализует только__getitem__
, но не__iter__
,iter(o)
построит итератор, который пытается извлечь элементы изo
по индексу, начиная с индекса 0. Итератор поймает любоеIndexError
(но никаких других ошибок), который поднимается, а затем поднимается .в самом общем смысле, невозможно проверить, возвращен ли итератор
iter
в здравом уме, кроме как попробовать.если объект
o
осуществляет__iter__
, theiter
функция убеждается что объект возвращен__iter__
- это итератор. Нет вменяемость проверить если объект реализует только__getitem__
.__iter__
выигрывает. Если объектo
реализует как__iter__
и__getitem__
,iter(o)
позвоню__iter__
.если вы хотите сделать свой собственный итерируемые объекты, всегда использовать тег
__iter__
метод.
for
петли
для того, чтобы следовать вдоль, вам нужно понимание того, что происходит, когда вы используете for
цикл в Python. Не стесняйтесь сразу перейти к следующему разделу, если вы уже знаете.
при использовании for item in o
для некоторого iterable объекта o
, Python вызывает iter(o)
и ожидает объект iterator в качестве возвращаемого значения. Итератор-это любой объект, который реализует __next__
(или next
в Python 2) Метод и __iter__
метод.
по соглашению __iter__
метод итератора должен возвращать сам объект (т. е. return self
). Python, а затем называет next
на итераторе пока StopIteration
поднимается. Все это происходит неявно, но следующая демонстрация делает его видимым:
import random
class DemoIterable(object):
def __iter__(self):
print('__iter__ called')
return DemoIterator()
class DemoIterator(object):
def __iter__(self):
return self
def __next__(self):
print('__next__ called')
r = random.randint(1, 10)
if r == 5:
print('raising StopIteration')
raise StopIteration
return r
итерация по a DemoIterable
:
>>> di = DemoIterable()
>>> for x in di:
... print(x)
...
__iter__ called
__next__ called
9
__next__ called
8
__next__ called
10
__next__ called
3
__next__ called
10
__next__ called
raising StopIteration
Обсуждение и иллюстрации
в пунктах 1 и 2: Получение итератора и ненадежных проверок
рассмотрим следующий класс:
class BasicIterable(object):
def __getitem__(self, item):
if item == 3:
raise IndexError
return item
вызов iter
С BasicIterable
возвращает итератор без каких-либо проблем, потому что BasicIterable
осуществляет __getitem__
.
>>> b = BasicIterable()
>>> iter(b)
<iterator object at 0x7f1ab216e320>
однако, важно отметить, что b
нет __iter__
атрибут и не считается экземпляром Iterable
или Sequence
:
>>> from collections import Iterable, Sequence
>>> hasattr(b, '__iter__')
False
>>> isinstance(b, Iterable)
False
>>> isinstance(b, Sequence)
False
вот почему Свободно Python Лучано Рамальо рекомендует позвонить iter
и обработка потенциала TypeError
а самый точный способ проверить, является ли объект является итерируемым. Цитата прямо из книги:
начиная с Python 3.4, самый точный способ проверить, является ли объект
x
is iterable является вызовiter(x)
и решенияTypeError
исключение, если это не так. Это более точно, чем использованиеisinstance(x, abc.Iterable)
, потому чтоiter(x)
также считает, что наследство__getitem__
метод, в то время какIterable
ABC не.
в пункте 3: итерация по объектам, которые предоставляют только __getitem__
, но не __iter__
итерация над экземпляром BasicIterable
работает как ожидалось: Python
создает итератор, который пытается получить элементы по индексу, начиная с нуля, до IndexError
поднимается. Этот демо-объекта __getitem__
метод просто возвращает item
который был предоставлен в качестве аргумента __getitem__(self, item)
по iterator, возвращенный iter
.
>>> b = BasicIterable()
>>> it = iter(b)
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
обратите внимание, что итератор вызывает StopIteration
когда он не может вернуть следующий элемент и что IndexError
, который поднимается на item == 3
обрабатывается внутренне. Вот почему петля над BasicIterable
с for
цикл работает, как ожидалось:
>>> for x in b:
... print(x)
...
0
1
2
вот еще один пример, чтобы привести домой концепцию как iterator, возвращенный iter
пытается получить доступ к элементам по индексу. WrappedDict
не наследуется от dict
, что означает, что экземпляры не имеют __iter__
метод.
class WrappedDict(object): # note: no inheritance from dict!
def __init__(self, dic):
self._dict = dic
def __getitem__(self, item):
try:
return self._dict[item] # delegate to dict.__getitem__
except KeyError:
raise IndexError
обратите внимание, что вызов __getitem__
возложены на dict.__getitem__
для которого квадратная скобка-это просто стенография.
>>> w = WrappedDict({-1: 'not printed',
... 0: 'hi', 1: 'StackOverflow', 2: '!',
... 4: 'not printed',
... 'x': 'not printed'})
>>> for x in w:
... print(x)
...
hi
StackOverflow
!
по пунктам 4 и 5:iter
проверяет наличие итератора при вызове __iter__
:
, когда iter(o)
для объект o
, iter
убедитесь, что возвращаемое значение __iter__
, если метод присутствует, является итератором. Это означает, что возвращаемый объект
необходимо реализовать __next__
(или next
в Python 2) и __iter__
. iter
не удается выполнить проверку здравомыслия для объектов, которые только
обеспечить __getitem__
, потому что он не может проверить, доступны ли элементы объекта по целочисленному индексу.
class FailIterIterable(object):
def __iter__(self):
return object() # not an iterator
class FailGetitemIterable(object):
def __getitem__(self, item):
raise Exception
обратите внимание, что построение итератора из FailIterIterable
экземпляры немедленно терпят неудачу при построении итератора из FailGetItemIterable
успешно, но будет выдавать исключение при первом вызове __next__
.
>>> fii = FailIterIterable()
>>> iter(fii)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'object'
>>>
>>> fgi = FailGetitemIterable()
>>> it = iter(fgi)
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/path/iterdemo.py", line 42, in __getitem__
raise Exception
Exception
по пункту 6:__iter__
победы
это просто. Если объект реализует __iter__
и __getitem__
, iter
будем называть __iter__
. Рассмотрим следующий класс
class IterWinsDemo(object):
def __iter__(self):
return iter(['__iter__', 'wins'])
def __getitem__(self, item):
return ['__getitem__', 'wins'][item]
и выход при циклическом пример:
>>> iwd = IterWinsDemo()
>>> for x in iwd:
... print(x)
...
__iter__
wins
в пункте 7: ваши итерационные классы должны реализовать __iter__
вы можете спросить себя, почему большинство встроенных последовательностей, как list
реализовать __iter__
метод, когда __getitem__
будет достаточно.
class WrappedList(object): # note: no inheritance from list!
def __init__(self, lst):
self._list = lst
def __getitem__(self, item):
return self._list[item]
в конце концов, итерация над экземплярами класса выше, который делегирует вызовы __getitem__
to list.__getitem__
(использование квадратных скобок), будет работать отлично:
>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
... print(x)
...
A
B
C
причины, по которым ваши пользовательские iterables должны реализовать __iter__
следующим образом:
- если вы реализуете
__iter__
, экземпляры будут считаться iterables иisinstance(o, collections.Iterable)
вернутсяTrue
. - если объект, возвращенный
__iter__
не является итератором,iter
сразу и не поднятьTypeError
. - специальная обработка
__getitem__
существует для обратной совместимости. Цитирование опять же из Fluent Python:
именно поэтому любой последовательности Python является итерируемым: все они исполняют
__getitem__
. Фактически, стандартные последовательности также реализуют__iter__
, и твое тоже, потому что специальная обработка__getitem__
существует для обратной совместимости и может быть ушел в будущее (хотя это не устарело, как я пишу это).
этого недостаточно: объект, возвращенный __iter__
необходимо реализовать протокол итерации (т. е. next
метод). См. соответствующий раздел в документация.
в Python хорошей практикой является " попробовать и увидеть "вместо"проверки".
try:
#treat object as iterable
except TypeError, e:
#object is not actually iterable
не выполнять проверки, чтобы увидеть Если ваша утка действительно утка чтобы увидеть, является ли он итерационным или нет, относитесь к нему так, как если бы он был, и жалуйтесь, если это не так.
лучшее решение я нашел до сих пор:
hasattr(obj, '__contains__')
который в основном проверяет, реализует ли объект in
оператора.
преимущества (ни одно из других решений не имеет всех трех):
- это выражение (работает как лямда -, а не попробовать...кроме вариант)
- это (должно быть) реализованы все итераторы, в том числе строки (в отличие от
__iter__
) - работает на любом Python >= 2.5
Примечания:
- философия Python "просите прощения, а не разрешения" не работает хорошо, когда, например, в списке у вас есть как iterables, так и non-iterables, и вам нужно относиться к каждому элементу по-разному в зависимости от его типа (обработка iterables на try и non-iterables на except б работать, но это будет выглядеть уродливее и заблуждение)
- решения этой проблемы, которые пытаются фактически перебирать объект (например, [x для x в obj]), чтобы проверить, является ли он итерационным, могут вызвать значительные штрафы за производительность для больших итераций (особенно если вам просто нужны первые несколько элементов итерабельного, например) и их следует избегать
в Python
но начиная с Python 2.6 и 3.0 вы можете использовать новую инфраструктуру ABC (абстрактный базовый класс) вместе с некоторыми встроенными ABCs, которые доступны в модуле collections:
from collections import Iterable
class MyObject(object):
pass
mo = MyObject()
print isinstance(mo, Iterable)
Iterable.register(MyObject)
print isinstance(mo, Iterable)
print isinstance("abc", Iterable)
Теперь, является ли это желательным или на самом деле работает, это просто вопрос конвенции. Как видите, вы ... --8-->can зарегистрировать не итерируемый объект, а метод - и это будет вызывать исключение во время выполнения. Следовательно, isinstance приобретает "новое" значение - он просто проверяет" объявленную " совместимость типов, что является хорошим способом пойти в Python.
С другой стороны, если ваш объект не удовлетворяет интерфейс, который вам нужен, что вы собираетесь делать? Возьмем следующий пример:
from collections import Iterable
from traceback import print_exc
def check_and_raise(x):
if not isinstance(x, Iterable):
raise TypeError, "%s is not iterable" % x
else:
for i in x:
print i
def just_iter(x):
for i in x:
print i
class NotIterable(object):
pass
if __name__ == "__main__":
try:
check_and_raise(5)
except:
print_exc()
print
try:
just_iter(5)
except:
print_exc()
print
try:
Iterable.register(NotIterable)
ni = NotIterable()
check_and_raise(ni)
except:
print_exc()
print
Если объект не удовлетворяет тому, что вы ожидаете, вы просто бросаете TypeError, но если была зарегистрирована правильная ABC, ваш чек непригоден. Наоборот, если __iter__
доступен метод Python автоматически распознает объект этого класса как итеративный.
Итак, если вы просто ожидаете итерацию, повторите ее и забудьте. С другой стороны, если вам нужно делать разные вещи в зависимости от типа ввода, вы можете найти инфраструктуру ABC довольно полезной.
Я нашел хорошее решение здесь:
isiterable = lambda obj: isinstance(obj, basestring) \
or getattr(obj, '__iter__', False)
вы можете попробовать это:
def iterable(a):
try:
(x for x in a)
return True
except TypeError:
return False
Если мы можем сделать генератор, который перебирает (но никогда не используйте генератор, поэтому она не займет много места), это повторяемое. Похоже на "да". Почему вам нужно определить, является ли переменная итерабельной в первую очередь?
по словам Python 2 Глоссарий, iterables являются
все типы последовательности (например,
list
,str
иtuple
) и некоторые типы не последовательности, такие какdict
иfile
и объекты любых классов, которые вы определяете с помощью__iter__()
или__getitem__()
метод. Итерабли могут использоваться в цикле for и во многих других местах, где необходима последовательность (zip (), map (),...). Когда итерируемый объект передается в качестве аргумента встроенной функции ИТЭР(), это возвращает итератор для объекта.
конечно, учитывая общий стиль кодирования для Python, основанный на том, что "легче просить прощения, чем разрешения.", общее ожидание-использовать
try:
for i in object_in_question:
do_something
except TypeError:
do_something_for_non_iterable
но если вам нужно проверить его явно, вы можете проверить итерацию по hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__")
. Вам нужно проверить оба, потому что str
s не имеют __iter__
метод (по крайней мере, не в Python 2, в Python 3 они делают) и потому что generator
объекты нет __getitem__
метод.
Я часто нахожу удобным, внутри моих скриптов, определить . (Теперь включает предложенное Альфой упрощение):
import collections
def iterable(obj):
return isinstance(obj, collections.Iterable):
так что вы можете проверить, если любой объект является итерируемым в очень читабельном виде
if iterable(obj):
# act on iterable
else:
# not iterable
как вы сделали бы сcallable
функции
EDIT: если у вас установлен numpy, вы можете просто сделать: от numpy import iterable
,
это просто что-то вроде
def iterable(obj):
try: iter(obj)
except: return False
return True
если у вас нет numpy, вы можете просто реализовать этот код, или тот, что выше.
панды имеет встроенную функцию, как это:
from pandas.util.testing import isiterable
С Python 3.5 можно использовать введя модуль из стандартной библиотеки для типа операции:
from typing import Iterable
...
if isinstance(my_item, Iterable):
print(True)
def is_iterable(x):
try:
0 in x
except TypeError:
return False
else:
return True
это будет поддакивать всякой итерируемые объекты, но это будет скажите " нет " строкам в Python 2. (Вот что я хочу, например, когда рекурсивная функция может принимать строку или контейнер строк. В такой ситуации ... --17-->прошу прощения может привести к обфускодированию, и лучше сначала спросить разрешение.)
import numpy
class Yes:
def __iter__(self):
yield 1;
yield 2;
yield 3;
class No:
pass
class Nope:
def __iter__(self):
return 'nonsense'
assert is_iterable(Yes())
assert is_iterable(range(3))
assert is_iterable((1,2,3)) # tuple
assert is_iterable([1,2,3]) # list
assert is_iterable({1,2,3}) # set
assert is_iterable({1:'one', 2:'two', 3:'three'}) # dictionary
assert is_iterable(numpy.array([1,2,3]))
assert is_iterable(bytearray("not really a string", 'utf-8'))
assert not is_iterable(No())
assert not is_iterable(Nope())
assert not is_iterable("string")
assert not is_iterable(42)
assert not is_iterable(True)
assert not is_iterable(None)
многие другие стратегии здесь скажут " да " строкам. Используйте их, если вы хотеть.
import collections
import numpy
assert isinstance("string", collections.Iterable)
assert isinstance("string", collections.Sequence)
assert numpy.iterable("string")
assert iter("string")
assert hasattr("string", '__getitem__')
Примечание: is_iterable () скажет " да " строкам типа bytes
и bytearray
.
-
bytes
объекты в Python 3 являются iterableTrue == is_iterable(b"string") == is_iterable("string".encode('utf-8'))
в Python 2 такого типа нет. -
bytearray
объекты в Python 2 и 3 являются iterableTrue == is_iterable(bytearray(b"abc"))
О. П. hasattr(x, '__iter__')
подход скажет " да "строкам в Python 3 и" нет " в Python 2 (независимо от того,''
или b''
или u''
). Спасибо @LuisMasuelli за заметив, что он также подведет вас на багги __iter__
.
самый простой способ, уважая в Python утиной типизацией, чтобы поймать ошибку (Python прекрасно знает, что он ожидает от объекта, чтобы стать итератором):
class A(object):
def __getitem__(self, item):
return something
class B(object):
def __iter__(self):
# Return a compliant iterator. Just an example
return iter([])
class C(object):
def __iter__(self):
# Return crap
return 1
class D(object): pass
def iterable(obj):
try:
iter(obj)
return True
except:
return False
assert iterable(A())
assert iterable(B())
assert iterable(C())
assert not iterable(D())
Примечания:
- это различия не имеют значения, является ли объект не повторяемое, или глючит
__iter__
реализовано, если тип исключения один и тот же: в любом случае вы не сможете выполнить итерацию объекта. -
Я думаю, что я поймите вашу озабоченность: как это
callable
существует как проверка, могу ли я также полагаться на типирование утки, чтобы поднятьAttributeError
Если__call__
не определено для моего объекта, но это не относится к итеративной проверке?Я не знаю ответа, но вы можете либо реализовать функцию, которую я (и другие пользователи) дал, либо просто поймать исключение в своем коде (ваша реализация в этой части будет похожа на функцию, которую я написал - Просто убедитесь, что вы изолируете создание итератора от остальной части код, чтобы вы могли захватить исключение и отличить его от другого
TypeError
.
на isiterable
func при следующем коде возвращает True
если объект является итерируемым. если это не iterable возвращает False
def isiterable(object_):
return hasattr(type(object_), "__iter__")
пример
fruits = ("apple", "banana", "peach")
isiterable(fruits) # returns True
num = 345
isiterable(num) # returns False
isiterable(str) # returns False because str type is type class and it's not iterable.
hello = "hello dude !"
isiterable(hello) # returns True because as you know string objects are iterable
вместо проверки __iter__
атрибут, вы можете проверить для __len__
атрибут, который реализуется каждым python builtin iterable, включая строки.
>>> hasattr(1, "__len__")
False
>>> hasattr(1.3, "__len__")
False
>>> hasattr("a", "__len__")
True
>>> hasattr([1,2,3], "__len__")
True
>>> hasattr({1,2}, "__len__")
True
>>> hasattr({"a":1}, "__len__")
True
>>> hasattr(("a", 1), "__len__")
True
None-iterable объекты не будут реализовывать это по очевидным причинам. Однако он не улавливает определяемые пользователем итерабли, которые не реализуют его, а также выражения генератора, которые iter
можно иметь дело. Однако это можно сделать в строке и добавив простой or
проверка выражений генераторы устранят эту проблему. (Обратите внимание, что писать!--6--> бросить NameError
. См.этой ответ.)
вы можете использовать GeneratorType из типов:
>>> import types >>> types.GeneratorType <class 'generator'> >>> gen = (i for i in range(10)) >>> isinstance(gen, types.GeneratorType) True
- - - - принятый ответ utdemir
(это делает его полезным для проверки, если это можно так назвать len
на объект.)
это всегда ускользало от меня, почему python имеет callable(obj) -> bool
а не iterable(obj) -> bool
...
конечно, это легче сделать hasattr(obj,'__call__')
даже если это медленнее.
так как почти каждый другой ответ рекомендует использовать try
/except TypeError
, где тестирование исключений обычно считается плохой практикой среди любого языка, вот реализация iterable(obj) -> bool
Я стал больше любить и использовать часто:
ради python 2 я буду использовать лямбду только для этой дополнительной производительности повышение...
(в python 3 не имеет значения, что вы используете для определения функции, def
имеет примерно такую же скорость как lambda
)
iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__')
обратите внимание, что эта функция выполняется быстрее для объектов с __iter__
так как это не испытание для __getitem__
.
большинство итерируемых объектов должны полагаться на __iter__
где специальные объекты падают обратно в __getitem__
, хотя для итерации объекта требуется либо то, либо другое.
(и поскольку это стандарт, это влияет на C объекты)
помимо обычной попытки, вы можете запустить help.
temp= [1,2,3,4]
help(temp)
help даст все методы, которые могут быть запущены на этом объекте(это может быть любой объект и не может быть списком, как в Примере), который является временным в этом случае.
Примечание:это было бы то, что вы сделали бы вручную.