В Python, как мне определить, является ли объект итеративным?
Есть ли такой метод, как isiterable
? Единственное решение, которое я нашел, это позвонить
hasattr(myObj, '__iter__')
Но я не уверен, насколько это глупо.
25 ответов
Проверка на
__iter__
работает с типами последовательностей, но не работает, например, со строками в Python 2. Я также хотел бы знать правильный ответ, до тех пор, здесь есть одна возможность (которая также будет работать со строками):try: some_object_iterator = iter(some_object) except TypeError as te: print some_object, 'is not iterable'
iter
встроенные проверки для__iter__
метод или в случае строк__getitem__
метод.Другой общий питонический подход - предполагать итеративность, а затем изящно проваливаться, если она не работает с данным объектом. Глоссарий Python:
Стиль программирования Pythonic, который определяет тип объекта путем проверки его метода или сигнатуры атрибута, а не путем явного отношения к какому-либо объекту типа ("Если он выглядит как утка и крякает как утка, он должен быть уткой".) Подчеркивая интерфейсы В отличие от конкретных типов, хорошо разработанный код повышает его гибкость, позволяя полиморфную замену. Утиная типография избегает тестов с использованием type() или isinstance(). Вместо этого он обычно использует стиль программирования EAFP (Проще просить прощения, чем разрешения).
...
try: _ = (e for e in my_object) except TypeError: print my_object, 'is not iterable'
collections
Модуль предоставляет некоторые абстрактные базовые классы, которые позволяют запрашивать классы или экземпляры, предоставляют ли они определенную функциональность, например:from collections.abc import Iterable if isinstance(e, 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__()
метод. Единственный надежный способ определить, является ли объект итеративным, это вызватьiter(obj)
,
Я хотел бы пролить немного больше света на взаимодействие iter
, __iter__
а также __getitem__
и что происходит за кулисами. Вооружившись этими знаниями, вы сможете понять, почему лучшее, что вы можете сделать, это
try:
iter(maybe_iterable)
print('iteration will probably work')
except TypeError:
print('not iterable')
Сначала я перечислю факты, а затем быстро напомню, что происходит, когда вы нанимаете for
цикл в Python, а затем обсуждение, чтобы проиллюстрировать факты.
факты
Вы можете получить итератор из любого объекта
o
позвонивiter(o)
если выполняется хотя бы одно из следующих условий:
а)o
имеет__iter__
метод, который возвращает объект итератора. Итератор - это любой объект с__iter__
и__next__
(Python 2:next
) метод.
б)o
имеет__getitem__
метод.Проверка на экземпляр
Iterable
или же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
Поднялся. Все это происходит неявно, но следующая демонстрация делает это видимым:
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
Итерация по 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
Вот почему Fluent Python от Luciano Ramalho рекомендует позвонить 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
,
>>> 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
Вот еще один пример, чтобы показать, как итератор возвращается 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: 'Stackru', 2: '!',
... 4: 'not printed',
... 'x': 'not printed'})
>>> for x in w:
... print(x)
...
hi
Stackru
!
По пунктам 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__
в list.__getitem__
(используя обозначение в квадратных скобках), будет работать нормально:
>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
... print(x)
...
A
B
C
Причины, по которым ваши пользовательские итерации должны быть реализованы __iter__
являются следующими:
- Если вы реализуете
__iter__
экземпляры будут считаться повторяемыми, иisinstance(o, collections.Iterable)
вернусьTrue
, - Если объект возвращен
__iter__
не итератор,iter
немедленно потерпит неудачу и подниметTypeError
, - Специальная обработка
__getitem__
существует по причинам обратной совместимости. Цитирую снова из Fluent Python:
Вот почему любая последовательность Python является итеративной: все они реализуют
__getitem__
, На самом деле стандартные последовательности также реализуют__iter__
и ваш тоже должен, потому что специальная обработка__getitem__
существует по причинам обратной совместимости и может исчезнуть в будущем (хотя это не рекомендуется, поскольку я пишу это).
В последнее время я немного изучаю эту проблему. Исходя из этого, я пришел к выводу, что на сегодняшний день это лучший подход:
from collections.abc import Iterable # drop `.abc` with Python 2.7 or lower
def iterable(obj):
return isinstance(obj, Iterable)
Вышеупомянутое было рекомендовано уже ранее, но по общему мнению, использование iter()
было бы лучше:
def iterable(obj):
try:
iter(obj)
except Exception:
return False
else:
return True
Мы использовали iter()
в нашем коде также для этой цели, но в последнее время меня все больше и больше раздражают объекты, у которых есть только __getitem__
считается повторяющимся. Есть веские причины иметь__getitem__
в не повторяемом объекте, и с ними приведенный выше код не работает. В качестве примера из реальной жизни мы можем использовать Faker. Приведенный выше код сообщает, что он повторяется, но на самом деле попытка повторения вызываетAttributeError
(проверено с Faker 4.0.2):
>>> from faker import Faker
>>> fake = Faker()
>>> iter(fake) # No exception, must be iterable
<iterator object at 0x7f1c71db58d0>
>>> list(fake) # Ooops
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/.../site-packages/faker/proxy.py", line 59, in __getitem__
return self._factory_map[locale.replace('-', '_')]
AttributeError: 'int' object has no attribute 'replace'
Если бы мы использовали insinstance()
, мы бы случайно не рассмотрели экземпляры Faker (или любые другие объекты, имеющие только __getitem__
) для повторения:
>>> from collections.abc import Iterable
>>> from faker import Faker
>>> isinstance(Faker(), Iterable)
False
В более ранних ответах говорилось, что использование iter()
безопаснее, поскольку старый способ реализации итерации в Python основывался на __getitem__
и isinstance()
подход не обнаружит этого. Возможно, это было справедливо для старых версий Python, но на основании моего довольно исчерпывающего тестированияisinstance()
отлично работает в настоящее время. Единственный случай, когдаisinstance()
не сработало но iter()
сделал было с UserDict
при использовании Python 2. Если это уместно, можно использовать isinstance(item, (Iterable, UserDict))
чтобы покрыть это.
Начиная с Python 3.5 вы можете использовать модуль ввода из стандартной библиотеки для вещей, связанных с типами:
from typing import Iterable
...
if isinstance(my_item, Iterable):
print(True)
Этого недостаточно: объект, возвращаемый __iter__
должен реализовывать итерационный протокол (т.е. next
метод). Смотрите соответствующий раздел в документации.
В Python хорошей практикой является "попробовать и посмотреть" вместо "проверки".
В Python <= 2.5 вы не можете и не должны - итерируемый интерфейс был "неформальным".
Но начиная с Python 2.6 и 3.0 вы можете использовать новую инфраструктуру ABC (абстрактный базовый класс) вместе с некоторыми встроенными ABC, которые доступны в модуле коллекций:
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)
Теперь, является ли это желательным или действительно работает, это просто вопрос соглашений. Как видите, вы можете зарегистрировать не повторяемый объект как Iterable - и он вызовет исключение во время выполнения. Следовательно, 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 автоматически распознает объект этого класса как Iterable.
Так что, если вы просто ожидаете итерации, итерируйте и забудьте об этом. С другой стороны, если вам нужно делать разные вещи в зависимости от типа ввода, вы можете найти инфраструктуру ABC довольно полезной.
try:
#treat object as iterable
except TypeError, e:
#object is not actually iterable
Не запускайте проверки, чтобы увидеть , действительно ли ваша утка является уткой, чтобы увидеть, является ли она итеративной или нет, относитесь к ней так, как будто она есть, и жалуйтесь, если это не так.
Вы можете попробовать это:
def iterable(a):
try:
(x for x in a)
return True
except TypeError:
return False
Если мы можем создать генератор, который выполняет итерации по нему (но никогда не использовать генератор, чтобы он не занимал место), он будет повторяемым. Похоже, что-то вроде "дух". Почему вы должны определить, является ли переменная итеративной в первую очередь?
Лучшее решение, которое я нашел до сих пор:
hasattr(obj, '__contains__')
который в основном проверяет, реализует ли объект in
оператор.
Преимущества (ни одно из других решений не имеет всех трех):
- это выражение (работает как лямбда, в отличие от try... кроме варианта)
- он (должен быть) реализован всеми итерациями, включая строки (в отличие от
__iter__
) - работает на любом Python >= 2.5
Заметки:
- философия Python "просить прощения, а не разрешения" не работает, когда, например, в списке есть как итерируемые, так и неитерируемые, и вам нужно обрабатывать каждый элемент по-разному в соответствии с его типом (обработка итерируемых элементов при попытке и не Итериблс, за исключением того, что это сработало бы, но это выглядело бы неприглядно и обманчиво
- Решения этой проблемы, которые пытаются фактически выполнить итерацию по объекту (например, [x для x в obj]), чтобы проверить, является ли он итеративным, могут привести к значительным потерям производительности для больших итерируемых элементов (особенно если вам просто нужны первые несколько элементов итерируемого, для пример) и его следует избегать
Я нашел хорошее решение здесь:
isiterable = lambda obj: isinstance(obj, basestring) \
or getattr(obj, '__iter__', False)
Согласно Глоссарию Python 2, итерации
все типы последовательностей (например,
list
,str
, а такжеtuple
) и некоторые непоследовательные типы, такие какdict
а такжеfile
и объекты любых классов, которые вы определяете с__iter__()
или же__getitem__()
метод. Итерации можно использовать в цикле for и во многих других местах, где требуется последовательность (zip(), map(), ...). Когда итеративный объект передается в качестве аргумента встроенной функции iter(), он возвращает итератор для объекта.
Конечно, учитывая общий стиль кодирования для 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
у меня нет __iter__
метод (по крайней мере, не в Python 2, в Python 3 они делают) и потому generator
объекты не имеют __getitem__
метод.
Я часто нахожу удобным в моих сценариях определить iterable
функция.
(Теперь включает в себя предложенное Alfe упрощение):
import collections
def iterable(obj):
return isinstance(obj, collections.Iterable):
так что вы можете проверить, является ли какой-либо объект итеративным в очень читаемой форме
if iterable(obj):
# act on iterable
else:
# not iterable
как вы бы сделали сcallable
функция
РЕДАКТИРОВАТЬ: если у вас установлен NumPy, вы можете просто сделать: из numpy import iterable
что-то вроде
def iterable(obj):
try: iter(obj)
except: return False
return True
Если у вас нет numpy, вы можете просто реализовать этот код или тот, что приведен выше.
Это всегда ускользает от меня, почему 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)
У pandas есть встроенная функция:
from pandas.util.testing import isiterable
Много способов проверить, является ли объект итерируемым
from collections.abc import Iterable
myobject = 'Roster'
if isinstance(myobject , Iterable):
print(f"{myobject } is iterable")
else:
print(f"strong text{myobject } is not iterable")
def is_iterable(x):
try:
0 in x
except TypeError:
return False
else:
return True
Это скажет да всем видам итерируемых объектов, но это скажет нет строкам в Python 2. (Это то, что я хочу, например, когда рекурсивная функция может взять строку или контейнер строк. В этой ситуации, просьба о прощении может привести к obfuscode, и лучше сначала спросить разрешение.)
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() скажет yes строкам типа bytes
а также bytearray
,
bytes
объекты в Python 3 являются итеративнымиTrue == is_iterable(b"string") == is_iterable("string".encode('utf-8'))
В Python 2 такого типа нет.bytearray
объекты в Python 2 и 3 являются повторяемымиTrue == is_iterable(bytearray(b"abc"))
ОП hasattr(x, '__iter__')
подход скажет да для строк в Python 3 и нет в Python 2 (независимо от того, ''
или же b''
или же u''
). Спасибо @LuisMasuelli за то, что заметили, что это также подведет вас к багги __iter__
,
В моем коде я проверял не итерируемые объекты:
hasattr(myobject,'__trunc__')
Это довольно быстро и может использоваться для проверки итераций (используйтеnot
).
Я не уверен на 100%, работает ли это решение для всех объектов, возможно, другие могут дать дополнительную информацию об этом. Метод швов должен быть связан с числовыми типами (все объекты, которые могут быть округлены до целых чисел, нуждаются в этом). Но я не нашел ни одного объекта, содержащего__trunc__
вместе с__iter__
или__getitem__
.
isiterable
func в следующем коде возвращает True
если объект является итеративным. если это не повторяется, возвращает 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
Самый простой способ, учитывая утонченную типизацию 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
,
Не совсем "правильно", но может служить быстрой проверкой большинства распространенных типов, таких как строки, кортежи, числа с плавающей точкой и т. Д.
>>> '__iter__' in dir('sds')
True
>>> '__iter__' in dir(56)
False
>>> '__iter__' in dir([5,6,9,8])
True
>>> '__iter__' in dir({'jh':'ff'})
True
>>> '__iter__' in dir({'jh'})
True
>>> '__iter__' in dir(56.9865)
False
Вместо проверки на __iter__
атрибут, вы можете проверить наличие __len__
атрибут, который реализуется каждым итерируемым встроенным питоном, включая строки.
>>> 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
Не повторяемые объекты не будут реализовывать это по очевидным причинам. Однако он не перехватывает пользовательские итерации, которые его не реализуют, и выражения генератора, которые iter
может иметь дело с. Тем не менее, это может быть сделано в строке, и добавив простой or
Проверка выражений для генераторов исправит эту проблему. (Обратите внимание, что написание type(my_generator_expression) == generator
бросил бы NameError
, Вместо этого обратитесь к этому ответу.)
Вы можете использовать GeneratorType из типов:
>>> import types >>> types.GeneratorType <class 'generator'> >>> gen = (i for i in range(10)) >>> isinstance(gen, types.GeneratorType) True
--- принял ответ утдемир
(Это делает его полезным для проверки, если вы можете позвонить len
на объекте, хотя.)
Довольно поздно на вечеринку, но я задал себе этот вопрос и увидел это, а затем подумал над ответом. Я не знаю, публиковал ли кто-нибудь это уже. Но, по сути, я заметил, что все повторяющиеся типы имеют в своем dict __getitem__(). Вот как вы можете проверить, является ли объект повторяющимся, даже не пытаясь. (Каламбур предназначен)
def is_attr(arg):
return '__getitem__' in dir(arg)
Может просто напишиhasattr(obj, "__iter__")
Или... что-то вроде этого может сработать:
def is_iterable(obj: object) -> bool:
return hasattr(obj, "__iter__")
Помимо регулярных попыток и кроме, вы можете запустить помощь.
temp= [1,2,3,4]
help(temp)
help предоставит все методы, которые могут быть запущены для этого объекта (это может быть любой объект, а может и не быть списком в соответствии с примером), что в данном случае является temp.
Примечание: это будет то, что вы будете делать вручную.