[python] 기본 파이썬 반복기 빌드

파이썬에서 반복 함수 (또는 반복자 객체)를 어떻게 만들 수 있습니까?



답변

반복자는 파이썬의 객체는 기본적으로 그들은 두 가지 방법을 제공하는 것을 의미 반복자 프로토콜을 준수 : __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 는 꽤 좋은 소개입니다.


답변

반복 함수를 작성하는 네 가지 방법이 있습니다.

예 :

# 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_genuc_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__.


답변

우선 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

물론 여기서 직접 생성기를 만들 수도 있지만 더 복잡한 클래스의 경우 유용 할 수 있습니다.


답변

이 질문은 반복자가 아닌 반복 가능한 객체에 관한 것입니다. 파이썬에서 시퀀스는 반복 가능하므로 반복 가능한 클래스를 만드는 한 가지 방법은 시퀀스와 같은 동작, 즉 메소드 __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)


답변

이 페이지의 모든 답변은 복잡한 개체에 정말 좋습니다. 그러나 속성으로 반복 가능한 종류의 내장 포함 된 사람들을 위해, 같은 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


답변

이없는 반복 가능한 함수 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