나는 조금의 상호 작용에 더 많은 빛 비트 창고 싶습니다 iter
, __iter__
그리고 __getitem__
어떤 커튼 뒤에 발생합니다. 그 지식으로 무장하면 왜 최선을 다할 수 있는지 이해할 수 있습니다.
try:
iter(maybe_iterable)
print('iteration will probably work')
except TypeError:
print('not iterable')
먼저 사실을 나열한 다음 for
파이썬에서 루프 를 사용할 때 발생하는 상황에 대한 간단한 알림과 함께 사실을 설명하기위한 토론을 진행합니다.
사리
당신은 어떤 개체에서 반복자를 얻을 수 있습니다 o
호출하여 iter(o)
다음 조건 중 하나 이상이 사실이 보유하고있는 경우
)를 o
이 __iter__
반복자 객체를 반환하는 방법을. 이터레이터는 __iter__
및 __next__
(Python 2 :) next
메서드 가있는 객체입니다 .
b) 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
파이썬 에서 루프 를 사용할 때 어떤 일이 발생하는지 이해해야 합니다. 이미 알고 있다면 다음 섹션으로 바로 넘어가십시오.
for item in o
반복 가능한 객체에 사용할 때 o
Python iter(o)
은 반복자 객체를 반환 값으로 호출 하고 기대합니다. 반복자는 __next__
(또는 next
Python 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__
Iterable
Sequence
>>> 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__
그동안, 방법을 Iterable
ABC는하지 않습니다.
포인트 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
. 이상 반복하는 이유입니다 BasicIterable
A를 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__
(또는 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.abc.Iterable)
반환 True
합니다.
- 에 의해 반환 된 객체
__iter__
가 반복자가 아닌 경우 iter
즉시 실패 하고을 발생 TypeError
시킵니다.
__getitem__
이전 버전과의 호환성을 위해 특별한 처리가 있습니다. Fluent Python에서 다시 인용 :
그렇기 때문에 모든 파이썬 시퀀스가 반복 가능한 이유는 모두 구현 __getitem__
합니다. 실제로 표준 시퀀스는 또한 구현 __iter__
하며, __getitem__
이전 버전과의 호환성을 위해 특수 처리가 존재하며 향후에는 사라질 수 있기 때문에 (당신 이 이것을 쓸 때 사용되지는 않지만) 표준 시퀀스도 구현 해야합니다 .
__getitem__
객체를 반복 가능하게 만들기에 충분합니다