我们先考虑这样一个函数,对于一个字符串,我们想找出每一个单词的首字母在这个字符串中的位置,程序如下:
# -*- coding: utf-8 -*- address = 'four score and seven years ago' def index_words(text): result = [] if text: #判断是否为空,不为空那么第一个元素肯定是0 result.append(0) for index,word in enumerate(text): if word == ' ': result.append(index+1) return result print(index_words(address))
输出:
[0, 5, 11, 15, 21, 27]
这里我们用到了enumerate,枚举,这样每当找到一个空格,说明就是下一个单词的开始,我们就加+1然后放到result列表中就可以了。
这里我们细想,其实代码有点拥挤,创建了新的列表,并且不断的添加元素到列表,这里我们可以考虑使用生成器(一个函数里如果运用了yield表达式,这个函数就是生成器,每次执行的时候,遇到yield就中断,下一次再执行,就从yield处继续执行,另外一个列表生成式如果外面是括号,那么也是一个生成器),当我们调用生成器时,他不会真的运行,而是会返回一个迭代器(迭代器就是可以被next函数不断调用的对象,也可以用for循环来输出)。然后我们就可以不断的在这个迭代器上调用next函数(我们一般使用for函数,来不断的输出每一个值),迭代器就会把生成器推到下一个yield表达是那里。生成器传给yield的每一个值,都会有迭代器来返回给调用者,这里的调用者就是我们的for循环。
# -*- coding: utf-8 -*- address = 'four score and seven years ago' def index_words(text): if text: yield 0 for index,word in enumerate(text): if word == ' ': yield index+1 print(index_words(address)) result = list(index_words(address)) #这里通过list函数将这个生成器转换为列表 print(result) for i in index_words(address): # 我们一般不用next函数来输出生成器的每一个元素,一般都用for来输出 print(i)
输出:
<generator object index_words at 0x00000000007D78E0>
[0, 5, 11, 15, 21, 27]
0
5
11
15
21
27
同样的可以从文件里读取数据来继续做上面的操作,我们新建一个 1.txt文件 ,内容为
four score and seven years ago asd asdd qqqq asdasd
程序:
# -*- coding: utf-8 -*- def index_words(handle): for line in handle: offset = 0 if line: yield 0 for letter in line: offset = offset + 1 if letter == ' ': yield offset with open('1.txt','r') as f: print(list(index_words(f))) 这里会输出一个列表,下面的那个for循环就没有用了,因为这个迭代器已经使用过了。 for i in index_words(f): print(i) # 请记住,迭代器是有状态的,不可以重复使用,所以这里就不会输出啥了,同样的,如果这个for循环写在前面,那么这个list列表也是没啥,为空
输出:
[0, 5, 11, 15, 21, 27, 0, 4, 9, 14]
总结:
使用生成器比使用列表把收集到的结果展示出来更清晰,直观。
由生成器函数所返回的那个迭代器,可以把生成器函数体中,传给yield表达式的每一个值,逐次产生出来。
无论输入的量有多大,生成器都能产生一系列的输出,因为这些输入和输出,都不会影响它在执行时所耗的内存。