流畅的迭代器(三)

流畅的迭代器(三)

通过一个简单类的实现及优化过程,慢慢深入迭代的概念。

该类的主要功能如下:

  • 传入一段话
  • 可以迭代输出这段话的中所包含的每个词

惰性单词序列

前面的两个版本单词序列,都是我们事先就把所有的单词全部找了出来。

这种情况下,如果我只用到前面几个单词,或者我传入一串很长的文字,解析出来的所有单词当前内存都不够存放,那该怎么办?

最好我们用到的时候再去计算所需的单词是什么,Python re 模块的 finditer 就是 findall 的惰性实现版本。

使用 finditer 后,我们的代码就变成了这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import re
import reprlib

RE_WORD = re.compile(r'\w+')


class Sentence(object):
def __init__(self, text):
self.text = text

def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)

def __iter__(self):
for match in RE_WORD.finditer(self.text):
yield match.group()

这时候我们就不需要 self.words 列表来存放我们事先算出来的所有单词,通过 finditer 拿到一个可迭代对象,然后通过 yield 关键字使得 __iter__ 方法返回一个生成器(生成器实现了 __iter____next__ 接口)。

OK,我们惰性版本的单词序列就完成了。

生成器表达式

看一个很有意思的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
In [1]: def gen_AB():
...: print('start')
...: yield 'A'
...: print('continue')
...: yield 'B'
...: print('end.')
...:

In [2]: res1 = [x*3 for x in gen_AB()]
start
continue
end.

In [3]: for i in res1:
...: print('-->', i)
...:
--> AAA
--> BBB

In [4]: res2 = (x*3 for x in gen_AB())

In [5]: res2
Out[5]: <generator object <genexpr> at 0x000001CCFAE7E518>

In [6]: for i in res2:
...: print('-->', i)
...:
start
--> AAA
continue
--> BBB
end.

列表推导式和生成器表达式虽然只是 []() 的区别,但是从上面这个例子直观的看到,列表推导式是直接把整个 gen_AB() 运行一遍,而 res2 只是生成一个生成器,只有当真正迭代 res2 的时候才会去运行指定的代码。

我们的惰性版单词序列也可以使用生成器表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re
import reprlib

RE_WORD = re.compile(r'\w+')


class Sentence(object):
def __init__(self, text):
self.text = text

def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)

def __iter__(self):
return (match.group() for match in RE_WORD.finditer(self.text))

看起来是不是又简洁了一点 (ง •_•)ง

总结

  1. 使用生成器的时候需要考虑其他哪些相关代码可以有相应的惰性实现方法。
  2. 生成器表达式和列表推导式类似,但是生成器表达式是惰性的。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×