首页 > 编程语言 >Python 中的迭代器趣味实践

Python 中的迭代器趣味实践

时间:2023-01-15 10:32:36浏览次数:48  
标签:遍历 word 迭代 Python 单词 dict words 趣味 line


1. 遍历文本文件中的单词

假设存在文本文件 test.txt,内容如下:

The Zen of Python

Beautiful is better than ugly

Simple is better than complex

注意文件包含有空行,要求完成如下任务:

  1. 统计文件有多少个单词
  2. 统计文件中每个单词出现的频率

2. 直接遍历的方法

2.1 统计单词个数

假设没有学习迭代器,使用直接遍历的方法实现 “统计单词个数” 的功能需求,代码如下:

file = open('test.txt')
count = 0

while True:
line = file.readline()
if not line:
break

words = line.split()
for word in words:
print(word)
count = count + 1
print('count = %d' % count)
  • 在第 1 行,打开文件 test.txt,变量 file 标识已经打开的文件
  • 在第 2 行,变量 count 用于记录文件中单词的个数
  • 程序逻辑由两个循环构成:外循环和内循环
  • 在第 4 行,外循环,遍历文件的每一行文本
  • 在第 5 行,读取文件的一行
  • 在第 6 行,如果 not line 为真,表示读取到文件的结束,退出程序
  • 在第 10 行,内循环,遍历每一行文本的单词
  • 在第 9 行,使用 split 方法将文本分割为多个单词,将结果保存在列表 words 中
  • 在第 10 行,使用 for 循环遍历列表 words
  • 在第 11 行,打印当前遍历的单词
  • 在第 12 行,统计单词个数
  • 在第 13 行,打印单词的总个数
  • 在第 4 行,外循环,遍历文件的每一行文本
  • 在第 5 行,读取文件的一行
  • 在第 6 行,如果 not line 为真,表示读取到文件的结束,退出程序
  • 在第 10 行,内循环,遍历每一行文本的单词
  • 在第 9 行,使用 split 方法将文本分割为多个单词,将结果保存在列表 words 中
  • 在第 10 行,使用 for 循环遍历列表 words
  • 在第 11 行,打印当前遍历的单词
  • 在第 12 行,统计单词个数
  • 在第 5 行,读取文件的一行
  • 在第 6 行,如果 not line 为真,表示读取到文件的结束,退出程序
  • 在第 9 行,使用 split 方法将文本分割为多个单词,将结果保存在列表 words 中
  • 在第 10 行,使用 for 循环遍历列表 words
  • 在第 11 行,打印当前遍历的单词
  • 在第 12 行,统计单词个数

注意,程序能够对空行进行正确的处理:

  • 在第 9 行,使用 split 方法将 line 分割为多个单词
  • 如果 line 为空行,则 split 返回一个空列表 []
  • 在第 11 行,使用 for 循环遍历一个空列表,不会执行 for 循环的循环体代码

程序运行输出结果如下:

The
Zen
of
Python
Beautiful
is
better
than
ugly
Simple
is
better
than
complex
count = 14

2.2 统计单词出现频率

假设没有学习迭代器,使用直接遍历的方法实现 “统计单词出现频率” 的功能需求,代码如下:

file = open('test.txt')
dict = {}

while True:
line = file.readline()
if not line:
break

words = line.split()
for word in words:
if word in dict:
dict[word] += 1
else:
dict[word] = 1

for word,count in dict.items():
print('%s: %d' % (word, count))
  • 在第 1 行,打开文件 test.txt,变量 file 标识已经打开的文件
  • 在第 2 行,字典 dict 用于记录文件中单词的出现频率
  • 字典 dict 的键为单词
  • 字典 dict 的值为该单词在文本中出现的次数
  • 程序逻辑由两个循环构成:外循环和内循环
  • 在第 4 行,外循环,遍历文件的每一行文本
  • 在第 5 行,读取文件的一行
  • 在第 6 行,如果 not line 为真,表示读取到文件的结束,退出程序
  • 在第 10 行,内循环,遍历每一行文本的单词
  • 在第 9 行,使用 split 方法将文本分割为多个单词,将结果保存在列表 words 中
  • 在第 11 行,如果 word 已经存在于 dict 中
  • 则在第 12 行,该单词出现的次数加 1
  • 在第 13 行,如果 word 不存在于 dict 中
  • 则在第 14 行,该单词出现的次数初始化为 1
  • 在第 16 行,打印 dict 的键和值
  • 字典 dict 的键为单词
  • 字典 dict 的值为该单词在文本中出现的次数
  • 在第 4 行,外循环,遍历文件的每一行文本
  • 在第 5 行,读取文件的一行
  • 在第 6 行,如果 not line 为真,表示读取到文件的结束,退出程序
  • 在第 10 行,内循环,遍历每一行文本的单词
  • 在第 9 行,使用 split 方法将文本分割为多个单词,将结果保存在列表 words 中
  • 在第 11 行,如果 word 已经存在于 dict 中
  • 则在第 12 行,该单词出现的次数加 1
  • 在第 13 行,如果 word 不存在于 dict 中
  • 则在第 14 行,该单词出现的次数初始化为 1
  • 在第 5 行,读取文件的一行
  • 在第 6 行,如果 not line 为真,表示读取到文件的结束,退出程序
  • 在第 9 行,使用 split 方法将文本分割为多个单词,将结果保存在列表 words 中
  • 在第 11 行,如果 word 已经存在于 dict 中
  • 则在第 12 行,该单词出现的次数加 1
  • 在第 13 行,如果 word 不存在于 dict 中
  • 则在第 14 行,该单词出现的次数初始化为 1
  • 则在第 12 行,该单词出现的次数加 1
  • 则在第 14 行,该单词出现的次数初始化为 1

程序运行输出结果如下:

The: 1
Zen: 1
of: 1
Python: 1
Beautiful: 1
is: 2
better: 2
than: 2
ugly: 1
Simple: 1
complex: 1

结果表明:

  • 单词 is better than 出现了 2 次
  • 其它单词出现了 1 次

2.3 直接遍历的方法的问题

2.1 小节程序的框架与 2.2 小节程序的框架类似:

  • 程序的主体结构由两重循环构成:外循环和内循环
  • 外循环,遍历文件的每一行文本
  • 内循环,遍历每一行文本的单词

它们的不同之处在于:

  • 遍历每个单词时,2.1 小节的程序执行如下代码统计单词个数
count = count + 1
  • 遍历每个单词时,2.2 小节的程序执行如下代码统计单词出现频率
if word in dict:
dict[word] += 1
else:
dict[word] = 1

这两个小节的程序的其它代码则完全一样,程序中存在明显的代码重复。

3. 使用迭代器的方法

3.1 可迭代对象与迭代器

本节实现类 IterateWord 用于简化遍历文本中的单词,**类 IterateWord 既是可迭代对象也是迭代器: **

  • 类 IterateWord 是可迭代对象,提供了 __iter__ 方法,返回一个迭代器
  • 类 IterateWord 是迭代器,提供了 __next__ 方法,返回下一个遍历的对象

类 IterateWord 的定义如下:

class IterateWord:
def __init__(self, file):
self.file = file
self.words = []
  • 在第 2 行,参数 file 指明了被遍历的文本文件
  • 在第 3 行,将参数 file 保存到成员变量中
  • 在第 4 行,IterateWord 将每一行文本分割为多个单词,保存在 self.words 中,该变量初始化为空列表

3.2 实现 __iter__ 方法

类 IterateWord 是一个可迭代对象,需要向外界提供 __iter__ 方法,该方法的实现如下:

def __iter__(self):
return self

类 IterateWord 既是可迭代对象也是迭代器,返回 self 表示 self 是一个迭代器。

3.3 实现 __next__ 方法

类 IterateWord 是一个迭代器,需要向外界提供 __next__ 方法,该方法的实现如下:

def __next__(self):
if len(self.words) == 0:
self.get_non_blank_line()
word = self.words.pop(0)
return word
  • 在第 1 行,定义 __next__ 方法
  • IterateWord 读取一行文本后,将该文本分割为单词列表,保存在 words 中
  • 在第 2 行,如果列表 words 中的单词数量为 0
  • 在第 3 行,调用 get_non_blank_line 方法读取一个非空的行
  • 在第 4 行,使用 words.pop(0) 从 words 中删除第 0 个单词,即该行文本的首个单词
  • 在第 5 行,返回从 words 中删除的第 0 个单词
  • 在第 2 行,如果列表 words 中的单词数量为 0
  • 在第 3 行,调用 get_non_blank_line 方法读取一个非空的行

get_non_blank_line 方法读取一个非空的行,代码如下:

def get_non_blank_line(self):
while True:
line = file.readline()
if not line:
raise StopIteration
self.words = line.split()
if len(self.words) != 0:
break
  • 在第 2 行,使用循环依次读取文件的每行文本
  • 在第 3 行,使用 readline 方法读取文件的一行文本
  • 在第 4 行,not line 为真表示读取到文件结束
  • 在第 5 行,抛出异常 StopIteration,表示遍历结束
  • 在第 6 行,将 line 分割为多个单词
  • 如果 line 是一个空行,则 len(words) == 0,需要跳过这种情况,读取下一行文本
  • 如果 line 不是一个空行,则 len(words) != 0,在第 7 行执行 break 退出循环,结束函数的执行,此时列表 self.words 中必定包含有若干个单词
  • 在第 5 行,抛出异常 StopIteration,表示遍历结束
  • 如果 line 是一个空行,则 len(words) == 0,需要跳过这种情况,读取下一行文本
  • 如果 line 不是一个空行,则 len(words) != 0,在第 7 行执行 break 退出循环,结束函数的执行,此时列表 self.words 中必定包含有若干个单词

4. 使用迭代器解决需求

4.1 统计单词个数

本节基于前面已经实现的迭代器,完成统计单词个数的任务,代码如下:

file = open('test.txt')
count = 0

for word in IterateWord(file):
print(word)
count = count + 1
  • 在第 1 行,打开文件 test.txt
  • 在第 2 行,变量 count 用于记录文件中单词的个数
  • 在第 4 行,遍历文件中的每一个单词
  • 在第 5 行,打印当前遍历的单词
  • 在第 6 行,统计单词个数
  • 在第 5 行,打印当前遍历的单词
  • 在第 6 行,统计单词个数

程序运行输出结果如下:

The
Zen
of
Python
Beautiful
is
better
than
ugly
Simple
is
better
than
complex
count = 14

4.2 统计单词出现频率

file = open('test.txt')
dict = {}

for word in IterateWord(file):
if word in dict:
dict[word] += 1
else:
dict[word] = 1

for word,count in dict.items():
print('%s: %d' % (word, count))
  • 在第 1 行,打开文件 test.txt,变量 file 标识已经打开的文件
  • 在第 4 行,遍历每一行文本的单词
  • 在第 5 行,如果 word 已经存在于 dict 中
  • 则在第 5 行,该单词出现的次数加 1
  • 在第 7 行,如果 word 不存在于 dict 中
  • 则在第 8 行,该单词出现的次数初始化为 1
  • 在第 10 行,打印 dict 的键和值
  • 在第 5 行,如果 word 已经存在于 dict 中
  • 则在第 5 行,该单词出现的次数加 1
  • 在第 7 行,如果 word 不存在于 dict 中
  • 则在第 8 行,该单词出现的次数初始化为 1
  • 则在第 5 行,该单词出现的次数加 1
  • 则在第 8 行,该单词出现的次数初始化为 1

程序运行输出结果如下:

The: 1
Zen: 1
of: 1
Python: 1
Beautiful: 1
is: 2
better: 2
than: 2
ugly: 1
Simple: 1
complex: 1

结果表明:

  • 单词 is better than 出现了 2 次
  • 其它单词出现了 1 次

4.3 总结

4.3.1 简化了遍历的代码

基于迭代器的方法解决 “统计单词个数” 与 “统计单词出现频率” 这两个任务,遍历文本中的单词的代码非常简洁,如下所示:

for word in IterateWord(file):
处理 word

IterateWord 屏蔽了文件由多行构成、可能存在空行、每行由多个单词构成等细节,遍历文件中的单词非常的方便。

4.3.2 迭代器的实现复杂

直接遍历文件单词的代码如下:

while True:
line = file.readline()
if not line:
break

words = line.split()
for word in words:
处理 word

使用直接遍历文件单词的方式解决 “统计单词个数” 与 “统计单词出现频率” 这两个任务,存在有明显的代码重复。虽然代码重复,但是代码很直观、容易理解

与之相比,IterateWord 的实现较为复杂、不够直观,Python 中提供了生成器的语法,可以用于简化迭代器的实现。请查找词条 “Python 中的生成器实现原理” 和 “Python 中的迭代器趣味实践”,阅读如何使用生成器简化实现迭代器。

标签:遍历,word,迭代,Python,单词,dict,words,趣味,line
From: https://blog.51cto.com/10zhancom/6008321

相关文章

  • 可以让程序员更有效率的 Python 技巧?
    在本文中,我们将了解一些可以使我们的编码人员的生活更高效、更轻松、更快乐的Python技巧. 使用Try和except语句我们在主要条件下看到的另一个缺乏效用的是使用try......
  • 为什么 Python 是初学者更好的语言?
    在本文中,我们将了解为什么Python如此常见的初学者语言。以下是各种原因。为什么Python是初学者更好的第一语言?还有其他优秀的编程语言可用,但Python是初学者最好的语言......
  • python:一文带你搞懂AB测试
    学习目标目标知道什么是AB测试知道AB测试的步骤知道AB测试原理  让我们想象一下,在公司的某产品研发讨论会上……“这个功能要不要上?”“我觉得没问题,XX指标肯定能涨一大截......
  • 为什么你应该使用NumPy数组而不是嵌套的Python列表?
    在本文中,我们将向您展示为什么使用NumPy数组而不是嵌套的Python列表,以及它们之间的异同。PythonNumPyLibraryNumPy是一个Python库,旨在有效地处理Python中的数组。......
  • Python实现排序
    冒泡排序交换排序相邻元素两两比较大小,有必要则交换元素越小或越大,就会在数列中慢慢的交换并“浮”向顶端,如同水泡咕嘟咕嘟往上冒核心算法排序算法,一般都实现为就......
  • Python闭包和装饰器的学习
    之前看了不少的帖子,总是看了这篇帖子说的理解了,换篇帖子说的又不理解了,把人弄晕了,究其原因还是因为没有把底层原理理解。这两个概念总是放在一起说,两者之间肯定是有关系的......
  • python def函数总结
    简单无参函数编写脚本test1.pydefregister_user():"""docstring"""#描述函数的功能print("Welcome!")register_user()#调用函数执行脚本test1.py输出结果We......
  • Python之集合操作举例
    #集合的操作(Set、frozenset)#集合特点:无序、元素不可重复、执行效率高但是比列表占用空间大,空间换时间s={"a","b","c"}s=set("abcd")print(s)#{'d','b',......
  • Python树与树算法
    Python树与树算法树的概念树(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>=1)个有限节点组成一个具......
  • Python-训练简单的机器学习分类算法
    Python-训练简单的机器学习分类算法人工神经元为了设计人工智能,人们尝试模仿生物神经元,神经元是大脑中连接起来参与化学和电信号处理与传输的神经细胞,麦库洛和皮兹(MCP)把......