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

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

hasattr(myObj, '__iter__')

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



답변

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

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. 검사는 __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__.


답변

오리 타자

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파이썬에서 루프 를 사용할 때 발생하는 상황에 대한 간단한 알림과 함께 사실을 설명하기위한 토론을 진행합니다.

사리

  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__이전 버전과의 호환성을 위해 특수 처리가 존재하며 향후에는 사라질 수 있기 때문에 (당신 이 이것을 쓸 때 사용되지는 않지만) 표준 시퀀스도 구현 해야합니다 .


답변

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

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


답변

파이썬 <= 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 인프라가 매우 유용 할 수 있습니다.


답변

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

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