문제

파이썬에서 반복 함수(또는 반복자 객체)를 어떻게 생성합니까?

도움이 되었습니까?

해결책

Python의 Iterator 객체는 반복자 프로토콜을 준수합니다. 이는 기본적으로 두 가지 메서드를 제공한다는 의미입니다. __iter__() 그리고 next().그만큼 __iter__ 반복자 객체를 반환하고 루프 시작 시 암시적으로 호출됩니다.그만큼 next() 메서드는 다음 값을 반환하고 각 루프 증분에서 암시적으로 호출됩니다. next() 반환할 값이 더 이상 없을 때 StopIteration 예외를 발생시킵니다. 이는 반복을 중지하기 위해 구문을 반복하여 암시적으로 캡처됩니다.

다음은 카운터의 간단한 예입니다.

class Counter:
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

    def next(self): # Python 3: def __next__(self)
        if self.current > self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1


for c in Counter(3, 8):
    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, 8):
    print c

인쇄된 출력은 동일합니다.내부적으로 생성기 개체는 반복자 프로토콜을 지원하고 Counter 클래스와 대략 유사한 작업을 수행합니다.

데이비드 머츠(David Mertz)의 기사, 반복자와 단순 생성기, 꽤 좋은 소개입니다.

다른 팁

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

예:

# generator
def uc_gen(text):
    for char in text:
        yield char.upper()

# generator expression
def uc_genexp(text):
    return (char.upper() for char in text)

# iterator protocol
class uc_iter():
    def __init__(self, text):
        self.text = text
        self.index = 0
    def __iter__(self):
        return self
    def __next__(self):
        try:
            result = self.text[self.index].upper()
        except IndexError:
            raise StopIteration
        self.index += 1
        return result

# getitem method
class uc_getitem():
    def __init__(self, text):
        self.text = text
    def __getitem__(self, index):
        result = self.text[index].upper()
        return result

네 가지 방법이 모두 실행되는 모습을 보려면 다음을 수행하세요.

for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem:
    for ch in iterator('abcde'):
        print ch,
    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__ 매직 메서드(뒤로 이동하는 새 반복자를 반환해야 함)그리고 반복 가능한 getitem(uc_getitem)가 있어야합니다 __len__ 마법의 방법:

    # for uc_iter
    def __reversed__(self):
        return reversed(self.text)

    # for uc_getitem
    def __len__(self)
        return len(self.text)

무한하게 지연 평가되는 반복기에 대한 Colonel 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

우선, itertools 모듈 이터레이터가 유용한 모든 종류의 경우에 매우 유용하지만, 파이썬에서 이터레이터를 만드는 데 필요한 모든 것은 다음과 같습니다.

생산하다

멋지지 않나요?수율은 일반을 대체하는 데 사용할 수 있습니다. 반품 함수에서.객체를 동일하게 반환하지만 상태를 파괴하고 종료하는 대신 다음 반복을 실행하려고 할 때를 위해 상태를 저장합니다.다음은 itertools 함수 목록:

def count(n=0):
    while True:
        yield n
        n += 1

기능 설명에 명시된 대로(이것은 세다() itertools 모듈의 함수...)를 사용하면 n으로 시작하는 연속 정수를 반환하는 반복자를 생성합니다.

생성기 표현식 완전히 다른 벌레 캔입니다(굉장한 벌레입니다!).그들은 대신에 사용될 수 있습니다 목록 이해 메모리를 절약하기 위해(목록 이해는 변수에 할당되지 않은 경우 사용 후 파괴되는 메모리에 목록을 생성하지만 생성기 표현식은 생성기 개체를 생성할 수 있습니다...이는 Iterator를 표현하는 멋진 방법입니다).다음은 생성기 표현식 정의의 예입니다.

gen = (n for n in xrange(0,11))

이는 전체 범위가 0에서 10 사이로 미리 결정된다는 점을 제외하면 위의 반복자 정의와 매우 유사합니다.

방금 찾았어요 xrange() (이전에 본 적이 없어서 놀랐습니다...) 위의 예에 추가했습니다. xrange() 의 반복 가능한 버전입니다. 범위() 목록을 미리 작성하지 않는다는 장점이 있습니다.반복해야 할 거대한 데이터 모음이 있고 이를 수행할 메모리가 너무 많다면 매우 유용할 것입니다.

너희 중 몇몇이 하는 걸 봤어 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

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

이 질문은 반복자가 아니라 반복 가능한 객체에 관한 것입니다.Python에서는 시퀀스도 반복 가능하므로 반복 가능한 클래스를 만드는 한 가지 방법은 시퀀스처럼 동작하도록 만드는 것입니다.그것을 제공 __getitem__ 그리고 __len__ 행동 양식.저는 이것을 Python 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)

이것은 반복 가능한 함수입니다. yield.그것은 iter 함수와 상태를 변경 가능한 상태로 유지하는 클로저(list) Python 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

이 페이지의 모든 답변은 복잡한 개체에 매우 적합합니다.하지만 내장된 반복 가능 유형을 속성으로 포함하는 경우에는 다음과 같습니다. 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 string)

다음과 같이 사용할 수 있습니다.

for x in Test("abcde"):
    print(x)

# prints
# a
# b
# c
# d
# e

짧고 간단한 것을 찾고 계시다면 아마도 이것으로 충분할 것입니다:

class A(object):
    def __init__(self, l):
        self.data = l

    def __iter__(self):
        return iter(self.data)

사용 예:

In [3]: a = A([2,3,4])

In [4]: [i for i in a]
Out[4]: [2, 3, 4]

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)
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top