В Python, как мне определить, является ли объект итеративным?

Есть ли такой метод, как isiterable? Единственное решение, которое я нашел, это позвонить

hasattr(myObj, '__iter__')

Но я не уверен, насколько это глупо.

25 ответов

Решение
  1. Проверка на __iter__ работает с типами последовательностей, но не работает, например, со строками в Python 2. Я также хотел бы знать правильный ответ, до тех пор, здесь есть одна возможность (которая также будет работать со строками):

    try:
        some_object_iterator = iter(some_object)
    except TypeError as te:
        print some_object, 'is not iterable'
    

    iter встроенные проверки для __iter__ метод или в случае строк __getitem__ метод.

  2. Другой общий питонический подход - предполагать итеративность, а затем изящно проваливаться, если она не работает с данным объектом. Глоссарий Python:

    Стиль программирования Pythonic, который определяет тип объекта путем проверки его метода или сигнатуры атрибута, а не путем явного отношения к какому-либо объекту типа ("Если он выглядит как утка и крякает как утка, он должен быть уткой".) Подчеркивая интерфейсы В отличие от конкретных типов, хорошо разработанный код повышает его гибкость, позволяя полиморфную замену. Утиная типография избегает тестов с использованием type() или isinstance(). Вместо этого он обычно использует стиль программирования EAFP (Проще просить прощения, чем разрешения).

    ...

    try:
       _ = (e for e in my_object)
    except TypeError:
       print my_object, 'is not iterable'
    
  3. 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, а затем обсуждение, чтобы проиллюстрировать факты.

факты

  1. Вы можете получить итератор из любого объекта o позвонив iter(o) если выполняется хотя бы одно из следующих условий:

    а) o имеет __iter__ метод, который возвращает объект итератора. Итератор - это любой объект с __iter__ и __next__ (Python 2: next) метод.

    б) o имеет __getitem__ метод.

  2. Проверка на экземпляр Iterable или же Sequence или проверка атрибута __iter__ недостаточно.

  3. Если объект o реализует только __getitem__, но нет __iter__, iter(o) создаст итератор, который пытается извлечь элементы из o по целому индексу, начиная с индекса 0. Итератор будет ловить любой IndexError (но без других ошибок), который поднимается, а затем поднимается StopIteration сам.

  4. В самом общем смысле, нет способа проверить, возвращен ли итератор iter в здравом уме, кроме как попробовать это.

  5. Если объект o инвентарь __iter__, iter Функция будет гарантировать, что объект, возвращаемый __iter__ это итератор. Нет проверки работоспособности, если объект только реализует __getitem__,

  6. __iter__ выигрывает. Если объект o реализует оба __iter__ а также __getitem__, iter(o) позвоню __iter__,

  7. Если вы хотите сделать свои собственные объекты повторяемыми, всегда реализуйте __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__ являются следующими:

  1. Если вы реализуете __iter__ экземпляры будут считаться повторяемыми, и isinstance(o, collections.Iterable) вернусь True,
  2. Если объект возвращен __iter__ не итератор, iter немедленно потерпит неудачу и поднимет TypeError,
  3. Специальная обработка __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())

Примечания:

  1. Не имеет значения, является ли объект не итеративным или глючным __iter__ был реализован, если тип исключения тот же: в любом случае вы не сможете выполнить итерацию объекта.
  2. Я думаю, что понимаю вашу озабоченность: как 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.

Примечание: это будет то, что вы будете делать вручную.

Другие вопросы по тегам