답변:
반복자는 파이썬의 객체는 기본적으로 그들은 두 가지 방법을 제공하는 것을 의미 반복자 프로토콜을 준수 : __iter__()
와 __next__()
.
이 __iter__
반복자 객체를 반환하며 루프 시작시 암시 적으로 호출됩니다.
이 __next__()
메소드는 다음 값을 리턴하며 각 루프 증분마다 내재적으로 호출됩니다. 이 메소드는 리턴 할 값이 더 이상 없을 때 StopIteration 예외를 발생시킵니다. 이는 반복을 중지하기 위해 루프 구문을 통해 암시 적으로 캡처됩니다.
다음은 간단한 카운터 예입니다.
class Counter:
def __init__(self, low, high):
self.current = low - 1
self.high = high
def __iter__(self):
return self
def __next__(self): # Python 2: def next(self)
self.current += 1
if self.current < self.high:
return self.current
raise StopIteration
for c in Counter(3, 9):
print(c)
인쇄됩니다 :
3
4
5
6
7
8
이전 답변에서 다룬 것처럼 생성기를 사용하여 작성하는 것이 더 쉽습니다.
def counter(low, high):
current = low
while current < high:
yield current
current += 1
for c in counter(3, 9):
print(c)
인쇄 된 출력물이 동일합니다. 후드에서 생성기 객체는 반복기 프로토콜을 지원하고 클래스 클래스와 거의 비슷한 작업을 수행합니다.
David Mertz의 기사 인 Iterators와 Simple Generators 는 꽤 좋은 소개입니다.
__next__
. counter
반복자이지만 시퀀스가 아닙니다. 값을 저장하지 않습니다. 예를 들어 이중 중첩 for 루프에서 카운터를 사용해서는 안됩니다.
__iter__
에 추가 해야합니다 __init__
. 그렇지 않으면 객체는 한 번만 반복 될 수 있습니다. 예를 들어,라고 ctr = Counters(3, 8)
하면 for c in ctr
두 번 이상 사용할 수 없습니다 .
Counter
반복자이며 반복자는 한 번만 반복해야합니다. self.current
에서 재설정 __iter__
하면 중첩 루프 Counter
가 완전히 끊어지고 반복자의 모든 가정 된 동작 (이를 호출 iter
하는 것이 dem 등원)에 위배됩니다. 반복을 ctr
두 번 이상 반복 하려면 반복자가 아닌 반복 가능해야하며 __iter__
호출 할 때마다 새로운 반복자를 반환합니다 . 믹스 앤 매치 ( __iter__
호출 시 암시 적으로 재설정되는 반복자)를 시도 하면 프로토콜을 위반합니다.
Counter
반복자가 아닌 반복자라면 __next__
/ 의 정의를 제거 하고이 대답의 끝에 설명 된 생성기와 동일한 형식의 생성기 함수로 next
재정 __iter__
의 할 수 있습니다 (범위 대신 제외) 인수에서 오는하기 __iter__
에, 그들은 인수 것 __init__
에 저장 self
에서와 액세스 self
에서 __iter__
).
반복 함수를 작성하는 네 가지 방법이 있습니다.
__iter__
및__next__
(또는 next
Python 2.x))__getitem__
)예 :
# generator
def uc_gen(text):
for char in text.upper():
yield char
# generator expression
def uc_genexp(text):
return (char for char in text.upper())
# iterator protocol
class uc_iter():
def __init__(self, text):
self.text = text.upper()
self.index = 0
def __iter__(self):
return self
def __next__(self):
try:
result = self.text[self.index]
except IndexError:
raise StopIteration
self.index += 1
return result
# getitem method
class uc_getitem():
def __init__(self, text):
self.text = text.upper()
def __getitem__(self, index):
return self.text[index]
네 가지 방법을 모두 보려면 :
for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem:
for ch in iterator('abcde'):
print(ch, end=' ')
print()
결과 :
A B C D E
A B C D E
A B C D E
A B C D E
참고 :
두 발전기 유형 ( uc_gen
및 uc_genexp
)은 reversed()
; 평범한 반복자 ( uc_iter
)는 __reversed__
마술 방법 이 필요합니다 ( docs에 따르면 새로운 반복자를 반환하지만 self
작업을 반환해야 합니다 (적어도 CPython에서)). 그리고 getitem iteratable ( uc_getitem
)에는 __len__
마술 방법 이 있어야합니다 .
# for uc_iter we add __reversed__ and update __next__
def __reversed__(self):
self.index = -1
return self
def __next__(self):
try:
result = self.text[self.index]
except IndexError:
raise StopIteration
self.index += -1 if self.index < 0 else +1
return result
# for uc_getitem
def __len__(self)
return len(self.text)
무한 게으르게 평가 된 반복자에 대한 Panic 대령의 두 번째 질문에 대답하기 위해 위의 네 가지 방법 각각을 사용하는 예제가 있습니다.
# generator
def even_gen():
result = 0
while True:
yield result
result += 2
# generator expression
def even_genexp():
return (num for num in even_gen()) # or even_iter or even_getitem
# not much value under these circumstances
# iterator protocol
class even_iter():
def __init__(self):
self.value = 0
def __iter__(self):
return self
def __next__(self):
next_value = self.value
self.value += 2
return next_value
# getitem method
class even_getitem():
def __getitem__(self, index):
return index * 2
import random
for iterator in even_gen, even_genexp, even_iter, even_getitem:
limit = random.randint(15, 30)
count = 0
for even in iterator():
print even,
count += 1
if count >= limit:
break
print
결과는 (적어도 내 샘플 실행의 경우) :
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32
사용할 것을 선택하는 방법? 이것은 대부분 맛의 문제입니다. 내가 가장 자주 보는 두 가지 방법은 생성기 및 반복기 프로토콜뿐만 아니라 하이브리드 ( __iter__
생성기 반환)입니다.
생성기 표현식은 목록 이해를 대체하는 데 유용합니다 (게 으르므로 리소스를 절약 할 수 있음).
이전 Python 2.x 버전과의 호환성이 필요한 경우을 사용하십시오 __getitem__
.
uc_iter
완료되면 만료되어야합니다 (그렇지 않으면 무한대로). 다시하고 싶다면 다시 전화하여 새 반복자를 가져와야 uc_iter()
합니다.
self.index = 0
있는 __iter__
당신을 통해 여러 번 반복 할 수 있도록. 그렇지 않으면 당신은 할 수 없습니다.
우선 itertools 모듈 은 반복자가 유용한 모든 종류의 경우에 매우 유용하지만 파이썬에서 반복자를 작성하는 데 필요한 모든 것입니다.
수율
멋지지 않습니까? 수익률은 함수 의 정규 수익 을 대체하는 데 사용할 수 있습니다 . 객체를 동일하게 반환하지만 상태를 파괴하고 종료하는 대신 다음 반복을 실행할 때의 상태를 저장합니다. itertools 함수 목록 에서 직접 가져온 조치의 예는 다음과 같습니다 .
def count(n=0):
while True:
yield n
n += 1
함수 설명 ( itertools 모듈 의 count () 함수입니다 ...)에 명시된 것처럼 n으로 시작하는 연속 정수를 반환하는 반복자를 생성합니다.
생성기 표현 은 다른 웜 캔 (굉장한 웜)입니다. 그것들은 메모리를 절약하기 위해 List Comprehension 대신에 사용될 수 있습니다 (list comprehension은 변수에 할당되지 않은 경우 사용 후 파괴 된 메모리에 목록을 생성하지만 제너레이터 표현식은 Generator Object를 생성 할 수 있습니다 ... 반복자). 다음은 생성기 표현식 정의의 예입니다.
gen = (n for n in xrange(0,11))
이것은 전체 범위가 0에서 10 사이로 미리 결정된 것을 제외하고는 위의 반복자 정의와 매우 유사합니다.
방금 xrange () (이전에는 보지 못했지만 ...)를 발견하고 위의 예제에 추가했습니다. xrange () 는 리스트를 미리 빌드하지 않는 이점이있는 range () 의 반복 가능한 버전입니다 . 반복 할 거대한 데이터 모음이 있고 메모리가 너무 많으면 매우 유용합니다.
나는 당신이 어떤 일을 볼 수 return self
에서 __iter__
. 난 그냥 참고로 원 __iter__
자체가 (따라서에 대한 필요성을 제거하는 발전기가 될 수 __next__
및 양육 StopIteration
예외)
class range:
def __init__(self,a,b):
self.a = a
self.b = b
def __iter__(self):
i = self.a
while i < self.b:
yield i
i+=1
물론 여기서 직접 생성기를 만들 수도 있지만 더 복잡한 클래스의 경우 유용 할 수 있습니다.
return self
에서 __iter__
. 내가 yield
그것을 사용하려고 할 때 나는 당신의 코드가 내가 원하는 것을 정확하게하고 있음을 발견했다.
next()
할까요? return iter(self).next()
?
self.current
다른 카운터를 추적하지 않아도됩니다 . 이것은 최고의 투표 답변이어야합니다!
iter
되지만 그 자체가 클래스의 인스턴스가 아닙니다.
이 질문은 반복자가 아닌 반복 가능한 객체에 관한 것입니다. 파이썬에서 시퀀스는 반복 가능하므로 반복 가능한 클래스를 만드는 한 가지 방법은 시퀀스와 같은 동작, 즉 메소드 __getitem__
와 __len__
메소드를 제공하는 것 입니다. 파이썬 2와 3에서 이것을 테스트했습니다.
class CustomRange:
def __init__(self, low, high):
self.low = low
self.high = high
def __getitem__(self, item):
if item >= len(self):
raise IndexError("CustomRange index out of range")
return self.low + item
def __len__(self):
return self.high - self.low
cr = CustomRange(0, 10)
for i in cr:
print(i)
__len__()
. __getitem__
예상되는 행동만으로 충분합니다.
이 페이지의 모든 답변은 복잡한 개체에 정말 좋습니다. 그러나 속성으로 반복 가능한 종류의 내장 포함 된 사람들을 위해, 같은 str
, list
, set
또는 dict
, 또는의 구현 collections.Iterable
, 당신은 당신의 수업 시간에 어떤 일을 생략 할 수 있습니다.
class Test(object):
def __init__(self, string):
self.string = string
def __iter__(self):
# since your string is already iterable
return (ch for ch in self.string)
# or simply
return self.string.__iter__()
# also
return iter(self.string)
다음과 같이 사용할 수 있습니다.
for x in Test("abcde"):
print(x)
# prints
# a
# b
# c
# d
# e
return iter(self.string)
?
이없는 반복 가능한 함수 yield
입니다. 그것은 사용할 수 있도록 iter
기능과 변경 가능한 (에서의 상태를 유지하는 폐쇄 list
파이썬 2의 둘러싸 범위를).
def count(low, high):
counter = [0]
def tmp():
val = low + counter[0]
if val < high:
counter[0] += 1
return val
return None
return iter(tmp, None)
Python 3의 경우 클로저 상태는 둘러싸는 범위에서 변경할 수 없으며 nonlocal
로컬 변수에서 상태 변수를 업데이트하는 데 사용됩니다.
def count(low, high):
counter = 0
def tmp():
nonlocal counter
val = low + counter
if val < high:
counter += 1
return val
return None
return iter(tmp, None)
테스트;
for i in count(1,10):
print(i)
1
2
3
4
5
6
7
8
9
iter
하지만 명확하게 말하면 : 이것은 yield
기반 생성기 함수를 사용하는 것보다 더 복잡하고 덜 효율적 이다. 파이썬은 yield
여기에서 이용할 수없는 기반 생성기 함수에 대한 수많은 인터프리터 지원을 통해이 코드를 상당히 느리게 만듭니다. 그럼에도 불구하고 찬성.
Matt Gregory의 대답에서 영감을 얻은 것은 a, b, ..., z, aa, ab, ..., zz, aaa, aab, ..., zzy, zzz를 반환하는 좀 더 복잡한 반복자입니다.
class AlphaCounter:
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
return self
def __next__(self): # Python 3: def __next__(self)
alpha = ' abcdefghijklmnopqrstuvwxyz'
n_current = sum([(alpha.find(self.current[x])* 26**(len(self.current)-x-1)) for x in range(len(self.current))])
n_high = sum([(alpha.find(self.high[x])* 26**(len(self.high)-x-1)) for x in range(len(self.high))])
if n_current > n_high:
raise StopIteration
else:
increment = True
ret = ''
for x in self.current[::-1]:
if 'z' == x:
if increment:
ret += 'a'
else:
ret += 'z'
else:
if increment:
ret += alpha[alpha.find(x)+1]
increment = False
else:
ret += x
if increment:
ret += 'a'
tmp = self.current
self.current = ret[::-1]
return tmp
for c in AlphaCounter('a', 'zzz'):
print(c)