通过一个简单类的实现及优化过程,慢慢深入迭代的概念。
该类的主要功能如下:
- 传入一段话
- 可以迭代输出这段话的中所包含的每个词
惰性单词序列
前面的两个版本单词序列,都是我们事先就把所有的单词全部找了出来。
这种情况下,如果我只用到前面几个单词,或者我传入一串很长的文字,解析出来的所有单词当前内存都不够存放,那该怎么办?
最好我们用到的时候再去计算所需的单词是什么,Python re
模块的 finditer
就是 findall
的惰性实现版本。
使用 finditer
后,我们的代码就变成了这样:
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,我们惰性版本的单词序列就完成了。
生成器表达式
看一个很有意思的例子:
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
的时候才会去运行指定的代码。
我们的惰性版单词序列也可以使用生成器表达式:
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))
看起来是不是又简洁了一点 (ง •_•)ง
总结
- 使用生成器的时候需要考虑其他哪些相关代码可以有相应的惰性实现方法。
- 生成器表达式和列表推导式类似,但是生成器表达式是惰性的。