iterr, yield与enumerate的模拟实现

模拟实现一个enumerate函数

1
2
3
4
5
6
def myEnumerate(seq, start=0):
results = []
n = start
for i in seq:
results.append((n, i))
return results

返回一个list, 如果list数据过多,则占用内存太大。而迭代器每次只需要很小的内存。再往下看迭代器。

迭代器

内建函数iter()可以生成一个iterator迭代器。相比list来说,iterator不需要很大的内存空间。
迭代器通过next()来遍历元素,并且完成遍历时抛出StopIteration()异常。

1
2
3
4
5
6
7
8
it = iter(range(5))
print it.next() # 0
print it.next() # 1
print it.next() # 2
print it.next() # 3
print it.next() # 4

print it.next() # StopIteration()

可以用for循环对迭代器进行遍历

1
2
3
4
5
it = iter(range(5))
for i in it:
print i

print it.next() # StopIteration()

遍历完成时,调用it.next(),抛出StopIteration()异常。可以看出for循环调用的是next()方法。就像下边这样。

1
2
3
4
5
while True:
try:
print it.next()
except StopIteration:
break

用迭代器改进enumerate的实现

一个类只要实现了iter与next()方法, 便可以进行迭代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class myEnumerate:
def __init__(self, seq, start=0):
self.seq = seq
self.start = start
self.n = 0

def __iter__(self):
return self

def next(self):
if self.n == len(self.seq):
raise StopIteration()
item = self.seq[self.n]
index = self.start
self.n += 1
self.start += 1
return index, item

使用迭代器解决了空间占用的问题,不过代码也太繁琐了,一点没有python风格。

yield

于是,简洁的代码便来了。一个可迭代的并且简洁的简洁的方案。使用next()方法会依次返回元素,并且越界时报StopIteration异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
def myEnumerate(seq, start=0):
n = start
for i in seq:
yield n, i
n += 1

it = myEnumerate(range(5))
print it.next() # (0, 0)
print it.next() # (1, 1)
print it.next() # (2, 2)
print it.next() # (3, 3)
print it.next() # (4, 4)
print it.next() # StopIteration