μΉ΄ν…Œκ³ λ¦¬ 보관물: Python

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_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__.


λ‹΅λ³€

μš°μ„  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

이 글은 Python μΉ΄ν…Œκ³ λ¦¬λ‘œ λΆ„λ₯˜λ˜μ—ˆκ³  λ‹˜μ— μ˜ν•΄ 에 μž‘μ„±λμŠ΅λ‹ˆλ‹€.