01-自动化工具的构建-天才老师防作弊

您好,我是悦创,很高兴能和您一起学习 Python 办公自动化。

这是鸽已久的文章了,现在我要宣布正式更新!至少一周一篇!

在本文中,请确保你对 Python 的数据结构、条件、循环、函数等这些基础知识已经掌握得不错,本课程中对于基本的 Python 语法不会做过多的讲解。

请记得跟流沙团队说一声,让我们把你加到微信群中,方便为你提供其他服务哦。我会不定期的开设直播分享,前期主要是 Python。末尾也会有流沙团队的付费答疑知识星球,欢迎加入提问。目前主要针对,公众号的各种文章答疑,当然其他类型的也可以啦!

1. AI悦创·施工计划 1

你可以看见,有所谓的施工计划1,也就会有所谓的2。大家多多支持吧!

所谓办公自动化,是将计算机技术使用到办公过程中,节省人工重复劳动,提高工作效率的一种现代化工作方式。

在日常的工作当中,你接触最多的办公软件,想必就是 office 套装中的那些工具了:Excel、Word、PPT。

表格整理、统计总结、文字处理,也许大量重复繁琐的工作正在耗费你的青春,你希望使用工具,更好的驱使电脑完成重复的劳动。

这门课程就是为了解放自我而设计。流沙团队为你讲述通过 Python 语言,如何让办公变得更为快捷和高效。

借助 Python 世界丰富的包资源,除了可以便捷的处理 office 文档,对于其它办公领域的工作,比如文件整理、电子邮件处理、Web 信息爬取、自动化消息处理等等,都可以手到擒来。

悦创以日常工作中的常见需求为基础,结合实际案例为你编写了本系列教程。 期待在学习之后,可以让你掌握事半功倍的办公技巧:

序号 章节 知识点
1 天才老师防作弊 自动化工具的构建
2 寻找VIP Excel 文件读取、读取工作表、读取单元格、工作表切片
3 自动合并周报 Excel 文件写入、操作工作表、修改单元格
4 自动生成销售英雄榜 Excel 中的字体、公式、行列宽度调整、合并及拆分、图表绘制等
5 文件整理自动化工具 文件及目录操作、压缩及解压
6 自动批量生成邀请函 Word 文档读写
7 自动合成文档、添加水印的小能手 PDF 批量生成、页面读取、合并、叠加、加密和解密
8 自动发送通知和检查回复的小秘书 电子邮件处理
9 自动抽取页面信息的工具 使用 requests 做网页下载、BeautifulSoup 进行信息提取
10 网页交互操作自动化工具 selenium 的使用
11 站巨人之肩上天揽月 以 API 方式使用网络服务
12 自动识别图片中的表格 以 SDK 方式使用网络服务
13 自动发送微信消息 操作键盘和鼠标

1.1 学习环境

阅读此文章搭建:https://www.aiyc.top/772.html

在代码执行错误时,如果遇到不会的问题,可以点加流沙团队微信,群友就可以看到你的代码帮你解答了。当然,流沙也支持付费答疑。

1.2 学习方法

Python 办公自动化是一门实用性非常强的课程。在这里,我们将 Python 视为一个实现自动化处理的工具,课程主要聚焦如何正确的使用这个工具来完成工作需求。

你不需要对 Python 实现某个功能的具体原理细节了解太多,只需要知道如何正确的使用,解决问题就可以。

在学习的过程中,坚持三多原则(多想哪些工作可以通过程序自动化解决、多查资料实现想法、根据实际需求多练习),一定能娴熟的掌握 Python 工具,让你在工作中如虎添翼。

1.3 知识结构

  • 需求描述
  • 试卷生成(提取 provinces 和 options、创建试卷、创建答案)
  • 生成答案迥异(jiǒng yì)的试卷

2. 需求描述

小悦在日常的工作中,遇到过这样的需求:考察学员或者公司新员工的技术能力。

出一份考题让被考察者完成是一个最常用的方法。

在本文中,请你跟着老师一起,用 Python 自动化地生成一份简单的地理试卷。这份试卷由 34 道选择题组成,题目形式都类似如下:

  1. 河北省的省会是哪里?

A. 北京
B. 郑州
C. 石家庄
D. 张家口

2.1 试卷生成

image.png

试卷生成的工具以一个省与省会名的对应字典作为输入:

capitals = {'北京市': '北京', '天津市': '天津', '上海市': '上海', '重庆市': '重庆',
            '河北省': '石家庄', '山西省': '太原', '陕西省': '西安', '山东省': '济南',
            '河南省': '郑州', '辽宁省': '沈阳', '吉林省': '长春', '黑龙江省': '哈尔滨',
            '江苏省': '南京', '浙江省': '杭州', '安徽省': '合肥', '江西省': '南昌',
            '福建省': '福州', '湖北省': '武汉', '湖南省': '长沙', '四川省': '成都',
            '贵州省': '贵阳', '云南省': '昆明', '广东省': '广州', '海南省': '海口',
            '甘肃省': '兰州', '青海省': '西宁', '台湾省': '台北', '内蒙古自治区': '呼和浩特',
            '新疆维吾尔自治区': '乌鲁木齐', '西藏自治区': '拉萨', '广西壮族自治区': '南宁',
            '宁夏回族自治区': '银川', '香港特别行政区': '香港', '澳门特别行政区': '澳门'}

我们要完成的这个工具需要做三件事情:

  1. 抽取出 capitals 中的省名(key),组成一个列表 provinces(省份)。
  2. 为每个 provinces 中的元素生成对应的题目选项,放置到列表 options(选项) 中。选项由城市名组成,其中有一个选项是正确答案,另外三个从其它城市中随机抽取。
  3. 根据第1、2 步生成的 provinces 和 options,生成试卷以及试卷结果并保存到文件。

也许只用语言描述有些抽象,我们用一张表来展视要构建的 provinces 和 options。

元素索引 provinces options
0 北京市 [拉萨,北京,海口,西宁]
n 河北省 [石家庄,银川, 贵阳, 哈尔滨]
  • provinces 是一个一维列表,每个元素是一个的名字
  • options 是一个二维列表,列表中的每个元素也是列表,由四个城市名组成,其中有一个城市是 provinces 中对应省的省会。

这样的两个列表一旦构建出来,生成试卷就会变得很简单,只需要从 0 到 33 遍历两个列表中的每个元素,分别取出省份名字以及城市选项名字,省份名字用在题面上,而城市选项用在选项上,就能正确输出结果了。

c43ea65545f7973c743fe8fea45e4296.gif

我们按照这个思路,分两步构建试卷。

2.2 提取 provinces 和 options

capitals 这个字典中,所有元素的 key 就是我们想要的省份名字,我们可以将它们提取出来,放到列表 provinces 中。

capitals = {'北京市': '北京', '天津市': '天津', '上海市': '上海', '重庆市': '重庆',
            '河北省': '石家庄', '山西省': '太原', '陕西省': '西安', '山东省': '济南',
            '河南省': '郑州', '辽宁省': '沈阳', '吉林省': '长春', '黑龙江省': '哈尔滨',
            '江苏省': '南京', '浙江省': '杭州', '安徽省': '合肥', '江西省': '南昌',
            '福建省': '福州', '湖北省': '武汉', '湖南省': '长沙', '四川省': '成都',
            '贵州省': '贵阳', '云南省': '昆明', '广东省': '广州', '海南省': '海口',
            '甘肃省': '兰州', '青海省': '西宁', '台湾省': '台北', '内蒙古自治区': '呼和浩特',
            '新疆维吾尔自治区': '乌鲁木齐', '西藏自治区': '拉萨', '广西壮族自治区': '南宁',
            '宁夏回族自治区': '银川', '香港特别行政区': '香港', '澳门特别行政区': '澳门'}

# 提取 capitals 变量中所有元素的 key 值,组成新的列表变量 provinces
provinces = 

看下老师的方法:

provinces = list(capitals.keys())

接下来,为每道题生成答案。像之前构想的那样,用二维列表变量 options 存储答案选项,options 的每个元素,是由一道题中 4 个备选项组成的列表。

对于每道题目的选项构建,是这个项目最困难的环节,跟着老师思路,我们一步步捋:

  1. 对于某个确定的省份,从 capitals 中获取这个省份对应的正确省会名字, 把它存储到变量 right_answer 中。
  2. capitals 中,获取所有城市的名字,然后将其中是正确答案的那个剔除掉,剩下一个全部由错误省会名字组成的变量 wrong_answer。下面动画表现的是当正确答案是昆明时的情形。

c43ea65545f7973c743fe8fea45e4296.gif

  1. wrong_answer 中随机挑选出 3 个名字,与第一步生成的 right_answer 一同组成由 4 个元素组成的选项。

在给你正式代码前,我给你补充点知识点:

In [1]: from faker import Faker

In [2]: faker = Faker()

In [3]: name_lst = []

In [4]: for i in range(10):
   ...:     name_lst.append(faker.name())
   ...:

In [5]: name_lst
Out[5]:
['Jonathan Kemp',
 'Shannon Moore',
 'Desiree Vaughan',
 'Christina Allen',
 'Jamie Petty',
 'Bonnie Green',
 'Mrs. Kristina Hamilton',
 'Lisa Harris',
 'Richard Hicks',
 'Natalie Long']

In [6]: import random

In [7]: r = random.sample(name_lst, 3)

In [8]: r
Out[8]: ['Shannon Moore', 'Christina Allen', 'Jonathan Kemp']

In [9]: r = random.sample(name_lst, 3)

In [10]: r
Out[10]: ['Lisa Harris', 'Mrs. Kristina Hamilton', 'Desiree Vaughan']

将以上需求翻译成代码实现,如以下这样:

import random

def generate_options(index):
    # generate_options 函数,根据输入的省份编号 index,生成对应的答案选项。
    # 1. 从 capitals 中获取 index 这个编号对应的省份的省会名字,存到 right_answer 变量中。
    right_answer = capitals[provinces[index]]
    # 2. 从 capitals 中获取所有城市名字,并剔除正确的答案,构建错误省会名组成的列表,
    # 存到变量 wrong_answer 中。
    wrong_answer = list(capitals.values())
    del wrong_answer[index]
    # 3. 从 wrong_answer 中随机挑选 3 个名字,并与 right_answer 一同组成由4个元素组成的选项
    wrong_answer = random.sample(wrong_answer, 3)
    current_options = [right_answer] + wrong_answer
    # 将选项中元素的顺序打乱
    random.shuffle(current_options)
    # 用列表结构返回第 index 个省的答案选项
    return current_options

老师将以上所述 3 个步骤封装到了函数 generate_options() 中。这个函数接收传入参数 index,用于指定要生成备选项的省份编号。

在第1步中,使用 provinces[index] 即获取了 index 对应的省的名字。然后,将这个名字作为 capitals 字典的 key,读取 capitals[provinces[index]] 就得到省会城市名,将它赋值给 right_answer

在第2步中,使用 capitals.values() 读取 capitals 的所有城市名,再将它转成列表类型,赋值给wrong_answer变量。

这时,wrong_answer 中还包含了正确选项,因此再使用 del wrong_answer[index]woring_answer 中的正确答案城市名剔除掉。

第3步,老师使用了 Python 的 random 模块的两个重要功能:sample()shuffle()

sample() 可以实现从一个候选集中随机挑选指定数量元素的功能。传入它的第一个参数是待挑选的集合变量,第二个参数是个数字,表示要挑选的元素数量。因此,代码使用 random.sample(wrong_answer, 3)woring_answer 中随机挑选了三个城市。

shuffle() 就如它的单词意思所描述,是个混洗函数,可以将传入的数据集合里的元素顺序打乱。将 right_answerwrong_answer拼成 current_options 之后,使用random.shuffle(current_options) 就实现了将 current_options中的答案顺序进行混洗的目的。

函数最后将 current_options作为返回结果,使得调用者可以通过函数的返回列表获取到传入的 index 所对应的答案选项。这个选项中的错误答案,是随机从城市里挑选的,并且答案顺序经过了混洗。

借助这个定义好的 generate_options() 函数,我们就可以构建 options 了,请在下面代码块中,尝试补全 TODO 部分的逻辑,生成我们在前面构想中希望创建的 options 列表:

import random

capitals = {'北京市': '北京', '天津市': '天津', '上海市': '上海', '重庆市': '重庆',
            '河北省': '石家庄', '山西省': '太原', '陕西省': '西安', '山东省': '济南',
            '河南省': '郑州', '辽宁省': '沈阳', '吉林省': '长春', '黑龙江省': '哈尔滨',
            '江苏省': '南京', '浙江省': '杭州', '安徽省': '合肥', '江西省': '南昌',
            '福建省': '福州', '湖北省': '武汉', '湖南省': '长沙', '四川省': '成都',
            '贵州省': '贵阳', '云南省': '昆明', '广东省': '广州', '海南省': '海口',
            '甘肃省': '兰州', '青海省': '西宁', '台湾省': '台北', '内蒙古自治区': '呼和浩特',
            '新疆维吾尔自治区': '乌鲁木齐', '西藏自治区': '拉萨', '广西壮族自治区': '南宁',
            '宁夏回族自治区': '银川', '香港特别行政区': '香港', '澳门特别行政区': '澳门'}

provinces = list(capitals.keys())


def generate_options(index):
    # generate_options 函数,根据输入的省份编号 index,生成对应的答案选项。

    # 1. 从 capitals 中获取 index 这个编号对应的省份的省会名字,存到 right_answer 变量中。
    right_answer = capitals[provinces[index]]

    # 2. 从 capitals 中获取所有城市名字,并剔除正确的答案,构建错误省会名组成的列表,
    # 存到变量 wrong_answer 中。
    wrong_answer = list(capitals.values())
    del wrong_answer[index]

    # 3. 从 wrong_answer 中随机挑选3个名字,并与 right_answer 一同组成由4个元素组成的选项
    wrong_answer = random.sample(wrong_answer, 3)
    current_options = [right_answer] + wrong_answer

    # 将选项打乱
    random.shuffle(current_options)

    # 用列表结构返回第 index 个省的答案选项
    return current_options


options = []
for i in range(len(capitals)):
    # TODO,根据编号i,创建每道题的选项,并把选项列表添加到 options 中

print(options)

是否填写出来了?看下老师的参考答案,你可以把它拷贝到代码中看下执行结果:

for i in range(len(capitals)):
    # TODO,根据编号i,创建每道题的选项,并把选项列表添加到options中
    option = generate_options(i)
    options.append(option)

通过调用 generate_options(i) 函数,我们计算出编号 i 所对应省份的题目选项。然后,使用 = 将结果赋值给变量 option。

再将 option 通过 options.append() 方法追加到 options 列表。在 for 循环遍历完所有的索引之后,就形成了最终的选项列表。

有了 provinces 和 options,我们的工作已经完成了大半,按一开始展示的 provinces 和 options 元素对照表的格式,我们通过代码打印一下这张表的值,看下效果是否是我们所预期的:

import random

capitals = {'北京市': '北京', '天津市': '天津', '上海市': '上海', '重庆市': '重庆',
            '河北省': '石家庄', '山西省': '太原', '陕西省': '西安', '山东省': '济南',
            '河南省': '郑州', '辽宁省': '沈阳', '吉林省': '长春', '黑龙江省': '哈尔滨',
            '江苏省': '南京', '浙江省': '杭州', '安徽省': '合肥', '江西省': '南昌',
            '福建省': '福州', '湖北省': '武汉', '湖南省': '长沙', '四川省': '成都',
            '贵州省': '贵阳', '云南省': '昆明', '广东省': '广州', '海南省': '海口',
            '甘肃省': '兰州', '青海省': '西宁', '台湾省': '台北', '内蒙古自治区': '呼和浩特',
            '新疆维吾尔自治区': '乌鲁木齐', '西藏自治区': '拉萨', '广西壮族自治区': '南宁',
            '宁夏回族自治区': '银川', '香港特别行政区': '香港', '澳门特别行政区': '澳门'}

provinces = list(capitals.keys())


def generate_options(index):
    # generate_options函数,根据输入的省份编号index,生成对应的答案选项。

    # 1. 从capitals中获取index这个编号对应的省份的省会名字,存到right_answer变量中。
    right_answer = capitals[provinces[index]]

    # 2. 从capitals中获取所有城市名字,并剔除正确的答案,构建错误省会名组成的列表,
    # 存到变量wrong_answer中。
    wrong_answer = list(capitals.values())
    del wrong_answer[index]

    # 3. 从wrong_answer中随机挑选3个名字,并与right_answer一同组成由4个元素组成的选项
    wrong_answer = random.sample(wrong_answer, 3)
    current_options = [right_answer] + wrong_answer

    # 将选项打乱
    random.shuffle(current_options)

    # 用列表结构返回第index个省的答案选项
    return current_options


options = []
for i in range(len(capitals)):
    # 根据编号i,创建每道题的选项,并把选项列表添加到options中
    option = generate_options(i)
    options.append(option)

for i in range(len(options)):
    print(provinces[i], options[i])

老师在上面代码段的最后,通过:

for i in range(len(options)):
    print(provinces[i], options[i])

遍历输出了 provinces 跟 options 中的每个元素,可以直观地看到两个列表元素的对应情况。

接下来,我们就可以通过读取 provinces 和 options 中的元素内容,完成试卷的构建了。

2.3 创建试卷

再回顾下我们要生成的题目样式:

  1. 河北省的省会是哪里?

A. 北京
B. 郑州
C. 石家庄
D. 张家口

题干的内容,其实就是由 provinces 中提取的省份名字加上固定的'的省会是哪里?'字符串内容组成的。另外,问题前面会加上个数字编号和英文的点。

选项的内容,就是从 options 中提取的每个列表元素,不过在生成答案时,需要对这个选项列表再进行一下遍历,在每个选项前面按顺序加上字母 A、B、C、D。

请你补全下面代码中的 TODO 部分逻辑,按以上格式打印出每一道题目内容:

import random

capitals = {'北京市':'北京', '天津市':'天津', '上海市':'上海', '重庆市':'重庆',
            '河北省':'石家庄', '山西省':'太原', '陕西省':'西安', '山东省':'济南', 
            '河南省':'郑州', '辽宁省':'沈阳', '吉林省':'长春', '黑龙江省':'哈尔滨',
            '江苏省':'南京', '浙江省':'杭州', '安徽省':'合肥', '江西省':'南昌', 
            '福建省':'福州', '湖北省':'武汉', '湖南省':'长沙', '四川省':'成都', 
            '贵州省':'贵阳', '云南省':'昆明', '广东省':'广州', '海南省':'海口', 
            '甘肃省':'兰州', '青海省':'西宁', '台湾省':'台北', '内蒙古自治区':'呼和浩特', 
            '新疆维吾尔自治区':'乌鲁木齐', '西藏自治区':'拉萨', '广西壮族自治区':'南宁', 
            '宁夏回族自治区':'银川', '香港特别行政区':'香港', '澳门特别行政区':'澳门'}

provinces = list(capitals.keys())

def generate_options(index):
    # generate_options函数,根据输入的省份编号index,生成对应的答案选项。

    # 1. 从capitals中获取index这个编号对应的省份的省会名字,存到right_answer变量中。
    right_answer = capitals[provinces[index]]

    # 2. 从capitals中获取所有城市名字,并剔除正确的答案,构建错误省会名组成的列表,
    # 存到变量wrong_answer中。
    wrong_answer = list(capitals.values())
    del wrong_answer[index]

    # 3. 从wrong_answer中随机挑选3个名字,并与right_answer一同组成由4个元素组成的选项
    wrong_answer = random.sample(wrong_answer, 3)
    current_options = [right_answer] + wrong_answer

    # 将选项打乱
    random.shuffle(current_options)

    # 用列表结构返回第index个省的答案选项
    return current_options

options = []
for i in range(len(capitals)):
    # 根据编号i,创建每道题的选项,并把选项列表添加到options中
    option = generate_options(i)
    options.append(option)

for i in range(len(options)):
    # TODO,按格式打印题目及选项

看下老师的答案:

for i in range(len(options)):
    # TODO,按格式打印题目及选项
    print('{}. {}的省会是哪里?'.format(i+1, provinces[i]))
    for j in range(4):
        print('{}. {}'.format('ABCD'[j], options[i][j]))
    print('\n')

for i in range(len(options)) 的每个循环过程中,打印每道题目。使用 i+1provinces[i] 分别填充到格式字符串 '{}. {}的省会是哪里?' 的两个花括号中,就按照循环中的 i值正确填入了编号及省份的名字。

在这之后,再使用内部循环 for j in range(4): 打印每一个选项内容。

由于 j 是从 0 到 3 的整数值,通过 'ABCD'[j] 这样的方式就能分别取到字符串 'ABCD' 的每个元素,就是 'A'、'B'、'C'、'D'。它们分别作为选项的编号。

options[i][j] 则是取出 options 这个二维列表中,第 i 个子列表中的第 j 个选项,这便是题目中候选城市的名字。

既然是生成试卷,光打印在输出终端中是不够的,我们需要生成一个文件。

现在你来试试,将上面代码中的打印到输出终端的逻辑,修改成写文件,生成一个完整的试题文件。

import random

capitals = {'北京市':'北京', '天津市':'天津', '上海市':'上海', '重庆市':'重庆',
            '河北省':'石家庄', '山西省':'太原', '陕西省':'西安', '山东省':'济南', 
            '河南省':'郑州', '辽宁省':'沈阳', '吉林省':'长春', '黑龙江省':'哈尔滨',
            '江苏省':'南京', '浙江省':'杭州', '安徽省':'合肥', '江西省':'南昌', 
            '福建省':'福州', '湖北省':'武汉', '湖南省':'长沙', '四川省':'成都', 
            '贵州省':'贵阳', '云南省':'昆明', '广东省':'广州', '海南省':'海口', 
            '甘肃省':'兰州', '青海省':'西宁', '台湾省':'台北', '内蒙古自治区':'呼和浩特', 
            '新疆维吾尔自治区':'乌鲁木齐', '西藏自治区':'拉萨', '广西壮族自治区':'南宁', 
            '宁夏回族自治区':'银川', '香港特别行政区':'香港', '澳门特别行政区':'澳门'}

provinces = list(capitals.keys())

def generate_options(index):
    # generate_options函数,根据输入的省份编号index,生成对应的答案选项。

    # 1. 从capitals中获取index这个编号对应的省份的省会名字,存到right_answer变量中。
    right_answer = capitals[provinces[index]]

    # 2. 从capitals中获取所有城市名字,并剔除正确的答案,构建错误省会名组成的列表,
    # 存到变量wrong_answer中。
    wrong_answer = list(capitals.values())
    del wrong_answer[index]

    # 3. 从wrong_answer中随机挑选3个名字,并与right_answer一同组成由4个元素组成的选项
    wrong_answer = random.sample(wrong_answer, 3)
    current_options = [right_answer] + wrong_answer

    # 将选项打乱
    random.shuffle(current_options)

    # 用列表结构返回第index个省的答案选项
    return current_options

options = []
for i in range(len(capitals)):
    # 根据编号i,创建每道题的选项,并把选项列表添加到options中
    option = generate_options(i)
    options.append(option)

# TODO,生成试卷,写到文件./试卷.txt 中

以下是老师的答案,执行之后,你可以直接通过下载按钮获取生成的试卷文件哦。

import random

capitals = {'北京市':'北京', '天津市':'天津', '上海市':'上海', '重庆市':'重庆',
            '河北省':'石家庄', '山西省':'太原', '陕西省':'西安', '山东省':'济南', 
            '河南省':'郑州', '辽宁省':'沈阳', '吉林省':'长春', '黑龙江省':'哈尔滨',
            '江苏省':'南京', '浙江省':'杭州', '安徽省':'合肥', '江西省':'南昌', 
            '福建省':'福州', '湖北省':'武汉', '湖南省':'长沙', '四川省':'成都', 
            '贵州省':'贵阳', '云南省':'昆明', '广东省':'广州', '海南省':'海口', 
            '甘肃省':'兰州', '青海省':'西宁', '台湾省':'台北', '内蒙古自治区':'呼和浩特', 
            '新疆维吾尔自治区':'乌鲁木齐', '西藏自治区':'拉萨', '广西壮族自治区':'南宁', 
            '宁夏回族自治区':'银川', '香港特别行政区':'香港', '澳门特别行政区':'澳门'}

provinces = list(capitals.keys())

def generate_options(index):
    # generate_options函数,根据输入的省份编号index,生成对应的答案选项。

    # 1. 从capitals中获取index这个编号对应的省份的省会名字,存到right_answer变量中。
    right_answer = capitals[provinces[index]]

    # 2. 从capitals中获取所有城市名字,并剔除正确的答案,构建错误省会名组成的列表,
    # 存到变量wrong_answer中。
    wrong_answer = list(capitals.values())
    del wrong_answer[index]

    # 3. 从wrong_answer中随机挑选3个名字,并与right_answer一同组成由4个元素组成的选项
    wrong_answer = random.sample(wrong_answer, 3)
    current_options = [right_answer] + wrong_answer

    # 将选项打乱
    random.shuffle(current_options)

    # 用列表结构返回第index个省的答案选项
    return current_options

options = []
for i in range(len(capitals)):
    # 根据编号i,创建每道题的选项,并把选项列表添加到options中
    option = generate_options(i)
    options.append(option)

# TODO,生成试卷,写到文件./tmp/试卷.txt中
with open('./试卷.txt', 'w') as f:
    for i in range(len(options)):
        f.write('{}. {}的省会是哪里?\n'.format(i+1, provinces[i]))
        for j in range(4):
            f.write('{}. {}\n'.format('ABCD'[j], options[i][j]))
        f.write('\n')

老师使用了 Python 中最普通的文件写操作,通过 with open('./试卷.txt', 'w') as f 打开文件之后,将原来逻辑中的 print() 都更换成 f.write(),实现向当前目录中 试卷.txt 文件写入内容的功能。

在实现中还需要多注意的一点是,print() 方法默认会在每一行末尾打印一个换行,而文件的 write() 方法不会,因此在调用 write() 时,传入的字符串末尾都加上了 '\n' 用来实现输出换行的目的。

2.4 创建答案

有了试卷之后,我们期望再生成一份答案文件,以方便批阅的时候使用。

答案的格式类似以下就足够了:
image.png

这个工作相对于生成试卷来说要简单很多,老师给你提供一个思路:

  1. 从 capitals 中确定当前编号所对应的省的省会城市。
  2. 确定这个省会城市在题目选项 options 中的索引编号。
  3. 通过第 2 步中索引编号取出 'ABCD' 中的字母,即是答案选项。

你来试试吧:

import random

capitals = {'北京市':'北京', '天津市':'天津', '上海市':'上海', '重庆市':'重庆',
            '河北省':'石家庄', '山西省':'太原', '陕西省':'西安', '山东省':'济南', 
            '河南省':'郑州', '辽宁省':'沈阳', '吉林省':'长春', '黑龙江省':'哈尔滨',
            '江苏省':'南京', '浙江省':'杭州', '安徽省':'合肥', '江西省':'南昌', 
            '福建省':'福州', '湖北省':'武汉', '湖南省':'长沙', '四川省':'成都', 
            '贵州省':'贵阳', '云南省':'昆明', '广东省':'广州', '海南省':'海口', 
            '甘肃省':'兰州', '青海省':'西宁', '台湾省':'台北', '内蒙古自治区':'呼和浩特', 
            '新疆维吾尔自治区':'乌鲁木齐', '西藏自治区':'拉萨', '广西壮族自治区':'南宁', 
            '宁夏回族自治区':'银川', '香港特别行政区':'香港', '澳门特别行政区':'澳门'}

provinces = list(capitals.keys())

def generate_options(index):
    # generate_options函数,根据输入的省份编号index,生成对应的答案选项。

    # 1. 从capitals中获取index这个编号对应的省份的省会名字,存到right_answer变量中。
    right_answer = capitals[provinces[index]]

    # 2. 从capitals中获取所有城市名字,并剔除正确的答案,构建错误省会名组成的列表,
    # 存到变量wrong_answer中。
    wrong_answer = list(capitals.values())
    del wrong_answer[index]

    # 3. 从wrong_answer中随机挑选3个名字,并与right_answer一同组成由4个元素组成的选项
    wrong_answer = random.sample(wrong_answer, 3)
    current_options = [right_answer] + wrong_answer

    # 将选项打乱
    random.shuffle(current_options)

    # 用列表结构返回第index个省的答案选项
    return current_options

options = []
for i in range(len(capitals)):
    # 根据编号i,创建每道题的选项,并把选项列表添加到options中
    option = generate_options(i)
    options.append(option)

# 生成试卷,写到文件./tmp/试卷.txt中
with open('./tmp/试卷.txt', 'w') as f:
    for i in range(len(options)):
        f.write('{}. {}的省会是哪里?\n'.format(i+1, provinces[i]))
        for j in range(4):
            f.write('{}. {}\n'.format('ABCD'[j], options[i][j]))
        f.write('\n')

# TODO,生成答案,写到文件./tmp/答案.txt中

参考一下老师的答案:

import random

capitals = {'北京市':'北京', '天津市':'天津', '上海市':'上海', '重庆市':'重庆',
            '河北省':'石家庄', '山西省':'太原', '陕西省':'西安', '山东省':'济南', 
            '河南省':'郑州', '辽宁省':'沈阳', '吉林省':'长春', '黑龙江省':'哈尔滨',
            '江苏省':'南京', '浙江省':'杭州', '安徽省':'合肥', '江西省':'南昌', 
            '福建省':'福州', '湖北省':'武汉', '湖南省':'长沙', '四川省':'成都', 
            '贵州省':'贵阳', '云南省':'昆明', '广东省':'广州', '海南省':'海口', 
            '甘肃省':'兰州', '青海省':'西宁', '台湾省':'台北', '内蒙古自治区':'呼和浩特', 
            '新疆维吾尔自治区':'乌鲁木齐', '西藏自治区':'拉萨', '广西壮族自治区':'南宁', 
            '宁夏回族自治区':'银川', '香港特别行政区':'香港', '澳门特别行政区':'澳门'}

provinces = list(capitals.keys())

def generate_options(index):
    # generate_options函数,根据输入的省份编号index,生成对应的答案选项。

    # 1. 从capitals中获取index这个编号对应的省份的省会名字,存到right_answer变量中。
    right_answer = capitals[provinces[index]]

    # 2. 从capitals中获取所有城市名字,并剔除正确的答案,构建错误省会名组成的列表,
    # 存到变量wrong_answer中。
    wrong_answer = list(capitals.values())
    del wrong_answer[index]

    # 3. 从wrong_answer中随机挑选3个名字,并与right_answer一同组成由4个元素组成的选项
    wrong_answer = random.sample(wrong_answer, 3)
    current_options = [right_answer] + wrong_answer

    # 将选项打乱
    random.shuffle(current_options)

    # 用列表结构返回第index个省的答案选项
    return current_options

options = []
for i in range(len(capitals)):
    # 根据编号i,创建每道题的选项,并把选项列表添加到options中
    option = generate_options(i)
    options.append(option)

# 生成试卷,写到文件./tmp/试卷.txt中
with open('./tmp/试卷.txt', 'w') as f:
    for i in range(len(options)):
        f.write('{}. {}的省会是哪里?\n'.format(i+1, provinces[i]))
        for j in range(4):
            f.write('{}. {}\n'.format('ABCD'[j], options[i][j]))
        f.write('\n')

# TODO,生成答案,写到文件./答案.txt中
with open('./答案.txt', 'w') as f:
    for i in range(len(options)):
        f.write('{}.'.format(i+1))
        f.write('{} '.format('ABCD'[options[i].index(capitals[provinces[i]])]))

关键代码就一行:f.write('{} '.format('ABCD'[options[i].index(capitals[provinces[i]])])),但值得仔细再说明一下。

这行代码的三部分分别做了前面所说的三件事情:

1、从 capitals 中确定当前编号所对应省的省会城市:capitals[provinces[i]]
2、确定这个省会城市在题目选项 options 中的索引编号:options[i].index(capitals[provinces[i]])。这里调用了列表 options[i] 的 index 方法,就直接能得到城市 capitals[provinces[i]] 在这个列表里的索引。
3、在第 2 步已经获取了正确选项在备选答案中的编号之后,通过 'ABCD'[第2步的结果] 就得到了正确选项对应的字母。

如果觉得一行代码过于费解,可以按照我们的实现思路拆解成如下三行:

capital = capitals[provinces[i]]
option = options[i].index(capital)
f.write('{} '.format('ABCD'[option]))

好了,看起来大功告成。但作为一个狡猾的天才老师,小象君觉得,为一个班里30位同学生成这同样的一份试卷进行测验,在防作弊上还差点意思。

所以,我们希望生成 30 分答案顺序完全不同的试卷,让作弊无用武之地。

3. 生成答案迥异的试卷

我们在定义试卷生成逻辑的时候,使用的 generate_options() 函数在每次生成答案选项时,都是随机抽取的备选答案,同时,也使用了 random.shuffle() 函数打乱答案选项的顺序。

在每次生成试卷时,试题顺序相同,但答案选项是不相同的。

因此,我们想要生成 30 份答案不同的试卷,只要按上面的方式循环生成 30 遍,每次将试卷和答案存储到不同的文件里就可以了。

为了更顺畅的构建循环生成试卷的逻辑,我们先对以上代码做些整理,将每轮生成试卷和答案文件的代码整理成一个独立的函数。

3.1 试卷生成逻辑整理为函数

import random

capitals = {'北京市':'北京', '天津市':'天津', '上海市':'上海', '重庆市':'重庆',
            '河北省':'石家庄', '山西省':'太原', '陕西省':'西安', '山东省':'济南', 
            '河南省':'郑州', '辽宁省':'沈阳', '吉林省':'长春', '黑龙江省':'哈尔滨',
            '江苏省':'南京', '浙江省':'杭州', '安徽省':'合肥', '江西省':'南昌', 
            '福建省':'福州', '湖北省':'武汉', '湖南省':'长沙', '四川省':'成都', 
            '贵州省':'贵阳', '云南省':'昆明', '广东省':'广州', '海南省':'海口', 
            '甘肃省':'兰州', '青海省':'西宁', '台湾省':'台北', '内蒙古自治区':'呼和浩特', 
            '新疆维吾尔自治区':'乌鲁木齐', '西藏自治区':'拉萨', '广西壮族自治区':'南宁', 
            '宁夏回族自治区':'银川', '香港特别行政区':'香港', '澳门特别行政区':'澳门'}

provinces = list(capitals.keys())

def generate_options(index):
    # generate_options函数,根据输入的省份编号index,生成对应的答案选项。

    # 1. 从capitals中获取index这个编号对应的省份的省会名字,存到right_answer变量中。
    right_answer = capitals[provinces[index]]

    # 2. 从capitals中获取所有城市名字,并剔除正确的答案,构建错误省会名组成的列表,
    # 存到变量wrong_answer中。
    wrong_answer = list(capitals.values())
    del wrong_answer[index]

    # 3. 从wrong_answer中随机挑选3个名字,并与right_answer一同组成由4个元素组成的选项
    wrong_answer = random.sample(wrong_answer, 3)
    current_options = [right_answer] + wrong_answer

    # 将选项打乱
    random.shuffle(current_options)

    # 用列表结构返回第index个省的答案选项
    return current_options

def generate_paper(index):
    options = []
    for i in range(len(capitals)):
        # 根据编号i,创建每道题的选项,并把选项列表添加到options中
        option = generate_options(i)
        options.append(option)

    # 生成试卷,写到文件./tmp/试卷_index.txt中
    with open('./tmp/试卷_{}.txt'.format(index), 'w') as f:
        for i in range(len(options)):
            f.write('{}. {}的省会是哪里?\n'.format(i+1, provinces[i]))
            for j in range(4):
                f.write('{}. {}\n'.format('ABCD'[j], options[i][j]))
            f.write('\n')

    # 生成答案,写到文件./tmp/答案_index.txt中
    with open('./tmp/答案_{}.txt'.format(index), 'w') as f:
        for i in range(len(options)):
            f.write('{}.'.format(i+1))
            f.write('{} '.format('ABCD'[options[i].index(capitals[provinces[i]])]))

generate_paper(0)

在上面的代码中,老师将生成试卷的操作整理到了函数 generate_paper() 中,函数接受一个参数index,用作试卷的编号。

在生成试题和答案文件的时候,会在文件名后加上 _index 区分不同的试卷以及答案。

比如在代码的最后一句,调用 generate_paper(0),便生成了试卷 _0.txt 和答案 _0.txt 两个文件。

接下来,请你在以下代码的 TODO 后补全逻辑,分别为 30 名学生准备答案选项完全不同的 30 份试卷。

3.2 生成 30 份试卷

import random

capitals = {'北京市':'北京', '天津市':'天津', '上海市':'上海', '重庆市':'重庆',
            '河北省':'石家庄', '山西省':'太原', '陕西省':'西安', '山东省':'济南', 
            '河南省':'郑州', '辽宁省':'沈阳', '吉林省':'长春', '黑龙江省':'哈尔滨',
            '江苏省':'南京', '浙江省':'杭州', '安徽省':'合肥', '江西省':'南昌', 
            '福建省':'福州', '湖北省':'武汉', '湖南省':'长沙', '四川省':'成都', 
            '贵州省':'贵阳', '云南省':'昆明', '广东省':'广州', '海南省':'海口', 
            '甘肃省':'兰州', '青海省':'西宁', '台湾省':'台北', '内蒙古自治区':'呼和浩特', 
            '新疆维吾尔自治区':'乌鲁木齐', '西藏自治区':'拉萨', '广西壮族自治区':'南宁', 
            '宁夏回族自治区':'银川', '香港特别行政区':'香港', '澳门特别行政区':'澳门'}

provinces = list(capitals.keys())

def generate_options(index):
    # generate_options函数,根据输入的省份编号index,生成对应的答案选项。

    # 1. 从capitals中获取index这个编号对应的省份的省会名字,存到right_answer变量中。
    right_answer = capitals[provinces[index]]

    # 2. 从capitals中获取所有城市名字,并剔除正确的答案,构建错误省会名组成的列表,
    # 存到变量wrong_answer中。
    wrong_answer = list(capitals.values())
    del wrong_answer[index]

    # 3. 从wrong_answer中随机挑选3个名字,并与right_answer一同组成由4个元素组成的选项
    wrong_answer = random.sample(wrong_answer, 3)
    current_options = [right_answer] + wrong_answer

    # 将选项打乱
    random.shuffle(current_options)

    # 用列表结构返回第index个省的答案选项
    return current_options

def generate_paper(index):
    options = []
    for i in range(len(capitals)):
        # 根据编号i,创建每道题的选项,并把选项列表添加到options中
        option = generate_options(i)
        options.append(option)

    # 生成试卷,写到文件./tmp/试卷_index.txt中
    with open('./tmp/试卷_{}.txt'.format(index), 'w') as f:
        for i in range(len(options)):
            f.write('{}. {}的省会是哪里?\n'.format(i+1, provinces[i]))
            for j in range(4):
                f.write('{}. {}\n'.format('ABCD'[j], options[i][j]))
            f.write('\n')

    # 生成答案,写到文件./tmp/答案_index.txt中
    with open('./tmp/答案_{}.txt'.format(index), 'w') as f:
        for i in range(len(options)):
            f.write('{}.'.format(i+1))
            f.write('{} '.format('ABCD'[options[i].index(capitals[provinces[i]])]))

# TODO,生成30份试卷

这一定难不倒你,答案非常简单:

# TODO,生成30份试卷
for i in range(30):
    generate_paper(i)

如果你想为更多的学生生成试卷,所作的操作就是改一下上面这个循环条件的数字,繁琐的试卷和答案生成工作,计算机都为你代劳了。

4. 本关总结

在办公自动化课程的第一关中,使用一个试卷生成案例,我们共同体验了在一个看似繁琐的场景中,如何将需求进行抽象,使用 Python 指挥计算机完成重复劳动的过程。

本关并没有使用太多 Python 基础以外的附加功能,旨在让你体会到,办公自动化并不是使用种类繁多花样百出的工具完成特别高大上的工作。

它需要我们立足实际的业务需求,在详细分析业务的处理流程之后,使用最合适的方法快速搞定任务。

本关使用的主要 Python 知识包括:循环(这是让计算机解决繁琐任务常借助的工具)、列表、随机处理包 random、文件处理。

在之后持续深入的自动化课程内容中,这些基本的处理方法也依然会时常出现。

将本关的知识要点做个简单总结,如以下框图所示。别忘了,还有课后练习等着你。

image.png

AI悦创·推出辅导班啦,包括「Python 语言辅导班、C++辅导班、算法/数据结构辅导班、少儿编程、pygame 游戏开发」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践等。QQ、微信在线,随时响应!V:Jiabcdefh

在这里插入图片描述

AI悦创·推出辅导班啦,包括「Python 语言辅导班、C++辅导班、算法/数据结构辅导班、少儿编程、pygame 游戏开发」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践等。QQ、微信在线,随时响应!V:Jiabcdefh
AI悦创 » 01-自动化工具的构建-天才老师防作弊

1 评论

发表评论