νμ΄μ¬μμ λ°λ³΅ ν¨μ (λλ λ°λ³΅μ κ°μ²΄)λ₯Ό μ΄λ»κ² λ§λ€ μ μμ΅λκΉ?
λ΅λ³
λ°λ³΅μλ νμ΄μ¬μ κ°μ²΄λ κΈ°λ³Έμ μΌλ‘ κ·Έλ€μ λ κ°μ§ λ°©λ²μ μ 곡νλ κ²μ μλ―Έ λ°λ³΅μ νλ‘ν μ½μ μ€μ : __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 λ κ½€ μ’μ μκ°μ λλ€.
λ΅λ³
λ°λ³΅ ν¨μλ₯Ό μμ±νλ λ€ κ°μ§ λ°©λ²μ΄ μμ΅λλ€.
- μμ±κΈ° μμ± ( yield ν€μλ μ¬μ© )
- μμ±κΈ° ννμ μ¬μ© ( genexp )
- λ°λ³΅μλ₯Ό λ§λλλ€ (defines
__iter__
λ°__next__
(λλnext
Python 2.x)) - νμ΄μ¬μ΄ μ€μ€λ‘ λ°λ³΅ ν μμλ ν΄λμ€λ₯Ό λ§λλλ€ ( define
__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__
.
λ΅λ³
μ°μ 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