有四种构建迭代函数的方法:
例子:
def uc_gen(text):
for char in text.upper():
yield char
def uc_genexp(text):
return (char for char in text.upper())
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
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__
魔法方法(根据文档, 必须返回一个新的迭代器, 但在CPython中返回self
也可以工作); 而getitem可迭代对象(uc_getitem
)必须实现__len__
魔法方法:
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
def __len__(self)
return len(self.text)
为了回答Colonel Panic的有关无限延迟评估迭代器的附加问题,以下是使用上述四种方法的示例:
def even_gen():
result = 0
while True:
yield result
result += 2
def even_genexp():
return (num for num in even_gen())
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
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__
。
__next__
方法。counter
是一个迭代器,但它不是一个序列。它不会存储它的值。例如,你不应该在双重嵌套的 for 循环中使用计数器。 - leewz__iter__
中对self.current
进行赋值(除了在__init__
中)。否则,对象只能被迭代一次。例如,如果你写了ctr = Counters(3, 8)
,那么你不能使用for c in ctr
超过一次。 - CurtCounter
是一个迭代器,而迭代器只应该被迭代一次。如果你在__iter__
中重置了self.current
,那么对Counter
的嵌套循环将会完全破坏,并且所有迭代器的预设行为(对它们调用iter
是幂等的)都将被违反。如果你想要能够多次迭代ctr
,它就需要是一个非迭代器可迭代对象,在每次调用__iter__
时返回一个全新的迭代器。试图混合和匹配(一个在调用__iter__
时隐式重置的迭代器)会违反协议。 - ShadowRangerCounter
将成为一个非迭代器的可迭代对象,您需要完全删除__next__
/next
的定义,并且可能会重新定义__iter__
为与本答案末尾描述的生成器相同形式的生成器函数(除了边界不是来自于__iter__
参数,而是来自于存储在self
上并在__iter__
中从self
访问的__init__
参数)。 - ShadowRanger