В Python, как мне определить, является ли объект итеративным? - PullRequest
929 голосов
/ 23 декабря 2009

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

hasattr(myObj, '__iter__')

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

Ответы [ 20 ]

10 голосов
/ 19 мая 2012

Согласно Глоссарий 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 s не имеют метода __iter__ (по крайней мере, не в Python 2, в Python 3 они есть) и потому что generator объекты не имеют метода __getitem__ .

6 голосов
/ 24 марта 2013

Я часто нахожу в своих скриптах удобным определение функции 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, вы можете просто реализовать этот код или тот, что приведен выше.

5 голосов
/ 12 ноября 2016

имеет такую ​​встроенную функцию:

from pandas.util.testing import isiterable
3 голосов
/ 22 марта 2016
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"))

Подход O.P. hasattr(x, '__iter__') скажет да для строк в Python 3 и нет в Python 2 (независимо от того, '' или b'' или u''). Спасибо @LuisMasuelli за то, что заметил, что он также подведет вас к багги __iter__.

2 голосов
/ 24 ноября 2015

Самый простой способ, с учетом утки 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.

2 голосов
/ 14 августа 2018

Мне всегда было трудно понять, почему в 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)

1 голос
/ 15 сентября 2017

Функция isiterable в следующем коде возвращает 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
1 голос
/ 14 августа 2018

Вместо проверки атрибута __iter__ вы можете проверить наличие атрибута __len__, который реализуется всеми итерациями, встроенными в python, включая строки.

>>> 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 для объекта.)

0 голосов
/ 25 января 2019

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

>>> '__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
0 голосов
/ 30 ноября 2017

Помимо обычной попытки и кроме, вы можете запустить справку.

temp= [1,2,3,4]
help(temp)

help выдаст все методы, которые могут быть запущены для этого объекта (это может быть любой объект, а не список в соответствии с примером), что в данном случае является temp.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...