파이썬에서 객체가 반복 가능한지 어떻게 알 수 있습니까?


1083

같은 방법이 isiterable있습니까? 내가 지금까지 찾은 유일한 해결책은 전화하는 것입니다

hasattr(myObj, '__iter__')

그러나 이것이 얼마나 바보인지 확실하지 않습니다.


18
__getitem__객체를 반복 가능하게 만들기에 충분합니다
Kos

4
FWIW : iter(myObj)if 경우 성공 isinstance(myObj, dict)하므로 s 또는 single myObj시퀀스 일 수 있는 을 보고 있으면 두 경우 모두 성공할 수 있습니다. 시퀀스가 무엇인지 아닌지 알고 싶다면 중요한 미묘함. (Python 2)dictdict
Ben Mosher

7
__getitem__인덱스가 0에서 시작하면 객체를 반복 가능하게 만드는 데 충분합니다 .
Carlos A. Gómez

답변:


26

나는이 문제를 꽤 최근에 연구 해왔다. 내 결론은 요즘에 이것이 최선의 접근법이라는 것입니다.

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()에 근거했다 파이썬에서 반복을 구현하는 기존의 방법으로 안전 __getitem__하고, isinstance()접근 방식은 감지하지 않을 것입니다. 이것은 오래된 파이썬 버전에서는 사실 일 수 있지만 isinstance()요즘 철저한 테스트를 기반으로 합니다. isinstance()작동 iter()하지 않았지만 UserDictPython 2 를 사용할 때 와 관련된 유일한 경우입니다. 관련이있는 경우 사용할 isinstance(item, (Iterable, UserDict))수 있습니다.


1
또한 typing.Dict반복 가능한 것으로 간주 iter(Dict)되지만 list(Dict)오류가 발생하여 실패합니다 TypeError: Parameters to generic types must be types. Got 0.. 예상대로 isinstance(Dict, Iterable)false를 반환합니다.
Pekka Klärck

1
나는 같은 결론에 도달했지만 다른 이유가 있습니다. 를 사용하면 iter"사전 캐싱"을 사용하는 일부 코드에서 불필요하게 속도가 느려졌습니다. 경우 __iter__코드가 느린, 그래서 전화 것입니다 iter... 당신이 뭔가가 반복 가능한 있는지 확인하기 위해 원하는 시간.
thorwhalen

842
  1. 검사는 __iter__시퀀스 유형에서 작동하지만 Python 2의 문자열 에서는 실패합니다 . 그때까지 올바른 대답을 알고 싶습니다. 여기에는 문자열에서 작동하는 한 가지 가능성이 있습니다.

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

    iter위한 내장 검사 __iter__방법 또는 스트링 인 경우 __getitem__방법.

  2. 또 다른 일반적인 pythonic 접근법은 iterable을 가정 한 다음 주어진 객체에서 작동하지 않으면 정상적으로 실패하는 것입니다. 파이썬 용어 :

    그 방법 또는 속성 서명 검사를 통해서가 아니라 어떤 종류의 객체 ( "그것이 보이는 경우에 명시 적 관계에 의해 객체의 유형을 결정하는 파이썬 프로그래밍 스타일 오리 와 같은 꽥꽥 오리 , 그것은해야합니다 오리 강조 인터페이스에 의해.") 잘 설계된 코드는 특정 유형이 아니라 다형성 대체를 허용하여 유연성을 향상시킵니다. 덕 타이핑은 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__.


34
[e for e in my_object]다른 이유로 예외를 제기 할 수 있습니다. 즉, 구현에 my_object정의되지 않았거나 가능한 버그입니다 my_object.
Nick Dandoulakis 2009

37
문자열 시퀀스 ( isinstance('', Sequence) == True) 이며 시퀀스 반복 가능합니다 ( isinstance('', Iterable)). 그럼에도 불구 hasattr('', '__iter__') == False하고 혼란 스러울 수 있습니다.
jfs

82
my_object매우 큰 경우 (예 : 무한대 itertools.count()) 목록 이해에는 많은 시간 / 메모리가 소요됩니다. (잠재적으로 무한한) 목록을 만들려고 시도하지 않는 생성기를 만드는 것이 좋습니다.
Chris Lutz

14
some_object 가 다른 이유 (버그 등)로 인해 TypeError를 던지면 어떻게 됩니까? "반복 불가능한 TypeError"에서 어떻게 알 수 있습니까?
Shaung

54
파이썬 3에서 hasattr(u"hello", '__iter__')리턴 :True
Carlos

572

오리 타자

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)입니다.


18
Luciano Ramalho의 "Fluent Python"에서 : Python 3.4부터 객체 x가 반복 가능한지 확인하는 가장 정확한 방법은 iter (x)를 호출하고 그렇지 않은 경우 TypeError 예외를 처리하는 것입니다. iter (x)는 레거시 getitem 메소드를 고려 하지만 Iterable ABC는 그렇지 않으므로 isinstance (x, abc.Iterable)를 사용하는 것보다 더 정확 합니다.
RdB

" 아직 isinstance(x, (collections.Iterable, collections.Sequence))대신 내가 할거야"라고 생각하는 경우 iter(x), 구현 만 구현 __getitem__하지만 반복하지 않는 반복 가능한 객체는 감지 하지 못합니다 __len__. iter(x)예외를 사용 하고 포착하십시오.
Dale

두 번째 답변이 효과가 없습니다. PyUNO에서 내가하면 iter(slide1)모든 것이 잘 진행되지만 isinstance(slide1, Iterable)던져 TypeError: issubclass() arg 1 must be a class집니다.
Hi-Angel

@ Hi-Angel은 버그 대신 PyUNO오류 메시지 issubclass()가 나타납니다 isinstance().
Georg Schölly 5

2
객체에 대해 iter ()를 호출하는 것은 비용이 많이 드는 작업 일 수 있습니다 (iter ()에서 여러 프로세스를 포크 / 스폰하는 Pytorch의 DataLoader 참조).
szali

126

나는 조금의 상호 작용에 더 많은 빛 비트 창고 싶습니다 iter, __iter__그리고 __getitem__어떤 커튼 뒤에 발생합니다. 그 지식으로 무장하면 왜 최선을 다할 수 있는지 이해할 수 있습니다.

try:
    iter(maybe_iterable)
    print('iteration will probably work')
except TypeError:
    print('not iterable')

먼저 사실을 나열한 다음 for파이썬에서 루프 를 사용할 때 발생하는 상황에 대한 간단한 알림과 함께 사실을 설명하기위한 토론을 진행합니다.

사리

  1. 당신은 어떤 개체에서 반복자를 얻을 수 있습니다 o호출하여 iter(o)다음 조건 중 하나 이상이 사실이 보유하고있는 경우

    )를 o__iter__반복자 객체를 반환하는 방법을. 이터레이터는 __iter____next__(Python 2 :) next메서드 가있는 객체입니다 .

    b) 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파이썬 에서 루프 를 사용할 때 어떤 일이 발생하는지 이해해야 합니다. 이미 알고 있다면 다음 섹션으로 바로 넘어가십시오.

for item in o반복 가능한 객체에 사용할 때 oPython iter(o)은 반복자 객체를 반환 값으로 호출 하고 기대합니다. 반복자는 __next__(또는 nextPython 2에서) 메소드와 메소드 를 구현하는 모든 객체입니다 __iter__.

관례 적으로, __iter__반복자 의 메소드는 객체 자체를 리턴해야합니다 (예 :) return self. 그런 다음 파이썬 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__IterableSequence

>>> from collections import Iterable, Sequence
>>> hasattr(b, '__iter__')
False
>>> isinstance(b, Iterable)
False
>>> isinstance(b, Sequence)
False

이것이 바로 Luciano Ramalho의 Fluent Python 이 객체의 반복 가능 여부를 확인하는 가장 정확한 방법으로 iter잠재력 TypeError을 호출 하고 처리 할 것을 권장하는 이유 입니다. 책에서 직접 인용 :

Python 3.4부터 객체 x가 반복 가능한지 여부를 확인하는 가장 정확한 방법 iter(x)TypeError예외가 아닌 경우 예외 를 호출 하고 처리하는 것입니다. 이것은 사용하는 것보다 더 정확 isinstance(x, abc.Iterable)하기 때문에, iter(x)또한 기존의 생각 __getitem__그동안, 방법을 IterableABC는하지 않습니다.

포인트 3 :을 제공 __getitem__하지만 제공 하지 않는 객체에 대한 반복__iter__

BasicIterable예상대로 작업 인스턴스를 반복 : Python은 0에서 시작하여 인덱스 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. 이상 반복하는 이유입니다 BasicIterableA를 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: '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__(또는 nextPython 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.abc.Iterable)반환 True합니다.
  2. 에 의해 반환 된 객체 __iter__가 반복자가 아닌 경우 iter즉시 실패 하고을 발생 TypeError시킵니다.
  3. __getitem__이전 버전과의 호환성을 위해 특별한 처리가 있습니다. Fluent Python에서 다시 인용 :

그렇기 때문에 모든 파이썬 시퀀스가 ​​반복 가능한 이유는 모두 구현 __getitem__합니다. 실제로 표준 시퀀스는 또한 구현 __iter__하며, __getitem__이전 버전과의 호환성을 위해 특수 처리가 존재하며 향후에는 사라질 수 있기 때문에 (당신 이 이것을 쓸 때 사용되지는 않지만) 표준 시퀀스도 구현 해야합니다 .


그래서 술어를 정의하는 것이 안전 is_iterable반환하여 True에서 try블록과 Falseexcept TypeError블록?
alancalvitti 19

이것은 좋은 대답입니다. 나는 그것이 getitem 프로토콜의 직관적이지 않고 불행한 특성을 강조한다고 생각한다. 추가 된 적이 없습니다.
Neil G

31

이것으로 충분하지 않습니다 :에 의해 반환 된 객체 __iter__는 반복 프로토콜 (즉, next메소드)을 구현해야합니다 . 설명서 의 관련 섹션을 참조하십시오 .

파이썬에서 좋은 방법은 "확인"대신 "시도하고 확인"하는 것입니다.


9
"오리는 타자"내가 믿는가? :)
willem

9
@willem : 또는 "허가를 요구하지 않고 용서를 구하십시오";-)
jldupont

14
@willem "권한"과 "용서"스타일은 모두 오리 타이핑으로 인정됩니다. 당신은 객체가 할 수 있는지 묻는다면 오히려 것보다 입니다 ,의 오리 입력있다. 당신이 내성을 사용한다면, 그것은 "권한"입니다; 당신이 그것을 시도하고 그것이 작동하는지 여부를 확인한다면, 그것은 "용서"입니다.
Mark Reed

22

파이썬 <= 2.5에서, 당신은 할 수없고해서는 안됩니다-iterable은 "비공식적 인"인터페이스였습니다.

그러나 Python 2.6 및 3.0부터는 collections 모듈에서 사용 가능한 일부 내장 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는 "새로운"의미를 얻습니다. "선언 된"형식 호환성 만 확인하면됩니다. 이는 파이썬으로가는 좋은 방법입니다.

반면에 객체가 필요한 인터페이스를 만족시키지 못하면 어떻게해야합니까? 다음 예제를 보자.

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__메소드가 이용 가능하다면 파이썬은 자동적으로 그 클래스의 객체를 Iterable로 인식합니다.

따라서 반복 가능한 것을 기대하면 반복하고 잊어 버립니다. 반면에 입력 유형에 따라 다른 작업을 수행해야하는 경우 ABC 인프라가 매우 유용 할 수 있습니다.


13
except:초보자를 위해 예제 코드에서 베어 를 사용하지 마십시오 . 나쁜 연습을 장려합니다.
jfs

JFS : 그렇지는 않지만 여러 예외 제기 코드를 거쳐야하고 특정 예외를 포착하고 싶지 않았습니다 ...이 코드의 목적은 분명하다고 생각합니다.
Alan Franzoni

21
try:
  #treat object as iterable
except TypeError, e:
  #object is not actually iterable

오리가 실제로 오리인지 확인하기 위해 수표를 실행하지 말고 오리 처럼 처리하고 그렇지 않은 경우 불평하십시오.


3
기술적으로, 반복하는 동안 계산에서 a TypeError를 던질 수 있지만 기본적으로 그렇습니다.
Chris Lutz

6
@willem : 벤치 마크를 수행하려면 timeit을 사용하십시오. 파이썬 예외는 종종 if 문보다 빠릅니다. 통역사를 통해 약간 더 짧은 경로를 이용할 수 있습니다.
S.Lott

2
@willem : IronPython은 CPython과 비교하여 예외가 느립니다.
jfs

2
작업 시도 : 진술은 정말 빠릅니다. 따라서 예외가 거의 없다면 try-except가 빠릅니다. 많은 예외가 예상되면 "if"가 더 빠를 수 있습니다.
Arne Babenhauserheide

2
" as e" TypeError를 추가하는 대신 " " 를 추가하여 예외 개체를 포착해서는 안 , e됩니까?
HelloGoodbye

21

Python 3.5 부터는 표준 라이브러리에서 입력 모듈을 사용하여 유형 관련 항목을 사용할 수 있습니다 .

from typing import Iterable

...

if isinstance(my_item, Iterable):
    print(True)

18

내가 지금까지 찾은 최고의 솔루션 :

hasattr(obj, '__contains__')

기본적으로 객체가 in연산자를 구현하는지 확인합니다 .

장점 (다른 솔루션은 세 가지 모두가 없습니다) :

  • 그것은 표현입니다 ( try ... except 변형 과는 대조적으로 lambda 로 작동합니다 )
  • 문자열을 포함하여 모든 iterable에 의해 구현되어야합니다 (와 대조적으로 __iter__)
  • 파이썬> = 2.5에서 작동

노트:

  • "허용을 구하지 말고 용서를 구하라"라는 파이썬 철학은 예를 들어 반복 가능한 것과 반복 할 수없는 것을 모두 가지고 있고 각 유형에 따라 각 요소를 다르게 취급해야 할 때 잘 작동하지 않습니다. 제외하고 iterables 작동하지만 엉덩이가 추악하고 오해의 소지가 있습니다)
  • 반복 가능한지 확인하기 위해 실제로 객체를 반복하려고 시도하는이 문제에 대한 해결책 (예 : [x for obj])은 반복 가능한 큰 iterables에 대해 상당한 성능 페널티를 유발할 수 있습니다 (특히 iterable의 처음 몇 요소 만 필요한 경우). 예) 피해야합니다

3
멋지지만 stackoverflow.com/questions/1952464/…에 제안 된대로 collections 모듈을 사용하지 않는 이유는 무엇입니까? 좀 더 표현력이있는 것 같습니다.
Dave Abrahams

1
명확성을 잃지 않으면 서 더 짧고 (추가 가져 오기가 필요하지 않음) "contains"메소드를 갖는 것은 무언가가 객체의 모음인지 확인하는 자연스러운 방법 인 것 같습니다.
Vlad

46
무언가가 무언가를 포함 할 수 있다고해서 반드시 반복 가능한 것은 아닙니다. 예를 들어, 사용자가 포인트가 3D 큐브에 있는지 확인할 수 있지만이 객체를 어떻게 반복 할 수 있습니까?
Casey Kuball

13
이것은 올바르지 않습니다. iterable 자체는 적어도 Python 3.4에서 contains를 지원하지 않습니다 .
Peter Shinners 2016 년

15

당신은 이것을 시도 할 수 있습니다 :

def iterable(a):
    try:
        (x for x in a)
        return True
    except TypeError:
        return False

생성기를 반복하는 생성기를 만들 수 있지만 (공간을 차지하지 않도록 생성기를 사용하지 않는 경우) 반복 가능합니다. "duh"종류의 것 같습니다. 변수가 처음에 반복 가능한지 여부를 결정해야하는 이유는 무엇입니까?


무엇에 대해 iterable(itertools.repeat(0))? :)
badp

5
@ badp, (x for x in a)그냥 생성기를 생성합니다 .a에서 반복을 수행하지 않습니다.
catchmeifyoutry

5
시도하는 (x for x in a)것과 정확히 같은 시도 iterator = iter(a)입니까? 아니면 둘이 다른 경우가 있습니까?
최대

for _ in a: break더 간단 하지 않습니까? 느려요?
Mr_and_Mrs_D

2
@Mr_and_Mrs_D 테스트 된 객체가 나중에 반복되는 반복자 인 경우 나쁘다 (위치를 재설정 할 수 없기 때문에 1 항목이 짧음) 가비지 생성기를 만들면 반복되지 않는 객체를 반복하지 않습니다 반복 할 수없는 경우 TypeError가 100 % 발생할 것이라고 확신하지는 않습니다.
Tcll

13

나는 여기 에서 좋은 해결책을 찾았다 .

isiterable = lambda obj: isinstance(obj, basestring) \
    or getattr(obj, '__iter__', False)

10

Python 2 용어 에 따르면 반복 가능한 것은

모든 시퀀스 유형 (예 list, strtuple)과 같은 일부 비 시퀀스 유형 dictfile당신이 함께 정의하는 모든 클래스의 객체 __iter__()또는 __getitem__()방법. 반복문은 for 루프와 시퀀스가 ​​필요한 다른 많은 장소 (zip (), map (), ...)에서 사용할 수 있습니다. 반복 가능한 객체가 내장 함수 iter ()에 인수로 전달되면 객체의 반복자를 반환합니다.

물론, "권한보다 용서를 구하는 것이 더 쉽다"는 사실에 근거한 파이썬의 일반적인 코딩 스타일을 고려할 때 일반적인 기대는

try:
    for i in object_in_question:
        do_something
except TypeError:
    do_something_for_non_iterable

그러나 명시 적으로 확인 해야하는 경우에 의해 iterable을 테스트 할 수 있습니다 hasattr(object_in_question, "__iter__") or hasattr(object_in_question, "__getitem__"). 메소드 str가 없거나 __iter__(파이썬 3에서는 파이썬 3에서는 그렇지 않음) generator객체 에는 메소드 가 없기 때문에 두 가지를 모두 확인해야합니다 __getitem__.


8

스크립트 내에서 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간단하게 다음과 같이 할 수 있습니다 : from

def iterable(obj):
    try: iter(obj)
    except: return False
    return True

numpy가없는 경우이 코드 또는 위의 코드를 간단히 구현할 수 있습니다.


3
부울 if x: return True else: return False과 같은 sth를 할 때마다 x이것을로 쓸 수 있습니다 return x. 귀하의 경우에는 return isinstance(…)어떤없이 if.
Alfe

Alfe의 솔루션이 더 낫다는 것을 인정하기 때문에 왜 그렇게 대답하기 위해 답을 편집하지 않았습니까? 대신, 이제 답변에 BOTH 버전이 있습니다. 불필요한 세부 사항. 이 문제를 해결하기 위해 수정 사항을 제출합니다.
ToolmakerSteve

2
`except : return False`줄에서 "TypeError"를 잡아야합니다. 모든 것을 잡는 것은 나쁜 패턴입니다.
Mariusz Jamro

알고. 일반 예외를 사용하는 NumPy 라이브러리에서 해당 코드를 번역했습니다.
fmonegaglia

NumPy에서 코드를 가져 왔다고해서 좋은 패턴을 의미하지는 않습니다. 프로그램 내부에서 명시 적으로 오류 처리를하는 경우에만 모든 것을 잡아야합니다.
Tcll

5

다음과 같은 내장 함수가 있습니다.

from pandas.util.testing import isiterable

그러나 이것은 단지 __iter__서열과 유사한 것에 관심 이 있는지 아닌지에 대한 것입니다 .
ead

4

그것은 파이썬이가 왜 나를 벗어났습니다 항상있어 callable(obj) -> bool하지만 iterable(obj) -> bool...
확실히 그것은 할 쉽게 hasattr(obj,'__call__')가 느린 경우에도 마찬가지입니다.

예외에 대한 테스트는 일반적으로 모든 언어에서 나쁜 습관으로 간주되는 try/을 사용하는 것이 가장 좋습니다 .except TypeErroriterable(obj) -> bool

파이썬 2를 위해, 나는 여분의 성능 향상을 위해 람다를 사용할 것입니다 ...
(파이썬 3에서는 함수를 정의하는 데 사용하는 것이 중요하지 않지만 def속도는 거의 같습니다 lambda)

iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__')

이 함수 __iter__는 테스트하지 않기 때문에 객체에 대해 더 빠르게 실행 됩니다 __getitem__.

대부분의 반복 가능한 객체는 객체가 반복 가능해야하는 __iter__특수한 경우 에 의존하는 곳에 의존해야합니다 __getitem__.
(그리고 이것이 표준이므로 C 객체에도 영향을 미칩니다)


그는 파이썬 코드에 대해 이야기 할뿐만 아니라 작동 코드를 제공하지 않습니다 ...이 답변은 실제로 여기에서 여러 번 수행 한 것처럼 편의를위한 것이 었습니다.
Tcll

3
def is_iterable(x):
    try:
        0 in x
    except TypeError:
        return False
    else:
        return True

이것은 모든 반복 가능한 객체에 대해 yes 라고 말하지만 Python 2에서는 문자열에 no라고 말할 것입니다 . (예를 들어 재귀 함수가 문자열이나 문자열 컨테이너를 취할 수있는 경우에 원하는 것입니다. 그런 경우 용서 를 구하는 것이 난독 화를 유발할 수 있으므로 먼저 허가를 요청하는 것이 좋습니다.)

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및 유형의 문자열에 대해 yes라고 말합니다 bytearray.

  • bytesPython 3의 객체는 반복 가능 True == is_iterable(b"string") == is_iterable("string".encode('utf-8'))합니다. Python 2에는 이러한 유형이 없습니다.
  • bytearray 파이썬 2와 3의 객체는 반복 가능합니다 True == is_iterable(bytearray(b"abc"))

OP hasattr(x, '__iter__')접근 방식은 Python 3의 문자열에 대해 yes라고 말하고 Python 2의 경우 ''또는 b''그렇지 않든 상관 없습니다 u''. 주의를 기울인 @LuisMasuelli에게 감사의 말을 전합니다 __iter__.


2

Python의 duck typing 과 관련 하여 가장 쉬운 방법 은 오류를 포착하는 것입니다 (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. 나는 당신의 관심사를 이해한다고 생각합니다 : 객체에 대해 if 가 정의되어 있지 않은 경우 callable오리 타이핑에 의존하여 AttributeErrorif __call__를 정의 할 수 있다면 어떻게 검사로 존재합니까? 반복 가능한 검사의 경우는 아닙니다.

    나는 대답을 모른다. 그러나 당신은 내가 (그리고 다른 사용자들)이 준 함수를 구현하거나 코드에서 예외를 잡을 수있다 (그 부분에서의 구현은 내가 작성한 함수와 같을 것이다. 나머지 코드에서 반복자를 작성하여 예외를 캡처하고 다른 예외와 구별 할 수 있습니다 TypeError.


1

객체가 반복 가능한 경우 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

2
많은 upvotes와 위의 너무 많은 자세한 답변과 설명 할 수없는 답변을 던져 ... meh
Nrzonline

베어 코드를 게시하지 마십시오. 이 작업에 대한 설명도 포함하십시오.
Jonathan Mee

1

__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

--- utdemir의 답변

(이것은 len객체를 호출 할 수 있는지 확인하는 데 유용합니다 .)


불행히도 모든 반복 가능한 객체가 사용하는 것은 아닙니다 __len__...이 경우 일반적으로 두 객체 사이의 거리 계산이 부적절합니다. obj.dist()쉽게 대체 될 수있는 곳 .
Tcll

네. 대부분의 사용자 정의 iterable은 iter와 getitem을 구현하지만 len은 구현하지 않습니다. 그러나 내장 유형이 가능하며 len 함수를 호출 할 수 있는지 확인하려면 len 검사가 더 안전합니다. 하지만 네 말이 맞아
DarthCadeus

0

실제로 "정확한" 것은 아니지만 문자열, 튜플, 플로트 등과 같은 가장 일반적인 유형을 빠르게 확인할 수 있습니다.

>>> '__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

파티에 늦었지만 나는이 질문을하고 이것이 답을 생각하는 것을 보았습니다. 누군가가 이미 이것을 게시했는지 모르겠습니다. 그러나 본질적으로 모든 반복 가능한 유형에는 dict에 __getitem __ () 이 있음을 알았 습니다. 이것은 시도조차하지 않고 객체가 반복 가능한지 확인하는 방법입니다. (펀칭)

def is_attr(arg):
    return '__getitem__' in dir(arg)

불행히도, 이것은 신뢰할 수 없습니다.
timgeb

1
세트 객체는 또 다른 반례입니다.
Raymond Hettinger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.