PDF文库 - 千万精品文档,你想要的都能搜到,下载即用。

点击下载:广州商学院首批广东省“十四五”职业教育规划教材推荐项目资料.pdf

I have no tears to cry.244 页 17.237 MB下载文档
点击下载:广州商学院首批广东省“十四五”职业教育规划教材推荐项目资料.pdf点击下载:广州商学院首批广东省“十四五”职业教育规划教材推荐项目资料.pdf点击下载:广州商学院首批广东省“十四五”职业教育规划教材推荐项目资料.pdf点击下载:广州商学院首批广东省“十四五”职业教育规划教材推荐项目资料.pdf点击下载:广州商学院首批广东省“十四五”职业教育规划教材推荐项目资料.pdf点击下载:广州商学院首批广东省“十四五”职业教育规划教材推荐项目资料.pdf
当前文档共244页 2.88
下载后继续阅读

点击下载:广州商学院首批广东省“十四五”职业教育规划教材推荐项目资料.pdf

广 州 商 学 院 文 件 广商校字〔2023〕20 号 广州商学院关于推荐首批广东省“十四五” 职业教育规划教材项目的报告 广东省教育厅: 根据《广东省教育厅关于组织开展首批“十四五”广东省 职业教育规划教材评选工作的通知》要求,我校限额申报 1 项 教材。经学院申报,学校审核,公示无异议后,现决定推荐周 维柏老师主编的《Python 程序设计》参加首批广东省“十四五” 职业教育规划教材评选。 附件: 1.首批广东省“十四五”职业教育规划教材申报表 2.推荐汇总表 广州商学院 2023 年 4 月 25 日 广州商学院综合办公室 - 2 - 2023 年 4 月 25 日印发 目 第1章 1.1 1.2 1.3 1.4 Python 概述 /1 Python 语言概述 /1 1.1.1 Python 语言发展史 /1 1.1.2 Python 语言特点 /1 1.1.3 Python 的应用领域 /2 Python 开发环境搭建 /3 1.2.1 Python 的安装 /3 1.2.2 交互式解释器——IPython /5 1.2.3 内置的 IDLE 开发环境 /6 1.2.4 PyCharm 集成开发环境 /7 建立及运行 Python 程序 /9 1.3.1 使用 IDLE 开发环境 /9 1.3.2 使用 PyCharm 建立及运行 Python 程序 / 10 Python 使用帮助 / 11 1.4.1 Python 交互式帮助系统 / 11 1.4.2 Python 帮助文档 / 12 习题 第2章 / 13 Python 程序基础语法 2.1 程序书写规范 2.2 2.3 录 / 14 / 14 2.1.1 Python 程序中的语句规范 / 14 2.1.2 Python 代码块及缩进规则 / 15 2.1.3 Python 代码中的注释 / 15 标识符和关键字 / 16 2.2.1 标识符 / 16 2.2.2 关键字 / 16 基本输入和输出 / 17 1 Python 程序设计 2.4 2.5 2.6 2.3.1 基本输入 / 17 2.3.2 基本输出 / 17 变量和赋值语句 / 18 2.4.1 变量的命名与赋值 / 18 2.4.2 赋值语句 / 18 2.4.3 提升训练 / 20 Python 基本数据类型 / 21 2.5.1 整型 / 21 2.5.2 浮点型 / 21 2.5.3 复数类型 / 22 2.5.4 布尔类型 / 23 2.5.5 类型转换函数 / 23 基本运算符和表达式 / 23 2.6.1 算术运算符 / 23 2.6.2 比较运算符 / 24 2.6.3 逻辑运算符 / 25 2.6.4 位运算 / 25 2.6.5 运算符优先级 / 26 2.6.6 提升训练 / 27 习题 第3章 Python 中的字符串 / 31 3.1 字符串的表示 / 31 3.2 转义字符及用法 / 32 3.3 字符串操作符 / 33 3.4 字符串索引及切片 / 33 3.5 字符串处理函数 / 34 3.6 字符串与数字 / 37 3.7 格式化字符串 / 38 3.8 习题 2 / 27 3.7.1 使用 % 操作符格式化字符串 / 38 3.7.2 format() 方法格式化字符串 / 39 提升训练 / 41 / 41 目 第4章 Python 流程控制语句 录 / 43 4.1 顺序结构 / 43 4.2 选择结构 / 44 4.2.1 单分支选择结构 / 44 4.2.2 双分支选择结构 / 45 4.2.3 多分支选择结构 / 46 4.3 4.2.4 if 语句的嵌套 / 47 4.2.5 / 49 提升训练 循环结构 / 49 4.3.1 for 循环 / 50 4.3.2 while 循环 / 51 4.3.3 循环的嵌套 / 52 4.3.4 Python 3 循环结构中 else 用法 / 54 4.3.5 pass、break 和 continue / 55 4.3.6 提升训练 / 57 4.4 异常处理 / 57 4.4.1 异常概述 / 57 4.4.2 常用异常类 / 58 4.4.3 异常处理基本结构 / 58 4.4.4 抛出异常 / 61 4.4.5 自定义异常 / 63 习题 第5章 / 63 Python 的组合数据类型 / 67 5.1 Python 的组合数据类型概述 / 67 5.2 列表 / 68 5.3 5.2.1 列表的基本特点和操作 / 68 5.2.2 列表常用方法 / 70 5.2.3 Python 的列表生成式 / 73 5.2.4 提升训练 / 73 元组 / 75 5.3.1 元组的基本特点和操作 / 75 5.3.2 元组常用方法 / 78 3 Python 程序设计 5.3.3 元组和列表的转换 / 78 5.3.4 提升训练 / 79 5.4 集合 5.5 / 80 5.4.1 集合基本特点和操作 / 80 5.4.2 集合的运算 / 83 字典 / 85 5.5.1 字典基本特点和操作 / 85 5.5.2 字典常用方法 / 87 5.5.3 提升训练 / 90 习题 第6章 6.1 / 93 Python 文件操作 文件 / 96 6.1.1 文件的概述 / 96 6.1.2 文件的打开和关闭 / 97 6.1.3 读 / 写文本文件 / 98 6.1.4 读 / 写二进制文件 / 103 6.1.5 目录操作 / 104 6.1.6 提升训练 / 107 6.2 读 / 写 CSV 文件 / 109 6.2.1 CSV 文件的概念 / 109 6.2.2 读 CSV 文件数据 / 110 6.2.3 将数据写入 CSV 文件 / 112 6.2.4 提升训练 / 114 6.3 Python 文件数据组织的维度 / 116 6.3.1 数据组织基本概念 / 116 6.3.2 一维数据 / 116 6.3.3 二维数据 / 117 习题 第7章 7.1 / 118 函数及模块 函数 7.1.1 4 / 96 / 123 / 123 函数的定义和调用 / 123 目 7.2 7.3 录 7.1.2 函数参数 / 124 7.1.3 函数的返回值 / 127 7.1.4 函数参数传递 / 128 7.1.5 / 129 提升训练 变量的作用域 / 129 7.2.1 Python 局部变量 / 130 7.2.2 Python 全局变量 / 131 7.2.3 global 语句 / 132 7.2.4 提升训练 / 133 特殊函数 / 134 7.3.1 匿名函数 lambda / 134 7.3.2 高阶函数 / 136 7.3.3 闭包和递归函数 / 139 7.3.4 提升训练 / 142 7.4 模块 / 143 7.4.1 模块的导入 / 143 7.4.2 模块的搜索顺序 / 144 7.4.3 模块的 _ _name_ _ 属性 / 145 7.5 包 / 145 7.6 库的应用 / 146 7.6.1 Python 标准库 / 146 7.6.2 Python 第三方库简介及安装 / 153 7.6.3 jieba 库的应用 / 155 7.7 提升训练 习题 第8章 8.1 8.2 / 157 / 159 面向对象程序设计 / 162 面向对象程序设计概述 / 162 8.1.1 面向对象程序设计概念 / 162 8.1.2 类和对象 / 163 8.1.3 面向对象程序设计特点 / 163 创建类和对象 / 164 8.2.1 创建类 / 164 8.2.2 创建对象 / 165 5 Python 程序设计 8.2.3 提升训练 8.3 属性 8.4 8.5 附录 / 166 8.3.1 成员属性和类属性 / 166 8.3.2 公有属性和私有属性 / 167 8.3.3 提升训练 / 168 方法 / 168 8.4.1 成员方法 / 168 8.4.2 类方法和静态方法 / 169 8.4.3 构造方法和析构方法 / 170 8.4.4 提升训练 / 171 继承 / 171 8.5.1 单继承 / 171 8.5.2 多继承 / 172 8.5.3 方法重写 / 173 8.5.4 提升训练 / 174 8.6 自定义类使用举例 / 174 习题 / 176 全国高等学校计算机水平考试二级 ——Python 程序设计考试大纲及样题 6 / 165 / 178 一、考试目标与要求 / 178 二、考试内容 / 178 三、考试方式 / 180 四、考试题目类型 / 180 五、考试环境 / 180 六、考试样题 / 180 第1章 Python 概述 学习目标 (1)了解 Python 3 程序设计语言的发展历程。 (2)掌握 Python 3 开发环境的搭建。 (3)了解 Python 3 程序的运行方式。 (4)掌握 IDLE 的使用方法。 (5)通过本章学习,让学生体会“万事开头难,只要肯攀登”,培养学生不畏艰难困阻、 勇于探索的精神。 章节导学 Python 是一种面向对象的解释型高级程序设计语言,是当前应用最为广泛的计算机设计语言之 一。Python 语言具有开源、简单易学、跨平台、成熟的程序包资源库和扩展性好等特点,Python 的 应用领域非常广泛。本章主要介绍 Python 语言概述、开发环境搭建和程序运行方式等内容。 1.1 1.1.1 Python 语言概述 Python 语言发展史 Python 语言是一种面向对象的、解释型的高级程序设计语言。由 Guido van Rossum 于 1989 年 底开始设计与开发,经过 30 多年的发展,已经成为最受欢迎的程序设计语言之一。Python 语言 的灵感来源于 ABC 语言,是一种脚本解释语言。Python 是纯粹的开源软件,其源代码和解释器均 遵循 GNU 通用公共许可证协议。 1991 年,Python 第一个公开发行版 0.9 问世,2000 年出现 2.0 版本,2008 年诞生 3.0 版本。 Python 3.0 不再向后兼容。Python 官方已决定于 2020 年停止对 Python 2.7 的支持,从而使开发人 员有充裕的时间过渡到 Python 3.x。 1.1.2 Python 语言特点 Python 语言功能强大,具有很多区别其他程序设计语言的个性化特点。 1. 跨平台且开源 Python 可以跨平台运行,能满足同时在 Linux、Windows 及 MacOS 等操作系统中的运行要求。 同时 Python 的使用和分发是完全免费的,具有大量的开放源代码。 1 Python 程序设计 2. 简单易学 Python 提供了简洁的语法和强大的内置工具,阅读一个良好的 Python 程序,即使是在 Python 语法要求非常严格的大环境下,给人的感觉也像是在读英语段落一样简洁明了。换句话说, Python 语言最大的优点之一,是其具有伪代码的特质,它可以让学者在开发 Python 程序时,专注 于解决问题,而不是搞明白语言本身。 3. 解释型语言 Python 解释器把源代码转换成字节码的中间形式,然后翻译成计算机使用的机器语言并执行。 4. 面向对象 Python 是一种面向对象的语言,可以用类实现抽象和封装。类模块支持多态、继承等操作。 5. 拥有丰富的程序包资源库 Python 以其 Packag 索引(Python Package Index,PyPI)为后盾,拥有丰富的 Python 模块 和脚本资源库,Python 标准库可以帮助 Python 完成各种工作,如正则表达式、文档生成、数据库、 电子邮件以及其他系统有关的代码。此外,Python 还有高质量的第三方库,如 jieba、turtle 等。 6. 优异的扩展性 Python 语言具有优异的扩展性,体现在它可以集成 C、C++、Java 等语言编写的代码,通过 接口和函数库等方式将它们整合在一起。此外,Python 语言本身提供了良好的语法和执行扩展接口, 能够整合各类程序代码。 1.1.3 Python 的应用领域 Python 的应用领域非常广泛,几乎所有大中型互联网企业都在使用 Python 完成各种各样的任 务,例如国外的 Google、Youtube、Dropbox,国内的百度、新浪、搜狐、腾讯、网易、知乎、豆瓣、 汽车之家、美团等。 Python 的应用领域主要有下面几个方面: 1.Web 应用开发 Python 经常被用于 Web 开发,尽管目前 PHP、JS 依然是 Web 开发的主流语言,但 Python 上 升 势 头 更 劲。 尤 其 随 着 Python 的 Web 开 发 框 架 逐 渐 成 熟( 如 Django、flask、TurboGears、 web2py、Tornado、WebPy、Bottle 等),程序员可以更轻松地开发和管理复杂的 Web 程序。全 球最大的搜索引擎 Google,在其网络搜索系统中就广泛使用 Python 语言。另外豆瓣网也是使 用 Python 实现的,全球最大的视频网站 Youtube 及 Dropbox(一款网络文件同步工具)也都是用 Python 开发的。 2. 自动化运维 在很多操作系统中,Python 是标准的系统组件,大多数 Linux 发行版及 NetBSD、OpenBSD 和 Mac OS X 都集成了 Python,可以在终端下直接运行 Python。有一些 Linux 发行版的安装器使用 Python 语言编写,例如 Ubuntu 的 Ubiquity 安装器、Red Hat Linux 和 Fedora 的 Anaconda 安装器等。 另外,Python 标准库中包含了多个可用来调用操作系统功能的库。例如,使用 pywin32 这个软件 包,能够访问 Windows 的 COM 服务以及其他 Windows API;使用 IronPython,能够直接调用 .NET Framework。通常情况下,Python 编写的系统管理脚本,无论是可读性,还是性能、代码重用度以 及扩展性方面,都优于普通的 Shell 脚本。 3. 人工智能领域 人工智能是非常火的一个研究方向,而 Python 在人工智能领域内的机器学习、神经网络、深 度学习等方面,都是主流的编程语言。可以这么说,基于大数据分析和深度学习发展而来的人工 智能,其本质上已经无法离开 Python 的支持了。 2 第 1 章 Python 概述 4. 网络爬虫 Python 语言很早就用来编写网络爬虫。Google 等搜索引擎公司大量地使用 Python 语言编写网 络爬虫。从技术层面上讲,Python 提供了很多服务于编写网络爬虫的工具,例如 urllib、Selenium 和 Beautiful Soup 等,还提供了一个网络爬虫框架 Scrapy。 5. 科学计算 自 1997 年, 美 国 国 家 航 空 航 天 局(National Aeronautics and Space Administration,NASA) 就大量使用 Python 进行各种复杂的科学运算。和其他解释型语言(如 Shell、JS、PHP)相比, Python 在数据分析、可视化方面有相当完善和优秀的库,例如 NumPy、SciPy、Matplotlib、Pandas 等, 这可以满足 Python 程序员编写科学计算程序。 6. 游戏开发 很多游戏使用 C++ 编写图形显示等高性能模块,而使用 Python 或 Lua 编写游戏的逻辑。和 Python 相比,Lua 的功能更简单、体积更小;而 Python 则支持更多的特性和数据类型。比如说, 国际上著名的游戏 Sid Meier's Civilization 就是使用 Python 实现的。 1.2 Python 开发环境搭建 在学习 Python 开发之前,需要先搭建 Python 开发环境。Python 是跨平台的开发工具,可 以在多个操作系统上进行编程。用 Python 语言编写好的程序也可以在不同系统上运行。由于 Python 是解释型编程语言,所以需要一个解释器,才能运行编写的代码。Python 解释器除自带 的 IDLE 之外,还有很多风格不同、功能各异的集成开发环境。本书只介绍 IPython 和 PyCharm 两款 Python 的集成开发环境。IPython 轻便小巧、设置简单,交互性较强,适合对过程数据需 要及时掌控的小型开发;PyCharm 功能强大,项目管理等方面比较专业,适合中型以上项目开发。 1.2.1 Python 的安装 根据 Windows 版本(64 位 /32 位)从 Python 官网下载对应的版本。本节安装以 Python 3.11.0 a2 为例。 安装的步骤如下: Step 01:进入官网(https://www.python.org/downloads/),下载相应版本。 Step 02:选择“Python”→“Downloads”→“Windows”,打开新的界面,在下方列表中寻找“Python 3.11.0 a2-Nov.5,2021”→“Downloads Windows embeddable package(64-bit)”,单击进行下载,如图 1-1 所示。 图 1-1 在官网选择相应版本进行下载 3 Python 程序设计 Step 03:双击下载好的安装文件 Python-3.11.0a2-amd64.exe,使其运行,进行安装,如图 1-2 所示。 图 1-2 Python 安装界面 在选择安装路径时,单击选中图 1-2 所示界面下方的“Add Python 3.11 to PATH”复选框,直 接添加用户变量,后续不用再添加。 Step 04:安装成功后会出现“Setup was successful”提示窗口,如图 1-3 所示,单击“Close” 按钮关闭窗口。 图 1-3 安装成功界面 安装好后,可以单击计算机桌面左下角的“开始”按钮,打开“开始”菜单找到“运行”命令, 打开“运行”对话框,输入“cmd”,单击“确定”按钮,进入命令提示符窗口,输入“python”, 按【Enter】键,输出运行成果。如果 Python 安装成功,窗口中那个将会显示 Python 的相关信息, 如图 1-4 所示。 Step 04:编写一个 Python 代码,测试是否能够成功运行。在命令提示符窗口中出现“>>>” 符号后,输入“print("hello,python!")”,按【Enter】键后,若输出“hello,python!”,说明程序可 正常运行,如图 1-5 所示。 4 第 1 章 1.2.2 图 1-4 Python 的相关信息 图 1-5 Python 代码运行测试 Python 概述 交互式解释器——IPython IPython 是一款高效的 Python 交互式解释器,它与 Python 自带的 Python Shell 基本功能类似, 它还具有更多更强大的功能,能够增强用户的交互式体验;提供了代码自动补完、自动缩进、高 亮显示、执行 Shell 命令等非常有用的特性。IPython 具有一个非常实用的功能,就是【Tab】键自 动补全。IPython 还具有对象自省、历史机制、内嵌的源代码编辑等特点。IPython 不仅可以调用 所有标准 Python 函数,还可以调用 Python Shell 内置函数。 1. IPython 安装 下面介绍在 Windows 系统下安装 IPython。首先打开 cmd.exe,即命令提示符窗口,输入“pip install ipython”,按【Enter】键即可进行安装。安装成功后的界面如图 1-6 所示。 图 1-6 IPython 安装成功界面 5 Python 程序设计 2. IPython 的使用 在命令提示符窗口中输入“ipython”命令,按【Enter】键即可进行代码测试,结果如图 1-7 所示, 表示 IPython 环境搭建。 图 1-7 IPython 的使用 3.【Tab】键补全 在 Shell 中输入表达式时,只要按【Tab】键,当前命令控件中任何与输入的字符串相匹 配的变量(对象、函数等)就会被找出来。如果是 Python 代码,可以补齐变量名,用 . 之后, 可以提示对象包含的属性和方法。如输入字母“p”后,按【Tab】键即可得到如图 1-8 所示 的结果。 图 1-8 【Tab】键补全 1.2.3 内置的 IDLE 开发环境 安装 Python 时,会自动安装一个 IDLE。它是一个 Python Shell,也就是一个通过输入文本与 程序交互的途径。程序开发人员可以利用 Python Shell 与 Python 交互。 Step 01:从“开始”菜单中可以找到 Python 3.11 文件夹,在文件夹下单击“IDLE (Python 3.11 64-bit)”命令,如图 1-9 所示,可以打开“IDLE Shell 3.11.0a2”窗口进行编程。 Step 02:在打开的“IDLE Shell 3.11.0a2”窗口中输入“我要学好 python!”,按【Enter】键后 会输出运行结果“’我要学好 Python!’”,如图 1-10 所示。 6 第 1 章 图 1-9 “开始”菜单中查找 IDLE Python 概述 图 1-10 “IDLE Shell 3.11.0a2”窗口 (Python 3.11 64-bit)命令 1.2.4 PyCharm 集成开发环境 PyCharm 是一个集成开发环境,带有一整套可以帮助用户在使用 Python 语言开发时提高其效 率的工具。它具有方便调试、语法高亮、项目管理、代码跳转、智能提示等特点。一般使用社区版(免 费版)。这里我们下载的版本是 PyCharm-Community-2020.1.3。下载安装步骤如下: Step 01:从官网下载 PyCharm,链接为 http://www.jetbrains.com/pycharm/download/#section= windows), 版本选择界面如图 1-11 所示,单击 Community 版本下的“Download”按钮,进入下载界面。 图 1-11 官网下载 Community 版本 PyCharm Step 02:下载完毕后,找到安装包,双击安装文件进行安装。 Step 03:进入图 1-12 所示对话框,提示开始安装,单击“Next”按钮。 Step 04:进入“Choose Install Location”对话框,选择默认的安装路径,或单击“Browse…”按钮, 如图 1-13 所示,打开对话框选择安装位置。选择完毕后单击“Next”按钮。 7 Python 程序设计 图 1-12 PyCharm Community 安装开始对话框 图 1-13 “Choose Install Location”对话框 Step 05: 进 入“Installation Options” 对 话 框, 在 PyCharm 安 装 选 项 选 框 的“Create Desktop Shortcut” (创建桌面快捷方式)选项中,根据电脑操作系统进行选择,系统 64 位就选 64-bit。在“Create Associations”选项中勾选“.py”复选框,关联 py 文件,这样双击 py 文件将以 PyCharm 打开,如图 1-14 所示。选择完毕后单击“Next”按钮。 Step 06:进入“Choose Start Menu Folder”对话框,保持默认选择,单击“Install”按钮进行安装, 如图 1-15 所示。 图 1-14 “Installation Options”对话框 图 1-15 “Choose Start Menu Folder”对话框 Step 07:安装成功后,桌面会显示 PyCharm 的快捷方式,如图 1-16 所示。 图 1-16 8 PyCharm 的快捷方式 第 1 章 1.3 Python 概述 建立及运行 Python 程序 Python 程序有两种运行方式:程序文件运行和交互式运行。 1. 程序文件运行 程序文件是包含一系列 Python 语句的源代码文件,文件扩展名通常为 py。在 Windows 的命 令提示符窗口中,可使用 Python.exe 来执行 Python 程序文件。由 Python 解释器将 py 文件翻译 成字节码文件,再由 Python 虚拟机(Python Virtual Machine,PVM)逐条翻译、执行字节码中的 Python 语句。 2. 交互式运行 Python 还可以通过交互方式运行。在 Windows 系统的命令提示符窗口中运行 Python.exe,可 进入 Python 的交互环境。在其中输入 Python 语句后,按【Enter】键即可运行。 1.3.1 使用 IDLE 开发环境 IDLE 是 Python 自带的编程工具,以 Python 3.11 为例,可在 Windows 的“开始”菜单中选择“Python 3.11\IDLE”命令,启动交互环境开始编程,如图 1-17 所示。 图 1-17 使用 IDLE 开发界面 IDLE 中可以先创建文件,编写代码,再运行程序。在 IDLE 交互环境中选择“File”→“New” 命令新建一个 py 文件,编写代码,如图 1-18 所示,然后按【Ctrl+S】组合键保存程序文件为 test1.py,然后按【F5】键运行程序,运行结果显示在 IDLE 交互环境中,如图 1-19 所示。 图 1-18 图 1-19 IDLE 中创建文件 运行结果界面 9 Python 程序设计 1.3.2 使用 PyCharm 建立及运行 Python 程序 Step 01:双击桌面上的 PyCharm 快捷方式打开程序。 Step 02: 打开 PyCharm 之后,进入“Welcome to PyCharm”对话框,单击“Create New Project”按钮, 如图 1-20 所示,新建一个项目。 图 1-20 在“Welcome to PyCharm”对话框中新建项目 Step 03: 在弹出的新建项目对话框中,可以指定项目名称、项目所在路径,以及解析器,单 击“Create”按钮确认创建。 Step 04: 在 新 建 的 项 目 窗 口 中, 右 击 左 侧 的 项 目 名“untitled”, 弹 出 快 捷 菜 单, 选 择 “New”→“Python File”命令,如图 1-21 所示,新建一个 Python 文件。输入代码,对其进行测试, 若能成功运行,说明设置成功,如图 1-22 所示。 图 1-21 10 选择“New”→“Python File”命令 第 1 章 图 1-22 1.4 1.4.1 Python 概述 py 文件运行界面——设置成功 Python 使用帮助 Python 交互式帮助系统 在编写和运行 Python 程序时,我们可能会遇到瓶颈,并需要得到帮助。我们可以通过 Python 解释器获取帮助。如果想知道一个对象更多的信息,可以调用 help() 函数。另外还有一些有用的 方法,dir() 函数会显示该对象的大部分相关属性名,还有对象 _ _ doc _ _ 会显示其相对应的文档 字符串。 help() 函数是 Python 的一个内置函数,可帮助我们获得某个类、函数、变量、模块等的文档。 help() 函数的语法如下: >>> help([object]) 在解释器交互界面,不传参数调用函数时,将激活内置的帮助系统,并进入帮助系统,如图 1-23 所示。在帮助系统内部输入模块、类、函数等名称时,将显示其使用说明,图 1-24 所示为 输入 print 后的结果。输入 quit,将退出内置帮助系统,并返回交互界面。 图 1-23 help() 函数 11 Python 程序设计 图 1-24 print 方法使用说明 在解释器交互界面,传入参数调用函数时,将查找参数是否是模块名、类名、函数名。如果 是,将显示其使用说明。如要查看 Python 中的关键字,可以在运行 Python 的命令提示符下输入 “help("keywords")”或在运行 Python 的 IDLE 中输入“help("keywords")”皆可得到关键字。 dir() 函数是 Python 的一个内置函数,可以帮助我们获取该对象的大部分相关属性。语法 如下: >>>dir([object]) 不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、 方法列表。如果参数包含方法 _ _dir_ _(),该方法将被调用。如果参数不包含 _ _dir_ _(),该方法将 最大限度地收集参数信息。 例如,要获得当前模块的属性列表,直接输入“dir()”,结果如图 1-25 所示。 图 1-25 输入“dir()”获得当前模块的属性列表 如果要查看列表的方法,输入“dir([ ])”,结果如图 1-26 所示。 图 1-26 1.4.2 输入“dir([ ])”查看列表的方法 Python 帮助文档 打开已安装好的 Python 自带的 IDLE 界面参考图 1-17,单击菜单栏的“Help”,选择“Python Docs F1”,或直接按【F1】键,即可调用帮助文档,界面如图 1-27 所示。 12 第 1 章 图 1-27 Python 概述 Python 帮助文档界面 习 题 一、选择题 1. 在项目中,可以使用 (A)File (B)Directory 2. 在项目中不可以创建 (A)Word 创建新的 Python 文件。 (C)Python Package (D)Python File (C)Directory (D)Python File 文件。 (B)Python Package 3. 若某计算机是 64 位操作系统,那么安装 Python 时应选择 (A)32-bit (B)64-bit 。 (C)128-bit (D)都可以 二、填空题 1. Python 3 中的字符默认使用 编码。 2. Python 3 语句的源代码文件的文件扩展名通常是 3. Python 3 自带的编译工具是 。但不同编译器也有不同的扩展名。 ,它包含交互环境和源代码编辑器。 13 第2章 Python 程序基础语法 学习目标 (1)掌握 Python 程序书写规范。 (2)掌握标识符和关键字。 (3)掌握变量和赋值语句。 (4)掌握 Python 的基本数据类型。 (5)掌握基本运算符和表达式。 (6)能够严谨地按照 Python 语言的语法规范编写程序,会将实际生活问题描述成计算机语 言,构建成表达式解决实际问题。让学生意识到语法规范和表达的重要性,培养学生在学习、生 活和工作中精益求精的态度。 章节导学 Python 语言中程序的书写规范将影响到程序是否可以正常运行,以及是否可以按照我 们的意愿解决相关的实际生活问题。一个完整的计算机程序代码包括了相关的程序编写规 则,为此我们需要掌握标识符、关键字、变量 fa 及赋值语句,还有 Python 语言中的基本数 据类型,掌握由数据和运算符构成的表达式书写方式等。本章将介绍 Python 3 的基础语法 及相关操作。 2.1 程序书写规范 计算机需要根据程序设计人员编写的程序执行任务,因此在编写代码时不仅要避免出现程序 歧义这样的逻辑错误,还要避免由于没有遵守 Python 语言的语法规范而导致的语法错误。由于每 一种计算机程序设计语言都会有自己的书写规范,编译程序或解释程序就会将符合编写规范的程 序转换为计算机能够理解和执行的机器语言程序,从而帮助我们解决现实生活中的实际问题。下 面我们将从 Python 的编程语法开始,逐步学习 Python 语言。 2.1.1 Python 程序中的语句规范 在 Python 中,通常一条语句占一行,但是在遇到较长的语句时,我们可以使用语句续行符号, 即续行的方法,将一条语句写在多行中。Python 中的续行有两种方法: (1)使用续行符号“\”。 14 第 2 章 Python 程序基础语法 (2)使用括号续行,包括小括号()、中括号 [ ]、大括号 { }。 【例 2-1】使用续行符号“\”示例。 if y%400==0 or \ y%100!=0 and y%4==0: print(" 是闰年! ") else: print(" 不是闰年! ") # 使用续行符号 "\" 需要特别注意的是,续行符号“\”之后不能出现任何其他符号,包括空格和注释等。 【例 2-2】使用括号续行示例。 if(y%400==0 or y%100!=0 and y%4==0): print(" 是闰年! ") else: print(" 不是闰年! ") # 使用括号进行续行,括号中的空格和注释、换行符都会被忽略 在 Python 中,除了语句续行,还可以使用分号分隔语句,从而将多行语句写在一行。对于重 复格式的语句,写在一行会显得更加简洁。 【例 2-3】使用分号分隔语句示例。 x=1 y=5 if x<5: x+=1; y-=1; print(x,y) if x>=5: x-=1; y+=1; print(x,y) 2.1.2 # 使用分号,将多条语句写在一行 Python 代码块及缩进规则 在 Java 和 C 语言中,使用大括号来表示代码块,而在 Python 语言中,使用缩进(空格)来 表示代码块。通常在 if 语句、for 循环、while 循环、函数、类等定义中都会使用到代码块,而语 句末尾的冒号表示代码块的开始。 【例 2-4】代码块示例。 for i in range(5): print("hello world!") 在包含代码的嵌套时,要特别注意同级代码块的缩进应该保持一致,否则容易导致缩进错误 或者得到与预想不一致的结果。 【例 2-5】同级代码块的缩进示例。 x=3 if x>3: print(" 数据大于 3 ! ") else: for i in range(5): print(" 数据小于等于 3 ! ") print(x) 2.1.3 Python 代码中的注释 计算机程序中的注释用于说明和备注程序的运算和功能,适当地注释语句便于增加程序的可 读性,方便团队和个人对代码进行调试和纠错。在 Python 语言中,有两种注释的方法。 15 Python 程序设计 (1)单行注释:使用“#”符号进行单行注释。 (2)多行注释:使用三个单引号或三个双引号注释多行内容。 注释的语句仅仅是说明的文字,不会随程序运行。 【例 2-6】单行注释示例。 # 我的第一个代码 print("hello world!") # 打印 hello world ! 上述代码中,“#”右边的文字会被当成说明的文字,不是真正要执行的程序。也就是在代 码中使用“#”时,其右边的任何数据都会被当成注释的内容,会被解释器忽略。单行注释可以 独占一行,也可以放在一行代码的后面,注意使用至少一个空格与语句分开。 【例 2-7】多行注释示例。 """ 程序功能:计算圆的面积 ( 保留小数点后 2 位 ) 设计时间:2021 年 11 月 8 日星期一 编写人员:小明、小华 """ R=eval(input(" 请输入圆的半径:")) area=3.14*R**2 print(" 圆的面积为:{:.2f}".format(area)) 在上述代码中,需要对多行内容进行注释。可以使用 3 对单引号或者 3 对双引号。引号包括 的内容为注释内容。但实际上,有时候我们也会将多行注释用“#”来注释,相当于多个单行注 释的操作。 2.2 2.2.1 标识符和关键字 标识符 标识符在计算机语言中为允许作为名字的有效字符串集合。就像是人的一个名字,它的作用 就是作为变量、函数、类、模块及其他对象的名称。 命名 Python 标识符的一些规则如下: (1)名字必须以字母、下画线及 Unicode 编码中的非英文字母(汉字)开头。注意数字不能 作为标识符开头。 (2)首字符之后的字符可以为上述的字符及数字的组合,名字长度没有限制。 (3)标识符的命名不能与 Python 关键字一样。 除了以上命名规则,在给标识符命名的时候应尽量做到“见名知义”,这样能够提高代码的 可读性,便于后期对代码进行维护。而且需要注意的是,Python 中标识符对大小写敏感,例如: Abc 和 abc 是两个不同的标识符。 2.2.2 关键字 关键字是程序设计语言中作为命令或常量等的单词。关键字不允许作为变量或其他标识符使 用。Python 中的保留字和关键字见表 2-1。 16 第 2 章 表 2-1 Python 程序基础语法 保留字和关键字 and await assert async as break True class continue def del elif else try except False finally for from global while if import in is lambda None with nonlocal not or pass raise return yield 【例 2-8】查看保留字示例。 >>> import keyword >>> keyword.kwlist 基本输入和输出 2.3 2.3.1 基本输入 Python 的内置函数 input() 用于取得用户的输入数据,其语法格式如下: 变量 =input(' 提示字符串 ') 其中,变量是 input() 函数返回的字符串数据。 提示信息:其参数可以省略。当程序执行到 input() 函数时会暂停执行,等待用户输入,用户 输入的全部数据均作为输入内容。 需要注意的是,如果要得到整数或小数,可以使用 eval() 函数得到表达式的值,也可以使用 int() 或 float() 函数进行转换。eval() 函数会将字符串对象转化为有效的表达式,再参与求值运算, 返回计算结果。 【例 2-9】input() 函数的使用示例。 >>> name=input(' 请输入你的名字:') 请输入你的名字:Marry >>> x=eval(input(' 请输入 x 的值:')) 请输入 x 的值:2 >>> y=eval(input(' 请输入 y 的值:')) 请输入 y 的值:3.0 >>> x+y 5.0 2.3.2 基本输出 Python 3 使用 print() 函数输出数据,其基本语法格式如下: print(value, …, sep=' ', end='\n', file=sys.stdout, flush=False) 其中各参数说明如下: Value:指要输出的值,后面的“...”表示可以放进很多个值,只是数量不定。 sep=' ':指将输出的值利用这个参数链接一起,默认的是空格,当然也可以设置为其他的链 接符号。 end=' ':指在输出结束的时候以这个字符结束,默认为 '\n',也就是换行符。这个在实现不换 17 Python 程序设计 行输出时会用到。 flush:表示是否实时将数据写入 file。若值为 False,则表示只有打印结束时才真正写入到文件, 若值为 True 则表示实时写入。默认值为 False。 【例 2-10】print() 函数的使用示例。 >>> x,y,z='a','b','c' >>> print(x,y,z) a b c >>> print(x,y,z,sep='&') # 设置 print() 函数的输出分隔符为 & a&b&c >>> print(x);print(y) # 2 个 print() 语句,默认分行显示 a b >>> print(x,end='--');print(y,end='--');print(z) # 设置 end 参数,用 '--' 分隔,不换行 a--b--c 变量和赋值语句 2.4 2.4.1 变量的命名与赋值 变量的命名有一定的规则,合法的变量名可以由字母、下画线以及 Unicode 编码中的非英文 字母(汉字)开头,后面的字符组成可以是上述任意字符也可以包括数字。例如:userName、 StudentName、_abc、abc123 等都是合法的变量名,2abc、name-1 等为不合法的变量名。当使用 不合法变量名时,会出现语法错误。 【例 2-11】使用不合法变量名示例。 >>> 2a=1 file "", line 1 2a=1 ^ SyntaxError: invalid syntax 2.4.2 赋值语句 我们将对变量进行赋值的一行代码称为赋值语句。在 Python 中,我们使用“=”号表示赋值符号。 程序执行赋值语句时,会将等号右边的表达式进行计算后的结果赋给等号左边的变量。赋值语句 的基本语法格式如下: < 变量 >=< 表达式 > 【例 2-12】赋值语句 >>> a=1+2 >>> a==4 # 赋值语句,将 1+2 的结果赋值给变量 a # 不是赋值语句,连续的两个等号是比较运算符,判断 a 的值与 4 是否相等 在 Python 中,Python 将所有的数据都当作对象来处理。对于一个简单的赋值语句,例如 a=3,该语句执行时会按顺序执行以下 3 个步骤:①创建对象整数 3;②检查变量 a 是否已经存在, 如果存在则建立变量 a 与整数 3 的引用,不存在则先建立变量 a;③建立变量 a 与整数 3 的引用。 因此我们在使用变量时,应该理解以下几点: (1)变量在第一次使用时被创建,后续代码如果再次出现该变量,则直接使用。 18 第 2 章 Python 程序基础语法 (2)变量没有数据类型的概念。因此在 Python 中,变量使用之前不需要声明,数据类型只 决定了对象在内存中的存储方式。 (3)变量引用对象,使用变量之前应该先为变量赋初值。 【例 2-13】赋值示例。 >>> a=b+2 # 没有为 b 赋初值,出现错误 Traceback (most recent call last): File "", line 1, in NameError: name 'b' is not defined >>> a=1 # 第一次为 a 赋值,创建变量 a,引用对象 1 >>> print(a+2) # 变量 a 被对象 1 替代,执行 print(1+2) >>> 3 赋值语句用于将数据赋值给变量,Python 中支持多种格式的赋值语句,包括:简单赋值、 序列赋值、多目标赋值及增强赋值。简单赋值可用于为单个变量赋值。序列赋值和多目标赋值 都可以为多个变量赋值,序列赋值可以给多个变量赋不同的值,多目标赋值可以给多个变量赋 相同的值。 【例 2-14】简单赋值、序列赋值、多目标赋值示例。 >>> a=1 >>> b,c=1,2 >>> b 1 >>> c 2 >>> x=y=z=10 >>> (x,y,z) (10,10,10) # 简单赋值 # 序列赋值,直接为变量 b,c 赋值 # 多目标赋值,直接为变量 x,y,z 赋值 10 需要注意的是,序列赋值会同时运算赋值号右侧的所有表达式,并一次性将结果同时分别赋 值给左边对应的变量。因此对于序列赋值,赋值号右边的数值个数要与左边变量个数一致,否则 会出现错误。 【例 2-15】序列赋值示例。 >>> a,b=2 # 赋值号右边的数值个数为 1,变量个数为 2,出现错误 Traceback (most recent call last): File "", line 1, in TypeError: cannot unpack non-iterable int object 小技巧:对于序列赋值的一个小应用,是可以直接互换变量的值的。 【例 2-16】将变量 a、b 中的数据实现交换。 >>> a,b=1,2 >>> a,b=b,a >>> a 2 >>> b 1 在 Python 中,除了以上 3 种赋值方式,还提供了增强赋值的操作。通过增强赋值能够直接更 新变量中的数据,往往会配合选择结构或循环结构进行使用。Python 中的增强赋值运算符包括: +=、-=、*=、/=、%=、**=、//=、&=、|=、^=、>>=、<<= 等。以 a=1,b=2 为例,Python 中各个 19 Python 程序设计 增强赋值运算符功能及示例见表 2-2。 表 2-2 增强赋值运算符功能及示例 赋值运算符 功能描述 示 例 += 加法赋值运算符 a+=b,等效于 a=a+b,a 的结果为 3 -= 减法赋值运算符 a-=b,等效于 a=a-b,a 的结果为 -1 *= 乘法赋值运算符 a*=b,等效于 a=a*b,a 的结果为 2 /= 除法赋值运算符 a/=b,等效于 a=a/b,a 的结果为 0.5 %= 求余赋值运算符 a%=b,等效于 a=a%b,a 的结果为 1 **= 求幂赋值运算符 a**=b,等效于 a=a**b,a 的结果为 1 //= 取整赋值运算符 a//=b,等效于 a=a//b,a 的结果为 0 &= 按位与赋值运算符 a&=b,等效于 a=a&b,a 的结果为 0 |= 按位或赋值运算符 a|=b,等效于 a=a|b,a 的结果为 3 ^= 按位异或赋值运算符 a^=b,等效于 a=a^b,a 的结果为 3 >>= 右移赋值运算符 a>>=b,等效于 a=a>>b,a 的结果为 0 <<= 左移赋值运算符 a<<=b,等效于 a=a<>> a=1 >>> b=2 >>> a += b >>> a 3 >>> b 2 表 2-2 中的增强赋值运算符 -=、&=、|=、^= 还支持对集合运算,分别对应集合的 4 种运算(差 集、交集、并集、补集),并完成集合的更新。 【例 2-18】集合交集运算示例。 >>> a={1,2,3} >>> b={2,3,4} >>> a &= b >>> a {2,3} 2.4.3 # 等价于 a=a&b,集合 a 与集合 b 进行交集运算,并更新集合 a 提升训练 【训练内容】 编写程序,统计在 1~100 中,能够被 3 整除的数据个数。 【实现设计思路】 创建变量 count1,使用 for 循环遍历整数序列 1~100,使用 if 语句进行判断数据能否被 3 整除。 20 第 2 章 Python 程序基础语法 当数据能够被 3 整除时,变量 count1 计数加 1。最后将 count1 打印出来。 【参考代码】 count1=0 for i in range(1,101): if i%3==0: count1+=1 print("100 以内能够被 3 整除的数据个数有 :{} 个 ".format(count1)) Python 基本数据类型 2.5 数据类型决定了程序如何存储和处理数据,它是 Python 中最基础的一种变量类型,也是 Python 程序实现计算功能的基础。和大多数编程语言一样,数据类型的赋值和运算都很直观,我 们也可以通过内置的 type() 函数来查看变量所指对象的数据类型。Python 中可支持的常用基本数 据类型有整型(int)、浮点型(float)、复数类型(complex)、布尔类型(bool)等。 2.5.1 整型 Python 中的整型对应于数学中的整数概念,类似 -3、-2、-1、0、1、2 这样的一些数,我们 把其称为整型数据。整型数据通常是不带小数点的整数,但是可以有正负符号。在 Python 2.x 中, 整型有两种,分别为 int 和 long。在 Python 3.x 中,整型舍弃了 long 类型,所以在 Python 3.x 中对 整型是没有大小限制的,只要计算机的内存足够大,那么整数的取值范围几乎包括全部整数,而 且整数永远都是精确的。整型(int)和布尔类型(bool)都属于整数类型。 在 Python 中,可以通过 4 种不同的进制数来表示整型数据,分别为:二进制、八进制、十进制、 十六进制。其中二进制以“0b”或“0B”开头,后面跟二进制数字 0 或 1。八进制以“0o”或“0O” 开头,后面跟八进制数字 0~7。十六进制以“0x”或“0X”开头,后面跟十六进制数字 0~9、A(a) ~F(f)。进制数示例见表 2-3。 表 2-3 进制数示例 进制数 开头符号 示例进制数 二进制 0b 或 0B 0b111、0B101 八进制 0o 或 0O 0o123、0O111 十六进制 0x 或 0X 0xFFA、0X123 默认情况下,我们使用十进制表示数据,但是在一些特殊的场景,我们也可以将十进制转换 为其他进制数。 下面是一些整数类型数据:1000,0X1ABC,0b1001,0O234,-98。 2.5.2 浮点型 Python 中的浮点型对应于数学中的实数概念,类似 -3.1、-2.0、1.4、3.14、2.88e2、1.33E-3 这样的一些数,都属于浮点型。浮点型是双精度的,每个浮点型数据占 8 个字节(即 64 位), 能表示的数据范围为:-10308~10308。浮点型数据通常是带小数点的数据类型,由整数部分和小数 部分组成,也可以使用科学计数法表示,使用科学计数法时,E 或者 e 表示基数 10,后面的整数 表示指数。指数的正负使用“+”或“-”表示,其中“+”号可以省略。 21 Python 程序设计 如前面的 2.88e2 表示的是浮点数 2.88×102 ,1.33E-3 表示的是浮点数 1.33×10-3。 需要特别注意的是,浮点数类型小数点前的 0 和小数点后的 0 都可以省略。 【例 2-19】小数点前的 0 和小数点后的 0 示例。 >>> 1. 1.0 >>> .2 0.2 2.5.3 # 省略小数点后的 0 # 省略小数点前的 0 复数类型 在 Python 中,复数类型对应于数学中的复数概念,类似 3+4j、-3+4.5j、-3-4.6J 等这样的一些数, 都属于复数类型。Python 从 1.4 版本起便加入了复数类型。复数由实部和虚部组成,其中实部是 一个实数,虚部是一个实数与 j(或 J)的组合。 Python 语言中的复数是多数计算机语言所没有的数据类型,其具有以下特点: (1)复数由实部和虚部组成,一般形式为:real+imag j 或(real+imag J)。 (2)实部 real 和虚部 imag 都是浮点类型,可以通过“对象名 .real”或“对象名 .imag”查看。 (3)虚部必须由后缀 j 或者 J。 需要强调的是,如果要表示一个复数,那就必须得有由实数和 j 组成的虚部。比如 1j 或 2j 都表示复数。而且要注意表示虚部的实数如果是 1 也不可以省略,比如 4+1j 不可以写成 4+j。 复数的创建:可以直接将复数赋值给变量,如 a=4+5j,也可以使用内建函数 omplex(real,imag) 创建复数,如 a=complex(4,6)。如果参数 imag 缺省,则 imag 使用默认值 0,如 complex(6) 可得到 复数 6+0j。 【例 2-20】复数创建示例。 >>> a=3+2j # 直接使用复数赋值给变量 a >>> a (3+2j) >>> a=complex(3,2) # 使用 complex() 函数创建复数,参数为 2 个的情况 >>> a (3+2j) >>> a=complex(3) # 使用 complex() 函数创建复数,参数为 1 个的情况 >>> a (3+0j) >>> a=2+j # 当虚部为 1 时,省略 1 会出现错误 Traceback (most recent call last): File "", line 1, in NameError: name 'j' is not defined 另外,需要特别注意:复数的实部和虚部都是浮点数类型。 【例 2-21】获取复数的实部和虚部示例。 >>> a=3+2j >>> a.real 3.0 >>> a.imag 2.0 22 # 直接使用复数赋值给变量 a 第 2 章 2.5.4 Python 程序基础语法 布尔类型 Python 中支持布尔类型。布尔类型是特殊的整型,其值只有两个,分别为 False 和 True。如 果将布尔值进行数值运算,那么 True 会被当成整型 1,False 会被当成整型 0。 在 Python 中,每个对象都具有对应的布尔值(True 或 False),进而可以进行以下 3 种运算(比 如与其他数据类型进行 and、or、not 运算)或者条件判断(比如用在 if 语句或者 while 语句中)。 在 Python 中,运算结果为以下情况会被认为是 False:False、整数 0、浮点数 0.0、复数 0.0+0.0j、 空字符串 ""、表示空值的 None、空集合 set( )、空元组 ( )、空字典 { }、空列表 [ ]。其他的值都为 True。 布尔类型的值可以直接进行四则运算,比如 True+1 的结果等于 2,False-2 等于 -2。 【例 2-22】布尔类型参与运算示例。 >>> a="abc"*(True+1) >>> a 'abcabc' >>> b=False-100 >>> b -100 2.5.5 类型转换函数 在 Python 中,若用户想要将从终端获取的数据作为数值进行计算,那就必须先进行类型转换, 比如使用 input() 函数获得的数据为字符串类型,如果要进行数值运算,那么应该先转为整型或者浮 点数类型,这时就会用到类型转换函数。Python 中常用的类型转换函数描述及示例见表 2-4。 表 2-4 函数格式 述 使用示例 int(x,[,base]) 将字符串类型转换为整型 int("123")、int("123",8), 结果分别为:123、83 float(x) 将字符串类型转换为浮点数类型 float("1"),结果为 1.0 complex(real,imag) 将按给定参数值创建复数 complex(1,4),结果为 1+4j str(x) 将数字转换为字符串类型 str(520),结果为 "520" eval(str) 执行字符串表达式,返回计算结果,如示例返回 68 eval("12"+"56"),结果为 1256 chr(x) 返回参数对应的 ASCII 字符 chr(97),结果为 "a" ord(C) 返回参数对应的编码 ord("a"),结果为 97 2.6 2.6.1 描 常用的类型转换函数描述及示例 基本运算符和表达式 算术运算符 Python 中的算术运算符有:+(加)、-(减)、*(乘)、/(除)、//(整除)、%(求余) 和 **(幂运算)。这些运算符都是双目运算符,由两个操作数和一个运算符组成表达式。下面以 a=2,b=5 为例,介绍各个运算符的功能和示例结果,具体见表 2-5。 23 Python 程序设计 表 2-5 运算符 述 使用示例 + 将两个操作数相加,获取两个操作数的和 a+b,结果为 8 - 将两个操作数相减,获取两个操作数的差 a-b,结果为 -3 * 将两个操作数相乘,获取两个操作数的积 a*b,结果为 10 / 将两个操作数相除,获取两个操作数的商 a/b,结果为 0.4 // 将两个操作数相除,获取两个操作数商的整数部分 a//b,结果为 0 % 将两个操作数相除,获取余数 a%b,结果为 2 ** 将两个操作数进行幂运算 a**b,结果为 32 2.6.2 描 运算符说明及示例 比较运算符 比较运算符能够将两个操作数构成一个表达式。这种表达式通常用于条件判断,结合 if 语句 和 while 语句使用。判断的结果只能是 True 或 False。比较运算符的操作数可以是对象也可以是表 达式,Python 中的比较运算符有:==( 等于 )、!=(不等于)、>(大于)、<(小于)、>=(大于 等于)、<=(小于等于)。下面以 a=4,b=5 为例,说明各个比较运算符的功能和相关示例结果, 见表 2-6。 表 2-6 运算符 比较运算符描述及使用示例 描 述 使用示例 == 比较两个操作数是否相等,相等则为真,否则为假 a==b,结果为 False != 比较两个操作数是否不相等,不相等则为真,否则为假 a!=b,结果为 True > 判断两个操作数大小关系,左边操作数大于右边操作数,则为真,否则为假 a>b,结果为 False < 判断两个操作数大小关系,左边操作数小于右边操作数,则为真,否则为假 a= 判断两个操作数大小关系,左边操作数大于或等于右边操作数,则为真,否则为假 a>=b,结果为 False <= 判断两个操作数大小关系,左边操作数小于或等于右边操作数,则为真,否则为假 a<=b,结果为 True 【例 2-23】比较运算符示例。 >>> a,b,c=1,5,3 >>>a>> a>=b False >>>a>b>>a=c True >>> a!=b>c True # 等价于 a>b and b=c # 等价于 a!=b and b>c 需要注意的是,Python 3 中具有链式比较的机制,比较运算符可以连着使用。 24 第 2 章 Python 程序基础语法 【例 2-24】比较运算符链式写法示例。 >>>3<5<4 False 对于 3 < 5 < 4,一般的思路是先计算 3 < 5,结果是 True,再计算 True < 4,结果应该是 True。 但 Python 有链式比较的机制,3 < 5 < 4 并非以 (3 < 5) < 4 的方式计算,而是等价于 3 < 5 and 5 < 4。 比较运算符还可以根据表示的区间,配合逻辑运算符使用。比如表示 x<0 或 x>100 的情况, 可以写成 x<0 or x>100。 有了链式比较,可以更加清晰又简洁地写出判断条件了。 2.6.3 逻辑运算符 Python 中提供了 3 种逻辑运算符,分别为:and(逻辑与)、or(逻辑或)、not(逻辑非)。 前两个为二目运算符,逻辑非为一目运算符。3 种逻辑运算符的含义为: 假如 a,b 是参加运算的 2 个逻辑量,那么: (1)a and b 的含义是当 a 和 b 都为 True 时,表达式结果为 True,否则为 False。 (2)a or b 的含义是当 a 和 b 都为 False 时,表达式结果为 False,否则为 True。 (3)not a 的含义是当 a 为 True 时,表达式结果为 False,当 a 为 False 时,表达式结果为 True。 下面以 a=True,b=False 为例对逻辑运算符进行说明,结果见表 2-7。 表 2-7 运算符 描 逻辑运算符描述及使用示例 述 使用示例 and 逻辑与,两个操作数都为真,结果才为真,否则为假 a and b 表达式结果为 False or 逻辑或,两个操作数至少 一个为真,结果为真,否则为假 a or b 表达式结果为 True not 逻辑非,操作数为真,结果为假,操作数为假,结果为真 not a 表达式结果为 False,not b 表达式结果为 True 【例 2-25】逻辑运算符示例。 >>> a,b=True,False >>> a and b False >>> a or b True >>> not a False 2.6.4 # 与运算 # 或运算 # 非运算 位运算 Python 中的数据都以二进制数的形式存储的。位运算就是直接对整数在二进制位进行相关操 作。例如:9 对应的二进制数是 1001,6 对应的二进制数是 110,那么 6&9 的结果是 0,这是二 进制对应位进行逻辑与运算的结果;6|11 的结果是 15,这是二进制对应位进行逻辑或运算的结果。 Python 中位运算主要还包括:<<(按位左移)、>>(按位右移)、&(按位与)、|(按位或)、^ (按位异或)、~(按位取反)。 下面以 a=6(二进制数为 110),b=3(二进制为 011)为例,将就这几种位运算进行详细介绍, 结果见表 2-8。 25 Python 程序设计 表 2-8 位运算描述及使用示例 运算符 描 述 使用示例 << 按位左移,把一个数的位向左移一定数目 a <<1,结果为 12 >> 按位右移,把一个数的位向右移一定数目 a >>1,结果为 3 b >>1,结果为 1 & 数的按位与 a & b,结果为 2 | 数的按位或 a | b,结果为 7 数的按位异或,即对应位进行异或运算,对应位相异, 结果为 1,对应位相同,结果为 0 ^ ~ 2.6.5 a ^b,结果为 5 ~a,结果为 -7 ~b,结果为 -4 数的按位取反 运算符优先级 在 Python 中,表达式是由变量和运算符按一定的语法形式组成的符号序列。表达式中的多个 运算符被执行是存在先后运算顺序的,即存在运算优先级。那么我们在计算表达式的值时,就应 该按照运算符的优先级别由高到低的次序执行。需要注意的是,在表达式中,我们也可以使用括 号 () 显式地标明运算次序,括号中的表达式会优先被计算。下面将从最高到最低优先级展示所有 运算符,见表 2-9。 表 2-9 优先级 最高 运算符 26 描 述 ** 指数(最高优先级) ~、+、- 按位翻转,一元加号和减号(最后两个的方法名为 +@ 和 -@) *、/、%、// 乘、除、取模和取整除 +、- 加法、减法 >>、<< 右移、左移运算符 & 位运算符,数的按位与 ^、| 位运算符,数的按位异或、按位或 <=、<、>、>= 比较运算符 <>、==、!= 等于运算符 =、%=、/=、//=、-=、+=、*=、 **= 最低 运算符及其优先级 赋值运算符 is、is not 身份运算符 in、not in 成员运算符 and、or、not 逻辑运算符 第 2 章 2.6.6 Python 程序基础语法 提升训练 【训练内容】 编写程序,根据杀人嫌疑人的口供,找出杀人凶手。 侦探在犯罪现场,逮捕了三名杀人嫌疑人,口供如下: A 说:我是凶手。 B 说:C 说谎。 C 说:我不是凶手。 只知其中有一人说谎,凶手只有一个。请问,谁才是凶手? 【设计实现思路】 用整数 1 表示是凶手,整数 0 表示不是凶手,则三名杀人嫌疑人的取值范围为 0 或 1。根据 他们的口供推出的关系见表 2-10。 表 2-10 三名嫌疑人 符 推出的关系 号 口供 关系表达式 A A 我是凶手 A==1 B B C 说谎 C==1 C C 我不是凶手 C==0 根据案例描述的情况,可以得到事实表达式见表 2-11。 表 2-11 事实表达式 事 实 表达式 3 个人只有 1 个说谎 (A==1)+(C==1)+(C==0)==2 有且只有 1 个人是凶手 A+B+C==1 【参考代码】 for a in [0,1]: for b in [0,1]: for c in [0,1]: if (a==1)+(c==1)+(c==0)==2 and (a+b+c==1): print("a=%d,b=%d,c=%d"%(a,b,c)) 习 题 一、选择题 1. 下面代码的输出结果是 。 print(0b01+0b10==3) (A)True (B)False (C)true 2. 关于 Python 的数字类型,以下选项中描述正确的是 (D)false 。 (A)1.0 是整数 (B)复数类型虚部为 1 时,表示为 1+j 27 Python 程序设计 (C)整数类型的数值可以出现小数点 (D)整型有十进制、二进制、八进制和十六进制等表示方式 3. 下面代码的输出结果是 。 x=12 print(type(x)) (A) (C) (B)(D) 4. 下列选项中,Python 不支持的数据类型是 。 (A) (B)(C) (D) 5. 下列选项中,Python 不支持的数据类型是 (A)int (B)char 。 (C)float 6. 关于 Python 语言的浮点数类型,以下选项中描述错误的是 (D)complex 。 (A)Python 语言中的浮点数可以没有小数部分 (B)浮点数类型与数学中实数的概念一致 (C)小数部分可以为 0 (D)浮点数类型表示带有小数的类型 7. 下面代码的输出结果是 。 print(0.1+0.1+0.1==0.3) (A)True (B)False 8. 下面代码的输出结果是 (C)true (D)false (C)true (D)false 。 print(0.3+0.3+0.3!=0.9) (A)True (B)False 9. 下列选项中,不属于浮点类型的是 (A)3.14159 (B)4.10E-8 10. 下面代码的输出结果是 。 (C).5 (D)-1.0+0.0j (C)3 (D)3.0 (C)9.99+0j (D)9.99 。 z=1.2+3j print(z.imag) (A)1.2 (B)1.20 11. 下面代码的执行结果是 。 a=9.99 print(complex(A)) (A)0.999 (B)(9.99+0j) 12. 关于 Python 的复数类型,以下选项中描述错误的是 。 (A)复数的虚数部分通过后缀“J”或者“j”来表示 (B)对于复数 z,可以用 z.real 获得它的实数部分 (C)对于复数 z,可以用 z.imag 获得它的虚数部分 (D)复数类型中实数部分和虚数部分都是整型 13. 每个对象都具有布尔值,下列值为 True 的是 (A)None 28 (B)0 。 (C)5 (D)"" 第 2 章 14. 下面代码的输出结果是 Python 程序基础语法 。 a=True b=False c=2 print(a+b**5-c) (A)True (B)False 15. 下面代码的输出结果是 (C)-1 (D)1 (C)aaa (D)bbb 。 a="" b={} if a and b: print("aaa") else: print("bbb") (A)True (B)False 16. Python 语言中,以下表达式输出结果为 22 的选项是 。 (A)print("2+2") (B)print(2+2) (C)print(eval("2+2")) (D)print(eval("2"+"2")) 17. 假设 a=7,b=2,那么下列运算结果中错误的是 。 (A)a+b 的值是 9 (B)a//b 的值是 3 (C)a%b 的值是 3 (D)a**b 的值是 49 18. 以下选项中描述正确的是 。 (A)条件 1<=6<5 是不合法的 (B)条件 1<=6<5 是合法的,且输出为 True (C)条件 1<=5<6 是合法的,且输出为 False (D)条件 1<=6<5 是合法的,且输出为 False 19. 下面表达式的输出结果为 True 的是 (A)"abc"<"b" (B)"abc"<"a" (C)"python"<"Python" 20. 判断以下代码输出结果为 。 (D)"Ab">"ab" 。 print(4 or 3 and 1) (A)True (B)False 21. 判断以下代码输出的结果 (C)1 (D)4 。 a="python" b="hi,python!" print(a in b) (A)"python" in "hi,python" (B)"python" (C)True 22. 判断以下代码输出的结果 (D)False 。 a="hello" b="world" c="h" print(c not in a+b) 29 Python 程序设计 (A)helloworld (B)"h" not in "helloworld" (C)False (D)True 23. 下列表达式运算结果为 。 print(5**2+7//3) (A)10 (B)27 24. 下列表达式运算结果为 (C)26 (D)125 (C)0 (D)12 。 print(-5%3*6) (A)-12 (B)6 25. 下列表达式运算结果为 。 print(not False and (False or True) ) (A)true (B)false (C)True (D)False 二、填空题 1. 表达式 3**2*5//6%7 的计算结果为 2. 表达式 3<6!=10 的结果为 。 。 3. 表达式 "python" < "Python" 的运算结果为 。 4. 以下代码可以输出 0~100 以内的偶数,请将空格位置的代码补充完整。 for i in range( ): if ==0: print(i,end=" ") 5. 有一个 5 位车牌号码 y,最左 2 位为 17,最后 1 位数为 5,且知道牌号为 69 的倍数,请补 充一下代码,帮忙找出车牌号。 for i in range(10): for j in range(10): y= if ==0: print(" 车牌号码是:%d"%y) 三、编程题 1. 编写程序,计算并联电阻的阻值 R。要求用户输入电阻阻值 R1、R2,计算公式为: 1/R=1/R1+1/R2,显示输出 R 的结果,保留 2 位小数。 2. 编写程序,要求用户输入一个 4 位整数,显示这个数据的各位、十位、百位、千位。例如 输入数据为:1234,则显示:输入数据是 1234,个位:4,十位:3,百位:2,千位 1。 3. 编写程序,检验平台登录过程中,输入密码的长度。当输入密码长度大于等于 6,显示“设 置完成!”,当长度不符合条件时,显示“长度不符合标准!请重新设置!”。 4. 有一天,某一珠宝店被盗走了一块贵重的钻石。经侦破,查明作案人肯定在甲、乙、丙、 丁之中。于是,对这 4 个重大嫌疑人进行审讯。审讯所得到的口供如下: 甲:我不是作案的。 乙:丁是罪犯。 丙:乙是盗窃这块钻石的罪犯。 丁:作案的不是我。 经查实:这 4 个人的口供中只有一个是假的。 请编写代码,帮忙找出珠宝店的钻石到底是被谁偷走的? 30 第3章 Python 中的字符串 学习目标 (1)了解字符串序列类型常用操作。 (2)掌握字符串的索引及切片。 (3)掌握字符串处理函数。 (4)了解输入、输出语句。 (5)掌握格式化字符串方法。 (6)通过讲解字符串切片,不同的设置可以把结果指引到不同的地方,来告诉学生在人生 道路的选择上也是如此,不同的选择会产生不同的结果,让学生明白树立正确的人生观、价值观 的重要性,从而帮助学生能在今后的人生选择中做出正确的决定。 章节导学 计算机经常需要对字符串进行各种操作,因此字符串是每种编程语言必不可少的元素。 字符串也是 Python 中最常用的数据类型。本章将介绍 Python 3 中字符串的表示及相关操作。 3.1 字符串的表示 字符串是一个有序的字符的序列,用于存储和表示基本的文本信息。Python 3 中字符串是由 Unicode 码组成的不可变序列。字符串就是“一串字符”,也就是用引号包裹的任何数据。例如 'Python 好 '、'gcc4501'、'GCCone'。Python 3 要求字符串必须使用引号括起来,可以使用单引号、双引号 或者三引号,只要成对即可。 字符串中的内容几乎可以包含任何字符,英文字符也行,中文字符也行。Python 3 对中文的 支持非常全面。在 Python 3 中,源文件默认使用的是 UTF-8 编码,这样一来,不但可方便地在 源代码的字符串中使用中文,而且变量名也可以使用中文,不需要来回地编码和解码,直接使用 print() 函数即可输出变量的内容。 【例 3-1】中文变量名的使用。 >>> 字符串 1 = 'Python 是一门非常棒的编程语言 ' >>> 字符串 1 Python 是一门非常棒的编程语言 31 Python 程序设计 注意: (1)'' 表示空字符串(空字符串就是字符串里没有内容)。 (2)字符串是不可变对象。 转义字符及用法 3.2 编程时,有时需要在字符串中使用特殊字符,比如在 Python 3 中,单引号(或双引号)是有 特殊作用的,它们常作为字符(或字符串)的标识(只要数据用引号括起来,就认定这是字符或 字符串),有时字符串中包含引号(例如 ' I'm so dumb, I can't even read. '),那该怎么办?为了避 免解释器将字符串中的引号误认为是字符串的“结束”引号,就需要对字符串中的单引号进行转 义,使其在此处取消它本身具有的含义,告诉解释器这就是一个普通字符。Python 3 使用反斜杠(\) 转义字符,因此如要使用单引号,就在单引号前加入转义字符(如: \'),尽管它由 2 个字符组成, 但通常将它看成是一个整体,是一个转义字符。我们已经见过很多类似的转义字符,包括 \'、\"、 \\ 、\t 等。Python 3 不只有以上几个转义字符,常用的转义字符见表 3-1。 表 3-1 Python 支持的转义字符 转义字符 说 \ 在行尾的续行符,即一行未完,转到下一行继续写 \' 单引号 \" 双引号 \0 空 \n 换行符 \r 回车符 \t 水平制表符,用于横向跳到下一制表位 \a 响铃 \b 退格(Backspace) \\ 反斜线 \0dd 八进制数,dd 代表字符,如 \012 代表换行 \xhh 十六进制数,hh 代表字符,如 \x0a 代表换行 【例 3-2】使用转义字符示例。 str_1 = ' 商品名 \t 单价 \t 数量 \t 总价 ' str_2 = ' 钢笔 \t99\t\t3\t\t297' print(str_1) print(str_2) 可以看到如下输出结果: 商品名 钢笔 32 明 单价 99 数量 3 总价 297 第 3 章 Python 中的字符串 Python 3 中的转义字符可以帮助我们在字符串处理中提供特殊的需求,如换行、使用制表键、 使用特殊符号等。但有时,我们并不想让转义字符生效,只想显示字符串原来的意思,比如需要 使用文件路径的时候,会使用很多反斜杠。如果每个反斜杠都用转义字符来写一遍就麻烦了。例 如要输出一个 ' C:\Windows\naaS\tasks\kasK\Wallpaper' 路径,如果直接执行语句 print('C:\Windows\ naaS\tasks\kasK\Wallpaper'),则输出的结果为: C:\Windows aaS asks\kasK\Wallpaper 此时不能让转义字符生效,可以采用两种措施:一种在字符串前面加 r 或 R;另一种再加一 个反斜线。 【例 3-3】不能让转义字符生效示例。 print(r'This is \t') print('This is \\t') # 字符串前面加一个 r 或 R # 再加一个反斜线 输出的结果都为:This is \t 这种在字符串前端使用了 'r' 或 'R' 的字符串成为原生字符串。原生字符串中的每个字符都表 示它本身的含义。 字符串操作符 3.3 为实现字符串的连接、子串的选取等操作,在 Python 3 中提供了一系列字符串的操作符,如 表 3-2 所示。其中 m、n 是两个字符串,m="Hello",n="world"。 表 3-2 操作符 描 字符串操作符 述 实 例 + 字符串连接 m+n 输出结果为 'Helloworld' * 重复输出字符串 m*2 输出结果为 'HelloHello' [] 通过索引获取字符串中字符 m[0] 输出结果为 'H' [:] 截取字符串中的一部分 m[1:4] 输出结果为 'ello' in 成员运算符,如果字符串中包含给定的字符返回 True "e" in m 输出结果为 True not in 成员运算符,如果字符串中不包含给定的字符返回 True "B" not in m 输出结果为 True r/R 原始字符串,所有的字符串都是直接按照字面的意思来使用,没 有转义特殊或不能打印的字符。原始字符串除在字符串的第一个引号 前加上字母“r”(可以大小写)以外,与普通字符串有着几乎完全 相同的语法 r'\n' 输出结果为 \n R'\n' 输出结果为 \n 3.4 字符串索引及切片 字符串可以定义为字符的有序集合,我们可以通过其位置获得其对应的元素。在 Python 3 中, 字符串中的字符是通过索引获取的。 Python 3 字符串有两种索引方式: 33 Python 程序设计 (1)从前往后的正向索引:有 m 个字符长度的字符串,索引值是 0~m-1(从 0 开始标序号的)。 (2)从后往前的负数索引:有 m 个字符长度的字符串,索引值是 -1~-m。 【例 3-4】字符串索引的应用示例。 >>> str = 'Python' >>> str[0] # 索引 0 对应的是第一个字符 'P' >>> str[-1] # 索引 -1 对应的是最后一个字符 'n' >>> str[7] # 下标索引越界了(超过了以上规定的范围),则会报错 Traceback (most recent call last): File "", line 1, in str[7] IndexError: string index out of range 字符串的切片又称分片,切片操作可以从一个字符串中获取子字符串(字符串的一部分)。 具体语法如下: s[star: end: step] 表示返回字符串 s 中从偏移量 star 开始,到偏移量 end 之前的子字符串。star、end 和 step 参数均 可省略,star 默认为 0,end 默认为字符串长度,step 即切片的厚度,默认值为 1。 【例 3-5】字符串切片的应用示例。 >>> str = 'Python' >>> str[1:3] 'yth' >>> str[1:3:2] 'yh' >>> str[1:] 'ython' >>> str[::] 'Python' >>> str[::2] 'Pto' >>> str[:4] 'Pytho' >>> str[::-1] 'nohtyP' >>> str[7:] '' >>> str[:8] 'Python' # 第 2 个位置切到第 4 个位置 # 第 2 个位置切到第 4 个位置,步长为 2 # 第 2 个位置切到最后 # 切出所有的字符 # 从第 1 个位置开始,步长为 2,切出所有的字符 # 从开始切到第 5 个 # 逆序字符串 # 起始位置越界,返回空字符串,系统不会报错 # 结束位置越界,返回整个字符串,系统不会报错 切片时索引如果出现越界,不会报错;起始位置越界,则返回空字符串;结束位置越界,则 返回整个字符串。 3.5 字符串处理函数 字符串作为一种常见的数据类型,Python 3 也为其提供了很多内置处理函数,用于处理在日 常面临的各式各样的字符串处理问题。常用的部分函数见表 3-3~ 表 3-9。 34 第 3 章 表 3-3 Python 中的字符串 字母处理函数 函数名 功能描述 upper() 字符串中字母全部大写 'string'. upper() 结果为:STRING lower() 字符串中字母全部小写 'STRing'.lower() 结果为:string swapcase() 字符串中字母大小写互换 'STRing'.swapcase() 结果为:strING capitalize() 字符串中字母首字母大写,其余小写 'stR In'.capitalize() 结果为:Str in title() 字符串中每个单词的首字母大写 'stR In'.title() 结果为:Str In 表 3-4 实 例 字符串查找替换函数 函数名 功能描述 find() 搜索指定字符串,没有返回 -1 'hello'.find('e') 结果为:1 index() 查找子串第一次出现的位置,找不到会报错 'hello'.index('l') 结果为:2 rfind() 类似 find() 函数,只是从右边开始查找 'hello'.rfind('e') 结果为:4 rindex() 类似 index() 函数,只是从右边开始查找 'hello'.rindex('e') 结果为:3 replace() 字符串替换 'hell'.replace('h','G') 结果为:Gell 表 3-5 实 例 格式相关函数 函数名 功能描述 ljust() 获取固定长度,左对齐,右边不够补齐 'he'.ljust(4,'#') 结果为:he## rjust() 获取固定长度,右对齐,左边不够补齐 'he'.rjust(4,'#') 结果为:##he center() 获取固定长度,中间对齐,两边不够补齐 'he'.center(4,'#') 结果为:#he# zfill() 获取固定长度,右对齐,左边不足用 0 补齐 'he'.zfill(4) 结果为:00he 表 3-6 实 例 字符串判断相关函数 函数名 功能描述 实 例 isupper() 是否全大写 'aB'.isupper() 结果为:False islower() 是否全小写 'ab'.islower() 结果为:True isdigit() 是否全数字 '12'.isdigit() 结果为:True isalpha() 是否全字母 'ab'.isalpha() 结果为:True isalnum() 是否全为字母或数字 'ab123'.isalnum() 结果为:True isspace() 判断字符是否为空格 'a b'.isspace() 结果为:False endswith('end') 是否以 end 结尾 'abe'.endswith('e') 结果为:True startswith('start') 是否以 start 开头 'abe'.startswith('a') 结果为:True 35 Python 程序设计 表 3-7 字符串的计算函数 函数名 功能描述 len() 返回字符串的长度 len('ab') 结果为:2 count() 统计指定的字符串出现的次数 'how are you'.count('o') 结果为:2 表 3-8 实 例 字符串的拆分合并函数 函数名 功能描述 split(s,num) 通过指定分隔符对字符串进行切片,并返回 分割后的字符串列表 'this is string example'.split() 结果为:['this', 'is', 'string', 'example'] join() 将字符串以指定的字符(分隔符)连接生成 一个新的字符串 '-'.join('hello') 结果为:h-e-l-l-o 表 3-9 实 例 字符串中空格删除函数 函数名 功能描述 实 例 strip() 把头和尾的空格去掉 ' go to '.strip() 结果为:go to lstrip() 把左边的空格去掉 ' go to '.lstrip() 结果为:go to rstrip() 把右边的空格去掉 ' go to '.rstrip() 结果为: go to 【例 3-6】字母处理函数的应用示例。 >>> str = "PYTHON" >>> str.lower() # 将字符串中的所有大写字母转换为小写字母 'python' >>> str = "Python" >>> str.upper() # 将字符串中的所有小写字母转换为大写字母 'PYTHON' >>> str = "Python" >>> str.swapcase() # 将字符串中字母大小写互换 'pYTHON' >>> str = "python" >>> str.capitalize() # 将字符串中字母首字母大写,其余小写 'Python' >>> str = "i love you" >>> str.title() # 将字符串中每个单词的首字母变成大写 'I Love You' 【例 3-7】字符串查找替换函数的应用示例。 >>> str = "ABACAD" >>> str.find("A") 0 >>> str.find("A",1) 2 >>> str.find("A",3,5) 4 36 # 从下标 0 开始,查找在字符串里第一个出现的子串,返回结果为 0 # 从下标 1 开始,查找在字符串里第一个出现的子串,返回结果为 2 #从下标3开始, 第5个结束, 查找在字符串里第一个出现的子串, 返回结果为 4 第 3 章 >>> str.find("E") -1 >>> str.replace("A","E") 'EBECED' >>> str.replace("A","E",2) 'EBECAD' Python 中的字符串 # 查找不到返回 -1 # 将字母 A 全部替换为字母 E # 将前 2 个为字母 A 替换为字母 E 【例 3-8】字符串拆分合并函数的应用示例。 >>> str = "3w.gor ly.test.com.cn" >>> str.split() # 使用默认分隔符分割字符串 ['3w.gor', 'ly.test.com.cn'] >>> str.split(".") # 指定分隔符为 '.',进行分割字符串 ['3w', 'gor ly', 'test', 'com', 'cn'] >>> str.split(".",1) # 指定分隔符为 '.',并且指定切割次数为 1 次 ['3w', 'gor ly.test.com.cn'] >>> str.split(".",-1) # 这种分割等价于不指定分割次数 ['3w', 'gor ly', 'test', 'com', 'cn'] 3.6 字符串与数字 Python 3 中有时需要对字符串与数字混合操作,例如运算操作、相互转换和拼接操作。 【例 3-9】字符串与数字运算操作。 >>> 3*"ABC" 'ABCABCABC' >>> 10*"#" '##########' Python 3 内置字符串与数字的相互转换函数见表 3-10。 表 3-10 字符串与数字的相互转换函数 函数名 功能描述 实 例 str() 将对象转换为字符串 str(3.15) 结果为 '3.15' chr() 将一个整数转换为一个字符 chr(65) 结果为 'A' ord() 将一个字符转换为它的整数值 ord("a") 结果为 97 【例 3-10】字符串与数字相互转换及拼接示例。 >>> chr(65) 'A' >>> ord("a") 97 >>> "source="+5 # 字符串直接与数字拼接会报错 Traceback (most recent call last): File "", line 1, in "source="+5 TypeError: can only concatenate str (not "int") to str >>> "source="+str(5) # 把数字转换为字符串后再拼接 'source=5' 37 Python 程序设计 3.7 格式化字符串 字符串是一种不可变的数据类型,但程序运行输出的结果往往以一种灵活的和可编辑的字 符串形式呈现,这就需要控制字符串的输出格式,输出格式化的字符串。Python 3 中字符串格式 化有两种方法:占位符(%)和 format() 方法。占位符方式在 Python 2.x 中用的比较广泛,随着 Python 3.x 的使用越来越广,format 方式的使用更加广泛。 3.7.1 使用 % 操作符格式化字符串 格式化字符串时,Python 使用一个字符串作为模板。模板中有格式符,这些格式符为真实值 预留位置,并说明真实数值应该呈现的格式。Python 3 用一个 tuple(元组)将多个值传递给模板, 每个值对应一个占位符。例如: "My name is %s. I'm %d years old from china."% ('Rose',25) "My name is %s. I'm %d years old from china." 为字符串的模板。%s 为第一个格式符,表示一个 字符串。%d 为第二个格式符,表示一个整数。('Rose',25) 的两个元素 ' Rose ' 和 25 为替换 %s 和 %d 的真实值。在模板和 tuple 之间,有一个 % 符号分隔,它代表格式化操作。 整个 "My name is %s. I'm %d years old from china."% ('Rose',25) 实际上构成一个字符串表达式, 可以像一个正常的字符串那样,将它赋值给某个变量。比如: var="My name is %s. I'm %d years old from china."% ('Rose',25) 格式符为真实值预留位置,并控制显示的格式。格式符包含一个类型码,用以控制显示的类型。 Python 提供的常用格式符见表 3-11。 表 3-11 Python 3 常用格式符 格 式 38 描 述 %% 格式化百分号标记 %c 格式化字符及其 ASCII 码 %s 格式化字符串 %d 格式化有符号整数(十进制) %u 格式化无符号整数(十进制) %o 格式化无符号整数(八进制) %x 格式化无符号整数(十六进制) %X 格式化无符号整数(十六进制大写字符) %e 格式化浮点数字 ( 科学计数法 ) %E 格式化浮点数字(科学计数法,用 E 代替 e) %f 格式化浮点数字(用小数点符号) %g 格式化浮点数字(根据值的大小采用 %e 或 %f) %G 格式化浮点数字(类似于 %g) %p 格式化指针(用十六进制打印值的内存地址) %n 格式化存储输出字符的数量放进参数列表的下一个变量中 第 3 章 Python 中的字符串 我们还可以用如下方式对格式进行进一步控制。 %[(name)][flags][width].[precision]typecode 参数说明如下: (1)(name) 为命名。 (2)flags 可以有 +,-,' ' 或 0。+ 表示右对齐。- 表示左对齐。' ' 为一个空格,表示在正数的 左侧填充一个空格,从而与负数对齐。0 表示使用 0 填充。 (3)width 表示显示宽度。 (4)precision 表示小数点后精度。 如果是浮点数,采用 %m.nf 格式。m 指的是输出总宽度,n 指的是小数点之后保留的位数(四 舍五入保存)。如果总宽度超过 m,按照实际显示。 【例 3-11】占位符(%)格式化字符串示例。 >>> num=24444 >>> '%6d'%num ' 24444' >>> '%3d'%num '24444' >>> '%-6d'%num '24444 ' >>> '%+6d'%num '+24444' >>> '%06d'%num '024444' >>> '%#o'%num '0o57574' >>> '%7.2f'%num '24444.00' 3.7.2 # 输出宽度为 6 位,右对齐,不够前面补空格 # 输出宽度为 3 位,但实际有 5 位数,按实际位数输出 # 输出宽度为 6 位,左对齐,不够后面补空格 # 输出宽度为 6 位,右对齐,不够前面补 + # 输出宽度为 6 位,右对齐,空格用 0 填补 # 八进制输出 # 总长为 7 位,保留 2 位小数 format() 方法格式化字符串 在 Python 3 中,字符串格式化操作还可以通过 format() 方法实现。相比于使用 % 操作符格式 化方式,format() 方法拥有更多的功能,操作起来更加方便,可读性也更强。该函数将字符串当成 一个模板,通过传入的参数进行格式化,并且使用大括号 {} 作为特殊字符代替 %。 基本使用格式是: < 模板字符串 >.format(< 逗号分隔的参数 >) 1. 参数对应关系 调用 format() 方法后会返回一个新的字符串,参数从 0 开始编号。 < 模板字符串 > 与 format() 方法中的参数对应关系有以下 3 种: (1)使用位置参数匹配。 在模板字符串中,如占位符 {} 里没有表示顺序的序号,将按照参数出现的先后次序进行匹配。 如占位符 {} 里指定参数序号,则按照序号替换对应参数。注意参数序号从 0 开始,且同一个参数 可以填充多次。 (2)使用键值对的关键字参数匹配。 format() 方法中的参数用键值对形式表示时,在模板字符串中用“键”来表示。 (3)使用序列的索引作为参数匹配。 format() 方法中的参数如果是列表或元组时,可以使用索引来匹配。 39 Python 程序设计 【例 3-12】format() 方法的使用示例。 #1. 使用位置参数匹配 >>> "My name is {}. I'm {} years old from china.".format ('Marry',25) "My name is Marry. I'm 25 years old from china." >>> "My name is {0}. I'm {1} years old from china.".format ('Marry',25) # 指定参数序号 "My name is Marry. I'm 25 years old from china." # 同一个参数可以填充多次 >>> "My name is {0}. I'm {1} years old. {1} is a flower like age. {0} comes from China".format('Marry',25) "My name is Marry. I'm 25 years old. 25 is a flower like age. Marry comes from China" #2. 使用键值对的关键字参数匹配 >>> "My name is {name}. I'm {age} years old from china.".format (name='Marry',age=25) "My name is Marry. I'm 25 years old from china." #3. 使用序列的索引作为参数匹配 >>> list=['Marry',25] >>> 'name is {0[0]} ,age is {0[1]}'.format(list) 'name is Marry ,age is 25' 2. 格式控制信息 format() 方法中 < 模板字符串 > 的占位符 {} 里除了包括参数序号,还可以包括格式控制信息。 此时,占位符 {} 里的内部样式如下: {< 参数序号 >:< 格式控制标记 >} 其中,< 格式控制标记 >用来控制参数显示时的格式,按先后顺序包括:< 填充 >< 对齐 >< 宽度 ><. 精度 >< 类型 > 几个字段,这些字段都是可选的,可以组合使用。下面逐一介绍: (1)< 填充 >:指< 宽度 > 内除了参数外的字符采用什么方式表示,默认采用空格,可以通 过 < 填充 > 更换。 (2)< 对齐 >:指参数在< 宽度 >内输出时的对齐方式,分别使用<、> 和 ^3 个符号表示左对 齐、右对齐和居中对齐。 (3)< 宽度 >:指当前占位符 {} 里的设定输出字符宽度,如果该占位符 {} 里对应的 format() 参数长度比 < 宽度 > 设定值大,则使用参数实际长度。如果该值的实际位数小于指定宽度,则位 数将被默认以空格字符补充。 (4)<. 精度 >:表示两个含义,由小数点(.)开头。对于浮点数,精度表示小数部分输出 的有效位数。对于字符串,精度表示输出的最大长度。 (5)< 类型 >:表示输出整数和浮点数类型的格式规则。 对于整数类型,输出格式包括 6 种。 b:输出整数的二进制方式。 c:输出整数对应的 Unicode 字符。 d:输出整数的十进制方式。 o:输出整数的八进制方式。 x:输出整数的小写十六进制方式。 X:输出整数的大写十六进制方式。 对于浮点数类型,输出格式包括 4 种。 e:输出浮点数对应的小写字母 e 的指数形式。 40 第 3 章 Python 中的字符串 E:输出浮点数对应的大写字母 E 的指数形式。 f:输出浮点数的标准浮点形式。 %:输出浮点数的百分形式。 【例 3-13】使用 format() 方法格式化字符串示例。 >>> "{0:-^15}".format(12345) # 居中对齐,空格以 '-' 填补 '-----12345-----' "{0:-^15,}".format(12345) # 居中对齐,空格以 '-' 填补,数字的千位分隔符使用逗号 '----12,345-----' >>> "{0:.2f}".format(12345.67890) '12345.68' >>> "{0:.2e},{0:.2E},{0:.2f},{0:.2%}".format(3.14) # 以 eE% 显示数据 '3.14e+00,3.14E+00,3.14,314.00%' 3.8 提升训练 【训练内容】 开发微信敏感词过滤程序,提示用户输入内容。如果用户输入的内容中包含特殊的字符,如“特 效”“全效”“神效”,则将内容替换为 ***。 【实现思路】 创建 3 个变量 vor1、vor2、 vor3,判断用户输入的字符串里面是否有敏感词。有的话,find 的 值就不是 -1。只有 find 的值是 -1 的时候,才是没有敏感词。 然后用 if 判断,如果 3 个值都等于 -1,才证明不包含定义的 3 个敏感词,就正常打印。如果 包含一个敏感词或者是包含 3 个敏感词都包含,就执行替换的操作。 替换操作思路是:先替换“特效”。替换完成之后,给它赋值一个新的变量名字,再把新的变 量里面的“全效”再次替换,最后替换“神效”,打印出来最后的变量就是三者都替换过了的结果。 【参考代码】 str_input = input(" 请输入字符串:") vor1 = str_input.find(' 特效 ') vor2 = str_input.find(' 全效 ') vor3 = str_input.find(' 神效 ') if vor1 == -1 and vor2 == -1 and vor3 == -1: print(str_input) elif var1 != -1 or var2 != -1 or vor3 == -1: input_1 = str_input.replace(' 特效 ','*****') input_2 = input_1.replace(' 全效 ','*****') input_3 = input_1.replace(' 神效 ','*****') print(input_3) else: pass 习 题 一、选择题 1. 若 s = 'The Python software foundation ',下列语句可以输出“Python”的是 (A)print(s[4:11].lower) 。 (B)print(s[4:11].lower()) 41 Python 程序设计 (C)print(s[4:11].capitalize) (D)print(s[4:11].capitalize()) 2. 若 s = 'take second arg if first is a string',要输出成 ['take', 'second', 'arg', 'if', 'first', 'is', 'a', 'string'] 形式,应使用语句 。 (A)print(s.encode()) (B)print(s.split()) (C)print(s.endswith()) (D)print(s.casefold()) 3. 关于语句 "{0:-^15,}".format(4321),下列说法错误的是 。 (A)对 4321 进行居中对齐,空格以“-”填补 (B)对 4321 进行格式化,其中参数序号 0 可有可无 (C)对 4321 进行靠左对齐,空格以“-”填补 (D)对 4321 进行格式化,数字的千位分隔符使用逗号 二、填空题 1. 设 str = 'it may alter the wrong spot',那么执行 str[:7]+'cut'+str[-15:] 语句后得到的值是 。 2. 网址通常是由网络名 + 域名的主体 + 域名的后缀这三部分组成。已知 str = 'www.baidu. com',代码 str. 能把网址的各部分提取出来保存于列表中,即 ['www', 'baidu', 'com']。请补全 横线处的代码。 三、编程题 1. 编写程序,要求用户输入姓名、学号,并按以下格式输出结果: 请输入您的姓名:刘斯惠 请输入您的学号:2021012406 *#**#**#**#**#**#**#**#**#**#**#**#* 刘斯惠同学,欢迎你!你的学号是 :2021012406 *#**#**#**#**#**#**#**#**#**#**#**#* 2. 用户输入含有字母和数字的一串字符,请计算该输入的字符串中含有几个数字、几个字母。 3. 开发一个广告敏感词语过滤程序,提示用户输入内容。如果用户输入的内容中包含特殊的 词语,如“特效”“全效”“神效”,则将内容替换为 ***。 4. 输入一个句子(只包含字母和空格),将句子中的单词位置反转,单词用空格分割,单词 之间只有一个空格,前后没有空格,如:In this paper → paper this In。 42 第4章 Python 流程控制语句 学习目标 (1)了解程序的控制结构。 (2)掌握程序的选择结构:单分支、双分支、多分支选择结构。 (3)掌握程序的循环结构: for 循环结构、while 循环结构。 (4)掌握循环控制语句:continue 语句、break 语句。 (5)掌握程序的异常处理语句:try...except 语句。 (6)编写小程序来解决一些实际应用问题,在学习选择结构、循环结构时,要注意结构的语 法,使学生理解标准和规则的重要性,提高规则意识,形成尊重、遵守标准和规则的氛围。在学 习多分支选择结构的时候让学生懂得世界的多样性,并不是非此即彼,条条道路通罗马,遇到挫 折敢于直面挑战,并努力解决困难。 章节导学 Python 3 将程序结构分为 3 种:顺序结构、选择结构、循环结构。顺序结构中的语句按照 先后顺序自上而下地顺序执行。选择结构则根据不同的条件执行不同的分支,根据分支的情 况又可以分为单分支、双分支和多分支选择结构。循环结构则可以重复执行相同的代码。根 据循环次数的确定性,可以分为确定次数的遍历循环 for 循环和不确定次数的无限循环 while 循 环。 另 外, 异 常 处 理 是 一 种 用 于 处 理 程 序 异 常 的 特 殊 控 制 结 构, 在 Python 3 中 使 用 tryexcept 结构来实现异常处理。 4.1 顺序结构 顺序结构是结构化程序设计中最简单的一种结构,根据解决问题的先后 顺序编写相应语句。程序执行顺序也是线性有序的过程,依次按语句出现的 先后顺序执行。顺序结构由连续的处理步骤依次排列组成,如图 4-1 所示。 但是在实际问题的应用中,为了实现复杂的逻辑和算法,单一的顺序结构便 无法满足编程需求,往往还需要配合分支结构和循环结构。 图 4-1 顺序结构 流程图 43 Python 程序设计 4.2 选择结构 选择结构根据条件选择对应的分支语句执行,就像人的一生经常需要面对选择,程序开发中 也经常要根据不同的条件选择不同的处理方式。根据分支的数量,选择结构可分为:单分支选择 结构、双分支选择结构、多分支选择结构。 在 Python 3 中使用 if...elif...else 语句来表示选择结构的基本框架,语法结构如下: if 条件表达式 1: 语句块 1 elif 条件表达式 2: 语句块 2 elif 条件表达式 3: 语句块 3 …… else: 语句块 n 需要注意的是,在结构中,if、elif、else 为关键字,同一组选择结构中关键字要保持对齐。 其中 elif、else 是非必须的,可以没有,也可以有多个 elif。elif 后面须跟条件表达式,else 后无须 跟条件表达式。满足条件要执行的语句块需要缩进。 4.2.1 单分支选择结构 单分支选择结构是选择结构中最简单的一种,当条件表达式的结果为 True 时,执行相应的语句块,否则,不做相应的处理。具体流程如图 4-2 所示。 单分支选择结构的语法结构如下: if 条件表达式: 语句块 1 【例 4-1】单分支选择结构验证密码示例。 Password = input(" 请输入您的密码:") if Password == "abc123": print(" 欢迎您! ") 图 4-2 单分支选择 结构流程图 程序执行时,用户输入密码,将输入的密码与字符串“abc123”进行比较,如果相等则输出“欢 迎您!”,否则不做任何处理。在一些问题的处理上,可以将单分支选择结构重复使用,以解决 多个条件表达式的判断处理。 【例 4-2】单分支选择结构实现车费计算示例。 用户输入坐车站点数 n 及人数 person,按坐车站点数量和人数计算支付车费 pay。规则为: 当 010 时,每 人 5 元。 n = int(input(" 请输入乘车站点数:")) person = int(input(" 请输入乘车人数:")) if 010: pay = 5*person if n<0: print(" 请重新输入正确的站点数! ") print(" 需支付金额为:{}".format(pay)) 需要特别注意的是,重复使用单分支解决问题时,应该仔细划分条件的区间,避免每个分支 的条件取值区间发生重叠,否则会出现错误的处理结果。而且单分支语句运行时,系统会对每个 分支进行判断,运算开销较大。比如当 n 为 3 时,第一个分支条件判断为 True 之后,依然会对后 面的每个单分支选择结构中的条件表达式进行判断。 4.2.2 双分支选择结构 Python 的双分支选择结构使用了 if...else 保留字对条件进行判断。 与单分支选择结构不同的是,除了对条件表达式结果为 True 时,会 执行相应的语句块 1,同时还要对条件表达式结果为 False 时,执行 相应的语句块 2。双分支选择结构会根据条件表达式的结果 True 或 False,选择不同的路径。双分支选择结构程序具体流程如图 4-3 所示。 双分支选择结构的语法结构如下: 图 4-3 双分支选择结构 程序流程图 if 条件表达式 : 语句块 1 else: 语句块 2 【例 4-3】在登录平台时需要对密码进行验证,假如用户密码为:abc123,现在需要对密码 进行验证。当密码输入正确时,显示:欢迎您!。否则,显示:密码输入错误!。 Password = input(" 请输入您的密码:") if Password == "abc123": print(" 欢迎您! ") else: print(" 密码输入错误! ") 【例 4-4】根据用户输入的身份证号码判断性别。判断规则为:身份证号码的第 17 位为性别 位,当性别位为奇数时,用户为男性;性别位为偶数时,用户为女性。 IdNum = input(" 请输入您的身份证号码:") if int(IdNum[-2])%2 == 0: print(" 女性 ") else: print(" 男性 ") 需要注意的是,每个 else 都与前面与它对齐的 if 配对,双分支选择结构的条件表达式将区间 一分为二,条件互斥。也就是说,当条件表达式结果为 True 时,执行语句块 1,条件表达式结果 为 False 时,直接执行 else 对应的语句块 2。双分支选择结构相对于单分支,节约了条件判断的时间, 执行效率更高。 除此之外,双分支还有一个更加简洁的表达形式,适合语句块 1 和语句块 2 都只包含简单表 达式的情况,语法结构如下: < 表达式 1>if< 条件表达式 >else< 表达式 2> 45 Python 程序设计 【例 4-5】双分支简洁表达形式示例。 >>> x = 95 >>> y = " 三好学生 " >>> print(y) 三好学生 4.2.3 if x>90 else " 暂不能获得三好学生 " 多分支选择结构 Python 的多分支选择结构使用了 if...elif...else 保留字对条件进行判断。与单分支选择结构、双 分支选择结构不同的是:多分支选择结构会对多个分支条件进行判断,根据不同条件结果执行相 对应的分支。多分支选择结构具体流程如图 4-4 所示。 图 4-4 多分支选择结构流程图 多分支选择结构的语法结构如下: if 条件表达式 1: 语句块 1 elif 条件表达式 2: 语句块 2 …… else: 语句块 N Python 在执行多分支语句时,会按先后的顺序计算条件表达式的值,当条件表达式结果为 True,则执行对应分支,分支语句块执行结束则 if 语句结束,不再计算后续分支条件表达式。只 有当条件表达式结果为 False 时,才会继续计算后续的分支表达式。若全部分支表达式结果都为 False,则会执行 else 子句分支语句块。 【例 4-6】获取用户输入的成绩 score,根据成绩评定等级。评定规则为:当 0<=score<60 时, 不及格;当 60<=score<70 时,及格;当 70<=score<80 时,中等;当 80<=score<90 时,良好;当 90<=score<=100 时,优秀。 score = eval(input(" 请输入你的成绩(百分制):")) if score < 0 or score > 100: print(" 请重新输入正确的成绩! ") elif score >= 0: print(" 不及格 ") elif score >= 60: 46 第 4 章 Python 流程控制语句 print(" 及格 ") elif score >= 70 : print(" 中等 ") elif score >= 80: print(" 良好 ") else: print(" 优秀 ") 运行程序,输入 85,观察输出结果。 >>> 请输入你的成绩(百分制):85 不及格 显而易见,成绩 85 不应该是“不及格”这样的等级。上述代码虽然能够正常运行,但是却 出现了明显的逻辑错误,原因主要就是在编写多分支条件表达式时,没有弄清条件表达式的先后 关系,使得条件表达式的取值区间发生了重叠。修改之后的代码如下: score = eval(input(" 请输入你的成绩(百分制):")) if score < 0 or score > 100: print(" 请重新输入正确的成绩! ") elif score < 60: print(" 不及格 ") elif score < 70: print(" 及格 ") elif score < 80: print(" 中等 ") elif score <90: print(" 良好 ") else: print(" 优秀 ") 运行程序,输入 85,观察输出结果。 >>> 请输入你的成绩(百分制):85 良好 4.2.4 if 语句的嵌套 当 if 语句的分支语句块中包含了 if 语句时,就把这样的结构称为 if 语句的嵌套。在程序结构 上相当于把 if...elif...else 结构放在了另一组 if...elif...else 结构中的分支语句块上。 if 语句的嵌套一般语法结构如下: if 条件表达式 1: if 条件表达式(1): 语句块(1) elif 条件表达式(2): 语句块(2) else: 语句块(3) elif 条件表达式 2: 语句块 2 else: 语句块 3 47 Python 程序设计 还可以根据分支的嵌套情况将 if 语句的嵌套分为:单分支嵌套单分支选择结构、双分支嵌套 双分支选择结构、多分支嵌套多分支选择结构等。单分支嵌套单分支选择结构流程图如图 4-5 所示。 图 4-5 单分支嵌套单分支选择结构流程图 单分支嵌套单分支选择结构的语法结构如下: if 条件表达式 1: if 条件表达式 2: 语句块 双分支嵌套双分支选择结构流程图如图 4-6 所示。 图 4-6 双分支嵌套双分支选择结构流程图 双分支嵌套双分支选择结构的语法结构如下: if 条件表达式 1: 语句块 1 else: if 条件表达式 2: 语句块 2 else: 语句块 3 【例 4-7】根据用户输入的身份证号码,判断身份证号码是否为 18 位长度。如果长度为 18 位, 进一步判断性别,并输出显示出生日期及性别,格式如下:出生日期为:××××年××月××日, 是男(女)性。否则提示:请重新输入正确的身份证号码。 IdNum = input(" 请输入你的身份证号码:") IdNum_len = len(IdNum) year = IdNum[6:10] 48 第 4 章 Python 流程控制语句 month = IdNum[10:12] day = IdNum[12:14] if IdNum_len == 18: if int(IdNum[-2])%2 == 0: print(" 出生日期为:{} 年 {} 月 {} 日,是女性! ".format(year,month,day)) else: print(" 出生日期为:{} 年 {} 月 {} 日,是男性! ".format(year,month,day)) else: print(" 请重新输入正确的身份证号码:") 运行程序,输入 445522198808085322,观察输出结果。 >>> 请输入你的身份证号码:445522198808085322 出生日期为:1988 年 08 月 08 日,是女性! 4.2.5 提升训练 【训练内容】 根据用户输入的具体年份及月份,计算当月一共有多少天,并按照以下格式进行输出。格式 如下:xxxx 年 xx 月有 xx 天。需要注意的是每年的 2 月份,2 月份的天数需要根据年份确定。 【设计实现思路】 需要先对年份进行判断,当年份为闰年时,2 月有 29 天,平年时,2 月有 28 天。闰年判断规 则是:如果年份能够被 4 整除且不能被 100 整除,或者能够被 400 整除的话,则为闰年,否则为 平年。 【参考代码】 year = eval(input(" 请输入具体的年份:")) month = eval(input(" 请输入具体的月份:")) if month in [1,3,5,7,8,10,12]: days = 31 elif month in [4,6,9,11]: days = 30 else: if year%400 == 0 or (year%4 == 0 and year%100 != 0): days = 29 else: days = 28 print("{} 年 {} 月一共有 {} 天 ".format(year,month,days)) 运行程序,年份输入 2022,月份输入 2,观察输出结果。 >>> 请输入具体的年份:2022 请输入具体的月份:2 2022 年 2 月一共有 28 天 4.3 循环结构 在程序中如果需要重复的执行某条或某些指令,例如程序中要实现每隔 1 秒就在屏幕上打印 一个“hello, world”这样的字符串,并持续一个小时,肯定不能够将 print('hello, world') 这句代码 49 Python 程序设计 写上 3 600 遍。因此,需要使用循环结构,有了循环结构就可以轻松地控制某件事或者某些事重复、 重复、再重复地发生。循环结构可以减少源程序重复书写的工作量(代码量),用来描述重复执 行某段算法的问题,这是程序设计中最能发挥计算机特长的程序结构。 在 Python 3 中构造循环结构有两种做法,一种是 for 循环,一种是 while 循环。for 循环一般用 于有明显边界范围的情况,while 循环一般应用于循环次数难以确定的情况。 4.3.1 for 循环 for 循环适合知道循环执行的次数或者是要对一个容器进行迭代,又称为遍历循环,它常用于 遍历字符串、列表、元组、字典、集合等序列类型,逐个获取序列中的各个元素。 1. for 循环的结构 for 语句实现遍历循环,其语法结构如下: for 变量 in 序列对象 : 循环体 else: 语句块 2 else 部分可以省略。object 是一个可迭代对象。for 语句执行时,依次将序列对象中的数据赋 值给变量,该操作称为迭代。 变量每赋值一次,则执行一次循环体。循环执行结束时,如果有 else 部分,则执行对应的语句块。 else 部分只在正常结束循环时执行。如果用 break 跳出循环,则不会执行 else 部分。 【例 4-8】for 循环示例。 for i in "python": # 这里变量为 i,序列对象为字符串 "python" print(i) # 循环体 else: print(" 这是有 else 语句的循环 ") # 循环结束后执行该语句 程序运行结果: p y t h o n 这是有 else 语句的循环 2. range() 函数 range() 函数是 Python 的内置函数,用于创建一个整数列表,一般用于 for 循环中,其语法结 构如下: range(start,end,step) 此函数中各参数的含义如下: start:用于指定计数的起始值,如果省略不写,则默认从 0 开始。 end:用于指定计数的结束值(不包括此值),此参数不能省略。 step:用于指定步长,即两个数之间的间隔,如果省略,则默认步长为 1。 总之,在使用 range() 函数时,如果只有一个参数,则表示指定的是 end;如果有两个参数, 则表示指定的是 start 和 end。 50 第 4 章 Python 流程控制语句 【例 4-9】使用 for 循环和 range() 函数计算 1~10 的和。 sum = 0 for x in range(11): sum += x print(sum) # 初始值 0 # 由于是加法,所以可以从 0 开始 【例 4-10】使用 for 循环和 range() 函数计算 1~10 之间奇数的和。 sum = 0 # 初始值 0 for x in range(1, 11,2): sum += x print(sum) 【例 4-11】使用 for 循环和 range() 函数计算 10 的阶乘。 sum = 1 # 初始值 1 for x in range(1,11): sum *= x print(sum) # 由于是乘法,所以不能从 0 开始 【例 4-12】使用 for 循环和 range() 函数计算 1~100 中能被 3 整除的数的和。 sum=0 for i in range(100): if i%3 == 0: sum += i print(i,end='/') print(sum) 4.3.2 while 循环 for 循环确定了循环次数,但有时候循环次数不确定,程序只是像 if 条件分支语句那样,即在 条件(表达式)为真的情况下,需执行相应的代码块。这种循环结构可以用 while 语句来实现。 while 语句的语法格式如下: while 条件表达式 : 循环体代码块 else: 语句块 2 其中 else 部分语句可以省略。条件表达式可以是任何表达式,任何非零或非空(null)的值 均为 True。 while 语句执行的过程是先判断条件表达式的值,其值为真(True)时,则执行循环体代码块 中的语句。当执行完毕后,再回过头来重新判断条件表达式的值是否为真。若仍为真,则继续重 新执行循环体代码块。如此循环,直到条件表达式的值为假(False),才终止循环。 【例 4-13】使用 while 循环编一个计数程序,大于等于 5 就不再计数。 i = 0 # 计数初始值 while i < 5: # 循环的条件 print("The count is:",i) i = i+1 # 改变初始值 else: print(" 数字已经大于 5 了,不再计数 ") 51 Python 程序设计 程序运行结果: The count is: 0 The count is: 1 The count is: 2 The count is: 3 The count is: 4 数字已经大于 5 了,不再计数 注意:while 语句是先判断再执行,所以循环有可能一次也不执行。在使用 while 循环时, 一定要保证循环条件有变成假的时候,否则这个循环将成为一个死循环,永远无法结束这个 循环。 【例 4-14】while 死循环示例。 i = 0 # 计数初始值 while i < 5: # 循环的条件 print("The count is:",i) 上例循环体不包含能改变变量值的语句,条件表达式始终为 True,则程序进入死循环。 除此之外,while 循环还常用来遍历列表、元组和字符串,因为它们都支持通过下标索引获取 指定位置的元素。 【例 4-15】将元组中的元素进行头尾置换,即元组中的第一个元素和倒数第一个元素交换, 第 2 个元素和倒数第 2 个元素交换,如此循环。 list1 = ["A","B","C","D",0,1,2,3,4] head = 0 # 开始位置 tail = len(list1) - 1 # 结束位置 while head < len(list1)/2: list1[head],list1[tail] = list1[tail],list1[head] head += 1 # 调整开始指针往后移 1 位 tail -= 1 # 调整结尾指针往前移 1 位 print(list1) # 头尾互换 程序运行结果: [4, 3, 2, 1, 0, 'D', 'C', 'B', 'A'] 4.3.3 循环的嵌套 Python 3 不仅支持 if 语句相互嵌套,while 和 for 循环结构也支持嵌套。例如 for 里面还有 for, while 里面还有 while,甚至 while 中有 for 或者 for 中有 while 也都是允许的。当两个(甚至多个) 循环结构相互嵌套时,位于外层的循环结构常简称为外层循环或外循环,位于内层的循环结构常 简称为内层循环或内循环。 程序遇到循环嵌套时,Python 解释器执行的流程为: (1)外层循环条件为 True 时,则执行外层循环结构中的循环体。 (2)外层循环体中包含了普通程序和内循环,当内层循环的循环条件为 True 时会执行此循 环中的循环体,直到内层循环条件为 False,跳出内循环。 (3)如果此时外层循环的条件仍为 True,则返回第(2)步,继续执行外层循环体,直到外 层循环的循环条件为 False。 (4)当内层循环的循环条件为 False,且外层循环的循环条件也为 False 时,整个嵌套循环 才算执行完毕。 52 第 4 章 Python 流程控制语句 1.for...for 嵌套结构 【例 4-16】使用 for...for 嵌套结构,输出九九乘法表。 # 外层循环 for i in range(1,10): # 这里 i 是控制打印第几行 # 内层循环 for j in range(1,10): # 这里 j 是控制打印第几列 print("{}*{}={}\t".format(i,j,i*j),end="") print() 程序运行结果: 1*1=1 2*1=2 3*1=3 4*1=4 5*1=5 6*1=6 7*1=7 8*1=8 9*1=9 1*2=2 2*2=4 3*2=6 4*2=8 5*2=10 6*2=12 7*2=14 8*2=16 9*2=18 1*3=3 2*3=6 3*3=9 4*3=12 5*3=15 6*3=18 7*3=21 8*3=24 9*3=27 1*4=4 2*4=8 3*4=12 4*4=16 5*4=20 6*4=24 7*4=28 8*4=32 9*4=36 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 6*5=30 7*5=35 8*5=40 9*5=45 1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 7*6=42 8*6=48 9*6=54 1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 8*7=56 9*7=63 1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 9*8=72 1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81 此程序中外层循环将循环 9 次(从 i=1 到 i=9),而每次执行外层循环时,内层循环都从 j=1 循环执行到 j=9。因此,该嵌套循环结构将执行 9*9 = 81 次。 2. while...while 嵌套结构 【例 4-17】使用 while...while 嵌套结构,输出上三角九九乘法表。 # 外层循环 i = 1 while i <= 9: # 内层循环 j = 1 while j <= i: print("{}*{}={}\t".format(i,j,i*j),end="") j += 1 print() i += 1 程序运行结果: 1*1=1 2*1=2 3*1=3 4*1=4 5*1=5 6*1=6 7*1=7 8*1=8 9*1=9 2*2=4 3*2=6 4*2=8 5*2=10 6*2=12 7*2=14 8*2=16 9*2=18 3*3=9 4*3=12 5*3=15 6*3=18 7*3=21 8*3=24 9*3=27 4*4=16 5*4=20 6*4=24 7*4=28 8*4=32 9*4=36 5*5=25 6*5=30 7*5=35 8*5=40 9*5=45 6*6=36 7*6=42 8*6=48 9*6=54 7*7=49 8*7=56 9*7=63 8*8=64 9*8=72 9*9=81 3. while...for 嵌套结构 【例 4-18】while...for 嵌套结构示例。 i = 1 while i < 3: 53 Python 程序设计 for j in range(3): print(" 外循环的 i 为:",i," 内循环的 j 为:",j) i += 1 程序运行结果: 外循环的 i 为: 1 外循环的 i 为: 1 外循环的 i 为: 1 外循环的 i 为: 2 外循环的 i 为: 2 外循环的 i 为: 2 4.3.4 内循环的 j 为: 0 内循环的 j 为: 1 内循环的 j 为: 2 内循环的 j 为: 0 内循环的 j 为: 1 内循环的 j 为: 2 Python 3 循环结构中 else 用法 Python 3 中,无论是 while 循环还是 for 循环,其后都可以紧跟着一个 else 代码块,它的作用是: 当循环条件为 False 跳出循环时,程序会最先执行 else 代码块中的代码。 【例 4-19】循环结构中 else 用法示例。 count_i = 0 while count_i < 5: print('count_i 小于 5: ', count_i) count_i += 1 else: print('count_i 大于或等于 5: ', count_i) 程序运行结果: count_i 小于 5: 0 count_i 小于 5: 1 count_i 小于 5: 2 count_i 小于 5: 3 count_i 小于 5: 4 count_i 大于或等于 5: 5 从上面的运行过程来看,当循环条件 count i < 5 变成 False 时,程序执行了 while 循环的 else 代码块。 简单来说,程序在结束循环之前,会先执行 else 代码块。从这个角度来看,else 代码块其实 没有太大的价值,将 else 代码块直接放在循环体之外即可。也就是说,上面代码其实可改为如下 形式: count_i = 0 while count_i < 5: print('count_i 小于 5: ', count_i) count_i += 1 print('count_i 大于或等于 5: ', count_i) for 循环同样可使用 else 代码块。当 for 循环把区间、元组或列表的所有元素遍历一次之后, for 循环会执行 else 代码块。在 else 代码块中,迭代变量的值依然等于最后一个元素的值。 【例 4-20】for 循环的 else 用法示例。 list1 = ["A", 2.4, 50, 'BCD', -5] for ele in list1: print(' 元素 : ', ele) else: 54 第 4 章 Python 流程控制语句 # 访问循环计数器的值,依然等于最后一个元素的值 print('else 块 : ', ele) 程序运行结果: 元素 : A 元素 : 2.4 元素 : 50 元素 : BCD 元素 : -5 else 块 : -5 4.3.5 pass、break 和 continue 1. pass 语句及其作用 Python 的 pass 语句就是空语句。有时候程序需要占一个位、放一条语句,但又不希望这条语 句做任何事情,此时就可通过 pass 语句来实现。通过使用 pass 语句,可以让程序更完整。 【例 4-21】pass 语句示例。 s = input(" 请输入一个整数:") s = int(s) if s > 5: print(" 大于 5") elif s < 5: # 空语句,相当于占位符 pass else: print(" 等于 5") 程序运行时输入数字 7 的结果: 请输入一个整数:7 大于 5 从上面程序所看到的,对于 s 小于 5 的情形,程序暂时不想处理(或不知道如何处理),此 时程序就需要通过空语句来占一个位,这样即可使用 pass 语句了。 2. break 和 continue 用法详解 在执行 while 循环或者 for 循环时,只要循环条件满足,程序将会一直执行循环体,不停地转圈。 但在某些场景,我们可能希望在循环结束前就手动离开循环,Python 提供了两种强制离开当前循 环体的办法。 (1)使用 continue 语句,可以跳过执行本次循环体中剩余的代码,转而执行下一次的循环。 (2)只用 break 语句,可以完全终止当前循环。 在某些场景中,如果需要在某种条件出现时强行中止循环,而不是等到循环条件为 False 时 才退出循环,就可以使用 break 来完成这个功能。 break 用于完全结束一个循环,跳出循环体。不管是哪种循环,一旦在循环体中遇到 break, 系统就将完全结束该循环,开始执行循环之后的代码。 break 语句一般会结合 if 语句进行搭配使用,表示在某种条件下,跳出循环体,如果使用嵌 套循环,break 语句将跳出当前的循环体。 【例 4-22】判断一个数是否为素数。 a = int(input(" 请输入一个整数: ")) 55 Python 程序设计 for i in range(2,a): if(a%i == 0): print("%d 不是一个素数 "%a) break else: print("%d 是一个素数 "%a) 程序运行结果: 请输入一个整数:13 13 是一个素数 请输入一个整数:9 9 不是一个素数 【例 4-23】输出 101~200 区间范围内的素数。 for i in range(101,200): for j in range(2,i): if(i%j == 0): break else: print(i,end="、") 需要注意的是,对于带 else 块的 for 循环,如果使用 break 强行中止循环,程序将不会执行 else 块。 【例 4-24】带 else 块的 for 循环中有 break 的示例。 for i in range(0, 10) : print("i 的值是 : ", i) if i == 2 : # 执行该语句时将结束循环 break else: print('else 块 : ', i) 程序运行结果: i 的值是 : i 的值是 : i 的值是 : 0 1 2 在使用 break 语句的情况下,循环的 else 代码块与直接放在循环体后是有区别的,即如果将 代码块放在 else 块中,当程序使用 break 中止循环时,循环不会执行 else 块;如果将代码块直接 放在循环体后面,当程序使用 break 中止循环时,程序自然会执行循环体之后的代码块。 while 循环一般要有能条件表达式的值为假(False)的语句,但有时条件表达式本身就是 True,这时可以在循环里使用特定语句来结束循环,比如 if 语句、break 语句、continue 语句。 【例 4-25】计算 1~100 中能被 3 整除的数的和。 sum = 0 x = 1 while True: # 这里条件表达式为 True x = x + 1 if x > 100: # 此语句即为结束循环的条件 break if x % 3 == 0: continue 56 第 4 章 Python 流程控制语句 sum += x print(sum) 4.3.6 提升训练 【训练内容】 求两个数的最大公约数有 3 种方法:短除法、辗转相除法、更相减损法。请使用辗转相除法 编程实现求两个数的最大公约数。 【设计实现思路】 辗转相除法的步骤如下: (1)比较两数,并使 m>n。 (2)将 m 作被除数,n 做除数,相除后余数为 r。 (3)循环判断 r,若 r==0,则 n 为最大公约数,结束循环。若 r !=0 ,执行 m=n,n=r。 求解思路:求两个数的最大公约数时,先用较大数除以较小数,如果能整除,最大公约数就 等于较小数;否则用较小数除以第一步的余数,如果能整除,最大公约数就等于第一步的余数; 否则用当前获得的余数除以上一步的余数,直到能整除为止。此时作为除数的那个数就是最开始 那两个数的最大公约数。 【参考代码】 a = int(input(" 请输入第一个数:")) b = int(input(" 请输入第二个数:")) m = max(a,b) # 把两个数中最大的赋值给 m n = min(a,b) # 把两个数中最小的赋值给 n r = m%n #r 为两个数的余数 # 如果余数 r 不等于 0,则把除数赋值给 m,余数赋值给 n, # 直到 r=0,此时作为除数的那个数就是最开始那两个数的最大公约数 while r != 0: m = n n = r r = m%n print("{} 和 {} 的最大公约数为:{}".format(a,b,n)) 4.4 异常处理 4.4.1 异常概述 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。一般情况下, 在 Python 3 无法正常处理程序时就会发生一个异常。 异常是 Python 对象,表示一个错误。当 Python 3 脚本发生异常时我们需要捕获处理它,否则 程序会终止执行。 【例 4-26】异常示例。 list1= [" 春季 "," 夏季 "," 秋季 "," 冬季 "] print(list1[1]) print(list1[5]) 程序运行结果: 夏季 57 Python 程序设计 Traceback (most recent call last): File "C:\Users\10471\Desktop\123.py", line 3, in print(list1[5]) IndexError: list index out of range 从程序运行结果来看,print(list1[1]) 语句正常执行,打印“夏季”;print(list1[5]) 语句执行时 则产生异常,报错的异常信息包括:Python 源文件的名字(123.py)及路径、异常的行号、异常 的类型及描述,其中异常的类型和描述为: IndexError: list index out of range 这一行语句提示索引错误,列表的索引超出范围。 4.4.2 常用异常类 Python 3 有 强 大 的 异 常 处 理 能 力, 它 有 很 多 内 置 异 常, 可 向 用 户 准 确 反 馈 出 错 信 息。 BaseException 是所有内置异常的基类,但用户定义的类并不直接继承 BaseException,所有的异 常类都是从 Exception 继承,且都在 exceptions 模块中定义。Python 自动将所有异常的名称存放 在内建命名空间中,所以程序不必导入 Exceptions 模块即可使用异常。一旦引发而且没有捕捉 SystemExit 异常,程序执行就会终止。如果交互式会话遇到一个未被捕捉的 SystemExit 异常,会 话就会终止。常用异常类见表 4-1。 表 4-1 常用异常类 异 常 AttributeError IOError ImportError IndentationError 述 试图访问一个对象没有的树形 输入 / 输出异常,基本上是无法打开文件 无法引入模块或包,基本上是路径问题或名称错误 语法错误(的子类),代码没有正确对齐 IndexError 下标索引超出序列边界 KeyError 试图访问字典里不存在的键 KeyboardInterrupt 【Ctrl+C】组合键被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python 代码逻辑语法出错,代码不能编译 TypeError 传入对象类型与要求的不符合 UnboundLocalError ValueError 4.4.3 描 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的 全局变量,导致你以为正在访问它 传入一个调用者不期望的值,即使值的类型是正确的 异常处理基本结构 Python 3 解释器检测到错误,触发异常(也允许程序员自己触发异常)。程序员编写特定的代码, 专门用来捕捉这个异常。如果捕捉成功则进入另外一个处理分支,执行你为其定制的逻辑,使程 序不会崩溃,这就是异常处理。 58 第 4 章 Python 流程控制语句 Python 3 提供的特定的语法结构来进行异常处理。基本语法结构如下: try: 被检测的代码块 except 异常类型 : 发生异常时执行的代码 基本语法的工作原理如下: (1)程序执行 try 子句。 (2)如果没有异常发生,忽略 except 子句,try 子句执行后结束。 (3)如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。 (4)如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。最后执 行 try 语句之后的代码。 (5)如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。 【例 4-27】基本异常处理示例。 while True: try: x = int(input(" 请输入一个数字:")) break except ValueError: print(" 您输入的不是数字,请再次尝试输入! ") 一个 try 语句可能包含多个 except 子句,分别来处理不同的特定的异常。最多只有一个分支 会被执行。 一个 except 子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组,例如: except (RuntimeError, TypeError, NameError): pass 1. try/except...else 结构 在 Python 3 的异常中,如果你想要的效果是:无论出现什么异常,我们统一丢弃,或者使用 同一段代码逻辑去处理他们,那么只有一个 Exception 就足够了。 如果你想要的效果是:对于不同的异常我们需要定制不同的处理逻辑,那就需要用到多分支 了。使用多分支优先处理一些能预料到的错误类型,一些预料不到的错误类型应该被最终的万能 异常捕获。需要注意的是,万能异常一定要放在最后,否则就没有意义了。基本语法结构如下: try: 执行代码 except 异常类型 1: 发生异常时执行的代码 except 异常类型 2: 发生异常时执行的代码 else: 没发生异常时执行的代码 【例 4-28】try/except...else 结构示例。 def div(): num1 = int(input(" 请输入被除数:")) num2 = int(input(" 请输入除数:")) r = num1/num2 59 Python 程序设计 print(r) if _ _name_ _ == "_ _main_ _": try: div() except ZeroDivisionError: # 处理除数为 0 的情况 print("num2 输入错误,除数不能为 0") except ValueError: # 处理输入非数值的情况 print(" 输入的不是数值 ") else: # 没有异常的情况 print(" 程序正在运行……") 使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到的而 except 又没有捕获的异常。 2. except...as 结构 可以在 except 语句中同时指定多种异常,以便使用相同的异常处理代码进行统一处理。在 except 语句中使用 as 为异常类创建一个示例对象。 【例 4-29】except...as 结构使用示例。 try: list1=[" 春季 "," 夏季 "," 秋季 "," 冬季 "] print(list1[1]) print(list1[5]) except IndexError as err: # 使用 as 为异常类创建一个示例对象 err print(" 异常原因 ",err) 程序运行结果: 夏季 异常原因 list index out of range 3. 捕捉所有异常 在捕捉异常时,如果 except 语句省略异常类型,则无论发生何种类型的异常,均执行 except 语句块中的异常处理代码。 【例 4-30】捕捉所有异常示例。 while True: try: x = int(input(" 请输入一个数字:")) break except: # 无论发生何种类型的异常,均执行 except 语句块中的异常处理代码 print(" 您输入的不是数字,请再次尝试输入! ") 4. try...finally 结构 finally 语句块的内容通常是做一些后事的处理,并且 finally 语句块是无论如何都要执行的, 即使在前面的 try 和 except 语句块中出现了 return,都要将 finally 语句执行完后,再去执行前面的 return 语句。语法结构如下: try: 执行代码 except 异常类型 : 发生异常时执行的代码 else: 60 第 4 章 Python 流程控制语句 没有发生异常时执行的代码 finally: 不管有没有异常都会执行的代码 【例 4-31】try...finally 结构示例。 while True: try: x = int(input(" 请输入一个数字: ")) break except ValueError: print(" 您输入的不是数字,请再次尝试输入! ") else: print(" 没有发生异常时执行的代码。") finally: print(' 这句话,无论异常是否发生都会执行。') 4.4.4 抛出异常 Python 3 中提供了一个 Exception 异常类,在开发时,如果需满足特定业务需求时,希望主动 抛出一个异常,则可以使用 raise 关键字,等同于 C# 和 Java 中的 throw,其语法规则如下: raise 异常类 raise 异常类对象 raise # 抛出异常,并隐式地创建异常类对象 # 抛出异常,创建异常类对象 # 重新抛出刚刚发生的异常 上述格式中,第一种方式和第二种方式是相同的,都会触发异常并创建异常类对象。但 第一种方式隐式地创建了异常类的对象,而第二种方式直接创建一个异常类的对象,是最常 用的。 1. 用类名引发异常 raise 语句中指定异常类名时,创建该类的实例对象,然后引发异常。 【例 4-32】用类名引发异常示例。 raise IndexError # 使用名字为 IndexError 的异常类,引发异常 程序运行结果: Traceback (most recent call last): File "", line 1, in raise IndexError # 使用名字为 IndexError 的异常类,引发异常 IndexError 2. 用异常类实例对象引发异常 通过显式地创建异常类的实例,直接使用该实例对象来引发异常。 【例 4-33】用异常类实例对象引发异常示例。 er = IndexError() raise er # 创建异常类的对象 er # 使用 er 实例对象来引发异常 程序运行结果: Traceback (most recent call last): File "C:/Users/Administrator/Desktop/12.py", line 2, in raise er # 使用 er 实例对象来引发异常 IndexError 61 Python 程序设计 3. 传递异常 不带任何参数的 raise 语句,可以再次引发刚刚发生过的异常,作用就是向外传递异常。 【例 4-34】不带任何参数的 raise 语句示例。 try: raise IndexError except: print(" 出错 ") raise # 引发 IndexError 异常 # 再次引发 IndexError 异常 程序运行结果: 出错 Traceback (most recent call last): File "C:/Users/Administrator/Desktop/12.py", line 2, in raise IndexError # 引发 IndexError 异常 IndexError 上述示例中,try 里面使用 raise 抛出了 IndexError 异常,程序会跳转到 except 子句中执行输 出打印语句,然后使用 raise 再次引发刚刚发生的异常 IndexError,导致程序出现错误而终止运行。 4. 指定异常的描述信息 当使用 raise 语句抛出异常时,还能给异常类指定描述信息。 【例 4-35】指定异常的描述信息示例。 raise IndexError(" 列表的索引下标已经超出范围! ") 程序运行结果: Traceback (most recent call last): File "C:/Users/Administrator/Desktop/12.py", line 1, in raise IndexError(" 列表的索引下标已经超出范围! ") IndexError: 列表的索引下标已经超出范围! 上述示例中,在抛出异常类时传入了自定义的描述信息“列表的索引下标已经超出范围!”。 【例 4-36】编写一程序,定义 input_pwd() 函数,提示用户输入密码。如果用户输入长度 < 4, 抛出异常;如果用户输入长度 >=4,返回输入的密码。 def input_pwd(): pwd = input(" 请输入密码:") if len(pwd) >= 4: return pwd er = Exception(" 密码长度不够 ") raise er try: user_pwd = input_pwd() print(user_pwd) except Exception as re: print(" 程序运行出现错误:%s" % re) 程序运行结果: 请输入密码:123456 123456 请输入密码:12 程序运行出现错误:密码长度不够 62 第 4 章 Python 流程控制语句 4.4.5 自定义异常 Python 3 内置了许多异常类,为编写代码划定红线,才使调试代码时能及时发现错误。那么 编写一个模块也可以为使用此模块者划定红线,来约束使用者可用哪些数据,这就需要自定义异 常类型。 所有内置的非系统退出类异常都派生于 Exception 类。所有用户自定义异常也应当派生自此类。 那么可以通过直接或间接的继承 Exception 类来自定义一个异常类,当然也可以间接地继承 Python 内置的异常类。 系统自带的异常只要触发会自动抛出,比如 ZeroDivisionError、NameError 等。但用户自定义 的异常需要用户自己决定什么时候抛出。可以使用 raise 语句手动抛出自定义的异常。 通常的做法是:先为自己的模块创建一个基类,然后再从此基类派生其他异常类,这样不但 清晰明了,也方便管理。 【例 4-37】编写一程序,定义一个 input_Error 异常类,若输入的不是数字就抛出异常信息。 # 自定义 input_Error 异常类,让 input_Error 类继承 Exception class input_Error(Exception): def _ _init_ _(self,s): self.s = s try: s = input(" 请输入数字:") if not s.isdigit(): raise input_Error(s) # 实例化一个异常,实例化的时候需要传参数 except input_Error as er: # 这里体现一个封装 print(" 输入错误:请输入数字。你输入的是:",er.s) # 捕获的就是 input_Error 类携 带过来的信息 except Exception as obj: # 万能捕获,之前的可能捕获不到,这里添加 Exception 作为 保底 print(obj) else: print(" 输入未发生异常 !") 程序运行结果: 请输入数字:d 输入错误:请输入数字。你输入的是: d 请输入数字:2 输入未发生异常 ! 习 题 一、选择题 1. 以下描述中不属于 Python 的控制结构的是 。 (A)选择结构 (B)顺序结构 (C)数据结构 (D)循环结构 2. 以下关于选择结构的描述中,错误的是 。 (A)Python 选择结构使用保留字 if...elif...else 来实现,每个 if 之后必须出现 elif 和 else 63 Python 程序设计 (B)if...else 结构是可以嵌套的 (C)执行多分支选择结构时,当某个分支表达式为真时,执行对应分支语句块,后续分支条件 不再判断 (D)缩进是 Python 分支语句的语法部分,缩进不正确会影响分支结果 3. 从键盘输入数字 3,以下代码的执行结果为 。 n = eval(input(" 请输入一个整数:")) s = 0 if n >= 3: n -= 1 s = 2 if n < 3: n -= 1 s = 1 print(n,s) (A)1 1 (B)1 2 4. 执行以下代码,输出的结果是 (C)3 1 (D)3 2 。 x=" 好好学习 " y=" 我爱 Python!" if x else " 天天向上! " print(y) (A)好好学习 我爱 Python ! (B)好好学习 天天向上! (C)我爱 Python ! (D)天天向上! 5. 从键盘输入数字“1,3”,以下代码的执行结果为 。 a,b = eval(input(" 请输入 2 个数字,以逗号分隔:")) if a < b: a,b = b,a print(a,b) else: print(a,b) (A)3 1 (B)1 3 6.下列代码输出的结果是 (C)3 3 (D)1 1 (C)0/1/2/4/5/7/8/ (D)1/2/4/5/7/8/ (C)0/1/2/4/5/7/8/ (D)1/2/4/5/7/8/ 。 for i in range(10): if i%3 == 0: continue else: print(i,end='/') (A)1/3/5/7/9/ (B)0/3/6/9/ 7. 下列代码输出的结果是 。 for i in range(10): if i%3 == 0: continue else: print(i,end='/') (A)1/3/5/7/9/ 64 (B)0/3/6/9/ 第 4 章 8. 关于 Python 中 break 语句和 continue 语句,下列说法错误的是 Python 流程控制语句 。 (A)break 语句跳出本次循环,而 continue 跳出整个循环 (B)Python 中的 pass 语句是空语句,目的在于保持程序结构的完整性 (C)当遇到 break 语句时,任何对应的循环 else 语句块将不执行 (D)continue 跳过当前循环中的剩余语句,然后继续下一次循环 9. 关于如下代码,下列选项说法正确的是 。 result = 1 for i in range(1,10): result *= i print(result) (A)循环内语句块执行了 10 次 (B)输出的结果是数字 362880 (C)result *= i 可以改写成 result = result*i (D)如果把 print(result) 语句完全左对齐,输出的结果不变 10. 关于 Python 中 while 循环和 for 循环的描述错误的是 。 (A)for 循环是在序列穷尽时停止,while 循环是在条件不成立时停止 (B)for 主要应用在遍历中,while 主要用于判断符合条件下循环 (C)部分情况下,for 循环和 while 循环可以互换使用 (D)while 循环只能用于未知循环次数的循环,for 循环只能用于已知循环次数的循环 11. 下列代码输出的结果是 。 for i in 'first': print(i,end='-') (A)f-i-r-s-t- (B)f-i-r-s-t (C)-f-i-r-s-t (D)-f-i-r-s-t- 12. 在 Python 3 中,异常处理不会用到的关键字是 (A)except (B)if 。 (C)else 13. 在 Python 3 中,关于异常说法正确的是 (D)finally 。 (A)输入错误会导致程序终止 (B)程序中抛出异常终止程序 (C)程序中抛出异常不一定终止程序 (D)拼写错误会导致程序终止 14. 关于 Python 中程序的异常处理,下列说法错误的是 。 (A)try...except 语句用来检测 try 语句块中的错误,从而让 except 语句捕获异常信息并处理 (B)在 Python 中,提供了 try...except 语句捕获并处理异常,在使用时,把处理结果放在 try 语句块中,把可能产生错误的代码放在 except 语句块中 (C)异常名 ValueError 表示传入无效的参数 (D)try...except...finally 语句无论是否发生异常都将执行最后的代码 二、填空题 1.Python 通过保留字 for 实现“遍历循环”。之所以称为“遍历循环”,是因为 for 语句的循 环执行次数是根据遍历结构中 确定的。 2. 下列程序是获取 0~100 范围内的素数,将其放入列表。请补全横线处的代码。 list1 = [ ] 65 Python 程序设计 m = 2 for m in range(2,100): n = 2 for n in : if : else: list1.append(m) print(list1) 3. 下列程序是计算 1-2+3-4+5+… +99 的结果,请补全横线处的代码。 sum = for i in range(1, 100): if i % 2 == 1: else: print(sum) 三、编程题 1. 根据用户输入的体重值(要求以 kg 结尾,例如:23.5 kg),系统输出对应的体重管理提示 语。如果用户的输入不符合要求,系统提示“请输入正确的体重值。”。体重管理提示语与体重 的对应关系如下: 0 <= 体重 <= 95:您还需要多吃点! 95 < 体重 <= 115:身材不错,继续保持! 95 < 体重 <= 体重 > 115:您该运动减肥啦! 2. 对一元二次方程 ax 2+bx+c=0 求解,其求解的一般计算公式如下: x= -b ± b2-4ac 2a 具体要求如下: (1)用户在同一行输入参数“a,b,c”(中间用,分隔)。 (2)如果用户的输入包括字符,系统提示“请输入数字!”。 (3)根据实际情况给出提示并输出结果(下面示例中的 X 和 Y 为系统求得的结果,结果保 留两位小数,标点符号为中文标点符号)。例如: A. “有一个实根:X。” B. “有两个实根:X,Y。” C. “没有实根。” 3. 计算 1!+2!+3!+4!+5!+6!+7!+8! 的结果。 4. 使用 for 循环找出 500~999 范围内的所有回文数。 5. 打印输出数字金字塔。 66 第5章 Python 的组合 数据类型 学习目标 (1)理解列表的特点,掌握列表的基本操作和常用方法。 (2)理解元组的特点,掌握元组的基本操作和常用方法。 (3)理解集合的特点,掌握集合的基本操作、集合的运算。 (4)理解字典的特点,掌握字典的基本操作和常用方法。 (5)学会使用列表迭代和列表解析。 (6)通过讲解可以把不同的数据类型组合在一起构成列表、元组、集合和字典,从而引出 不同的学生可组合在一起构成一个整体,进而培养学生的团队意识和集体荣誉感,让学生明白一 个集体都需要每个成员遵守相应的规则。 章节导学 在前面的章节中,学习了整数类型、浮点数类型和复数类型,这些类型仅能表示一个数据, 这种表示单一数据的类型称为基本数据类型。然而,实际编程中却存在大量同时处理多个数 据的情况,这就需要将多个数据有效地组织起来并统一表示,这种能够同时表示多个数据的 数据类型称为组合数据类型。组合数据类型能够将多个相同类型或者不同类型的数据组织起 来,通过单一的表示使数据操作更有序、更容易。在本章中,将学习如何使用 Python 的组合 数据类型,以便在程序设计时有更多、更有效的解决问题的方案。 5.1 Python 的组合数据类型概述 根据数据组织方式的不同,Python 的组合数据类型可分成三类:序列类型、集合类型和映射 类型。 1. 序列类型 序列类型是一个元素向量,元素之间的存在先后关系,通过序号访问,元素之间不排它。 Python 中的序列主要有三种:字符串、列表和元组。 2. 集合类型 集合类型是一个元素类型,元素之间无序,相同元素在集合中唯一存在。Python 集合与数学 中的集合概念一致,要求放入集合中的元素必须是不可变类型。Python 中的整型、浮点型、字符 串类型和元组属于不可变类型,列表、字典及集合本身都属于可变的数据类型。对于所有的数据 67 Python 程序设计 类型而言,它们只要能进行哈希运算,就可以作为集合中的元素出现。 3. 映射类型 映射类型是键 - 值数据项的组合,每个元素是一个键值对,表示为 key:value。Python 中同样 采用“键 - 值”这种形式存储数据间的映射关系。字典是 Python 唯一的内建映射类型。 列表 5.2 列表(list)属于序列类型的数据。序列类型是一维元素向量,元素之间存在先后关系,通过 序号访问。 列表是包含零个或多个对象引用的有序序列。列表的长度和内容都是可变的,列表中的数据 可自由项进行增加、删除或替换。列表没有长度限制,元素类型也可以不同,使用灵活。 5.2.1 列表的基本特点和操作 列表常量用方括号表示,例如 [54,"Python 3", [48, 21] , 90]。列表的基本特点如下: (1)列表可以包含任意类型的对象,如数字、字符串、列表、元组或其他对象。 (2)列表是一个有序序列。与字符串类似,可通过位置偏移量执行列表的索引和切片操作。 (3)列表是可变的。列表的长度是可变的,即可以添加或删除列表成员。列表元素的值也 是可变的。 (4)每个列表元素存储的是对象的引用,而不是对象本身。 列表的基本操作包括:创建列表、索引、求长度、合并、重复、遍历列表、关系判断和切片等。 1. 创建列表 在 Python 3 中,创建列表的方法可分为两种:一种使用列表常量,标记“[ ]”,来创建;另 一种使用 list() 函数创建。 【例 5-1】创建列表示例。 >>>[ ] [ ] >>> [" 歌尔股份 "," 海康威视 "," 海天味业 "] [' 歌尔股份 ', ' 海康威视 ', ' 海天味业 '] >>> [" 歌尔股份 ",3600," 海天味业 "] [' 歌尔股份 ', 3600, ' 海天味业 '] >>> list() [ ] >>> list(" 歌尔股份 ") [' 歌 ', ' 尔 ', ' 股 ', ' 份 '] >>> list(range(5)) [0, 1, 2, 3, 4] >>> list((5,7,9)) [5, 7, 9] >>> [x*2 for x in range(5)] [0, 2, 4, 6, 8] # 创建空列表对象 # 用相同类型数据创建列表对象 # 用不同类型数据创建列表对象 # 创建空列表对象 # 用可迭代对象创建列表对象 # 用连续整数创建列表对象 # 用元组创建列表对象 # 用解析结构创建列表对象 2. 索引、访问列表元素和修改列表元素 与字符串类似,序列中的每个元素都分配一个数字,即它的位置(或索引),可以通过索引 来访问列表中的元素,也可通过索引修改列表中的元素。除了正向索引列表中的元素外,也可以 逆向索引列表的元素,用元素下标 -1 表示最后一个元素,-2 表示倒数第二个元素,同样注意不 68 第 5 章 Python 的组合数据类型 能超出列表长度的限制。 【例 5-2】列表的索引、访问列表元素和修改列表元素示例。 >>> stocks = [" 歌尔股份 "," 海康威视 "," 海天味业 "] >>> stocks[0] ' 歌尔股份 ' >>> stocks[1] ' 海康威视 ' >>> stocks[-1] ' 海天味业 ' >>> stocks[2] = " 伊利股份 " >>> stocks [' 歌尔股份 ', ' 海康威视 ', ' 伊利股份 '] # 输出列表的第 1 个元素 # 输出列表的第 2 个元素 # 从列表末尾开始逆向索引 # 修改列表的第 3 个元素 3. 求长度 可以使用 len() 方法求列表的长度。 【例 5-3】求列表长度示例。 >>> len([" 歌尔股份 "," 海康威视 "," 海天味业 "]) 3 >>> len(list()) 0 4. 合并 使用加法运算符“+”可以将多个列表合并。 【例 5-4】使用“+”合并列表示例。 >>> [" 歌尔股份 "," 海康威视 "]+[" 美的集团 "," 海天味业 "] [' 歌尔股份 ', ' 海康威视 ', ' 美的集团 ', ' 海天味业 '] >>> list(range(5)) + list((5,6,7)) [0, 1, 2, 3, 4, 5, 6, 7] 5. 重复 使用“*”运算符,可重复一个列表多次。 【例 5-5】使用“*”合并列表示例。 >>> [" 歌尔股份 "," 海康威视 "," 海天味业 "] * 2 [' 歌尔股份 ', ' 海康威视 ', ' 海天味业 ', ' 歌尔股份 ', ' 海康威视 ', ' 海天味业 '] >>> [4,5,6] * 4 [4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6] 6. 遍历列表 遍历列表可以逐个处理列表中的元素。常使用 for 循环遍历列表,主要有以下两种方法。 (1)遍历列表的方法,隐藏了列表的长度,操作最为简单。 【例 5-6】for 循环遍历列表示例。 >>> stocks = [" 歌尔股份 "," 海康威视 "," 海天味业 "] >>> for lst in stocks: #lst 依次取列表的每一个元素 print(lst) 程序运行结果: 歌尔股份 海康威视 海天味业 69 Python 程序设计 (2)遍历方法使用 len() 方法,计算出列表的长度后进行遍历操作。 【例 5-7】使用 len() 方法遍历列表示例。 >>> stocks = [" 歌尔股份 "," 海康威视 "," 海天味业 "] >>> for i in range(len(stocks)): print(stocks[i]) 程序运行结果: 歌尔股份 海康威视 海天味业 其中,range() 函数返回的是从 0 到列表 stocks 长度的数值序列。 7. 关系判断 使用 in 和 not in 操作符,判断对象是否属于列表。 【例 5-8】使用 in 和 not in 操作符进行关系判断示例。 >>> stocks = [" 歌尔股份 "," 海康威视 "," 海天味业 "] >>> " 贵州茅台 " in stocks False >>> " 贵州茅台 " not in stocks True >>> " 海天味业 " in stocks True 8. 切片 与字符串类似,可通过切片获得列表中多个元素,也可以通过切片将列表中多个元素替换成 新的元素。 【例 5-9】列表的切片示例。 >>> stocks = [" 歌尔股份 "," 海康威视 "," 美的集团 "," 海天味业 "," 贵州茅台 "] >>> stocks[1:3] # 返回切片列表,不包括下标为 4 的元素 [' 海康威视 ', ' 美的集团 ', ' 海天味业 '] >>> stocks[1:] # 省略切片结束位置时,切片直至列表末尾 [' 海康威视 ', ' 美的集团 ', ' 海天味业 ', ' 贵州茅台 '] >>> stocks[:2] # 省略切片开始位置时,切片从第 1 个元素开始 [' 歌尔股份 ', ' 海康威视 ', ' 美的集团 '] >>> stocks[0:3:2] # 指定切片时的步长为 2 [' 歌尔股份 ', ' 美的集团 '] >>> stocks[3:1:-1] # 步长为负数时,从列表末尾开始切片 [' 贵州茅台 ', ' 海天味业 ', ' 美的集团 '] >>> stocks[1:4:-1] # 步长为负数时,切片的开始位置大于结束位置 [ ] >>> stocks[::-1] # 省略切片开始、结束位置,步长为 -1,反转列表中的元素 [' 贵州茅台 ', ' 海天味业 ', ' 美的集团 ', ' 海康威视 ', ' 歌尔股份 '] >>> stocks[2:3] = [" 恒瑞医药 "," 爱尔眼科 "] # 通过切片替换列表多个数据 >>> stocks [' 歌尔股份 ', ' 海康威视 ', ' 恒瑞医药 ', ' 爱尔眼科 ', ' 贵州茅台 '] 5.2.2 列表常用方法 Python 为列表对象提供了一系列处理方法,用于完成列表元素的增、删、改等操作。 70 第 5 章 Python 的组合数据类型 1. 为列表增加元素 (1)添加单个元素。 append(x) 方法用于在列表末尾追加一个元素 x。 【例 5-10】使用 append(x) 方法为列表添加单个元素示例。 >>> stocks = [" 歌尔股份 "," 海康威视 "," 美的集团 "] >>> stocks.append(" 海天味业 ") >>> stocks [' 歌尔股份 ', ' 海康威视 ', ' 美的集团 ', ' 海天味业 '] (2)在列表末尾添加多个元素。 extend(lst) 方法用于在列表末尾添加多个元素,参数 lst 为可迭代对象。 【例 5-11】使用 extend(lst) 方法为列表添加多个元素示例。 >>> stocks.extend([" 爱尔眼科 "," 中公教育 "]) >>> stocks [' 歌尔股份 ', ' 海康威视 ', ' 美的集团 ', ' 海天味业 ', ' 爱尔眼科 ', ' 中公教育 '] (3)在列表指定位置插入元素。 insert(i,x) 方法用于在列表的下标为 i 的位置插入元素 x。 【例 5-12】使用 insert(i,x) 方法在指定的位置向列表插入元素示例。 >>> stocks.insert(3," 三一重工 ") >>> stocks ['歌尔股份', '海康威视', '美的集团', '三一重工', '海天味业', '爱尔眼科', '中公教育'] 2. 删除列表元素 (1)删除列表中的指定元素。 remove(x) 方法用于删除列表出现的第一个匹配 x 的元素。 【例 5-13】使用 remove(x) 方法删除列表指定元素示例。 >>> stocks.remove(" 三一重工 ") >>> stocks [' 歌尔股份 ', ' 海康威视 ', ' 美的集团 ', ' 海天味业 ', ' 爱尔眼科 ', ' 中公教育 '] (2)删除列表中指定位置的元素。 del 语句用于删除列表中指定位置的元素或切片。 【例 5-14】使用 del 语句删除列表元素示例。 >>> del stocks[2] # 删除下标为 2 的元素 >>> stocks [' 歌尔股份 ', ' 海康威视 ', ' 海天味业 ', ' 爱尔眼科 ', ' 中公教育 '] >>> del stocks[2:3] # 删除下标为 2、3 的元素 >>> stocks [' 歌尔股份 ', ' 海康威视 ', ' 中公教育 '] (3)删除并返回列表中指定位置的元素。 pop(i) 方法用于删除列表中指定位置的元素,同时返回被删除的元素;省略参数 i 时,删除列 表的最后一个元素,并返回该元素。 【例 5-15】使用 pop(i) 方法删除并返回列表元素示例。 >>> stocks.pop() ' 中公教育 ' # 删除并返回最后一个元素 71 Python 程序设计 >>> stocks [' 歌尔股份 ', ' 海康威视 '] >>> stocks.pop(1) ' 海康威视 ' >>> stocks [' 歌尔股份 '] # 删除并返回下标为 1 的元素 (4)删除列表中所有的元素。 clear() 方法用于删除列表中所有的元素。 【例 5-16】使用 clear() 方法清空列表示例。 >>> stocks.clear() >>> stocks [ ] 3. 复制列表 copy() 方法用于复制生成一个新的列表。 【例 5-17】使用 copy() 方法复制列表示例。 >>> stocks = [" 歌尔股份 "," 海康威视 "," 美的集团 "," 海天味业 "," 贵州茅台 "] >>> stocks_2 = stocks.copy() >>> stocks_2 [' 歌尔股份 ', ' 海康威视 ', ' 美的集团 ', ' 海天味业 ', ' 贵州茅台 '] 4. 列表排序 (1)sort() 方法可用于对列表排序。若列表元素全部是数字,则将数字从大到小排序。若列 表元素全部是字符串,则按字典顺序排序。若列表包含多种类型,则会出错。 【例 5-18】使用 sort() 方法排序列表示例。 >>> stocks.sort() # 对字符串列表排序 >>> stocks [' 歌尔股份 ', ' 海天味业 ', ' 海康威视 ', ' 美的集团 ', ' 贵州茅台 '] >>> price = [60.8,74,120.1,65] >>> price.sort() # 对数字列表排序 >>> price [60.8, 65, 74, 120.1] >>> x = [" 歌尔股份 "," 海康威视 "," 美的集团 ",60.8,74,120.1] >>> x.sort() # 对不同类型元素列表排序时出错 Traceback (most recent call last): File "", line 1, in x.sort() TypeError: '<' not supported between instances of 'float' and 'str' sort() 方法默认按从小到大排序,可以用 reverse 参数指定按从大到小排序。 【例 5-19】使用 sort() 方法由大到小排序示例。 >>> stocks.sort(reverse = True) >>> stocks [' 贵州茅台 ', ' 美的集团 ', ' 海康威视 ', ' 海天味业 ', ' 歌尔股份 '] (2)可用 reverse() 方法反转列表中的元素。 【例 5-20】使用 reverse() 方法反转列表示例。 >>> stocks.reverse() >>> stocks 72 第 5 章 Python 的组合数据类型 [' 歌尔股份 ', ' 海天味业 ', ' 海康威视 ', ' 美的集团 ', ' 贵州茅台 '] 5.2.3 Python 的列表生成式 Python 3 内置非常简单,却很强大,可以用来创建 list 的生成式。列表生成式又称为列表解析。 列表生成式的结构是在一个中括号里包含一个表达式,然后是一个 for 语句,然后是 0 个或 多个 for 或者 if 语句。列表表达式可以是任意的,即可以在列表中放入任意类型的对象。返回结 果将是一个新的列表,在这个以 if 和 for 语句为上下文的表达式运行完成之后产生。 列表生成式的格式如下: [ expression for i in 序列 if …] == 表达式 + 循环 + 条件 【例 5-21】生成一个列表,列表元素分别为 [1 ** 1,2 ** 2,…,i**i,…,9 ** 9]。 # 一般方法 list1 = [ ] for i in range(1,10): list1.append(i*i) print(list1) # 用列表生成式 list2 = [i*i for i in range(1,10)] print(list2) 【例 5-22】找出 1~100 之间能被 3 和 7 整除的数,并将其平方返回一个列表。 >>> list2 = [i**2 for i in range(1,100) if i%3 == 0 and i%7 == 0] >>> list2 [441, 1764, 3969, 7056] 5.2.4 提升训练 【训练内容】 选股池是股票投资组合,也就是通常说的不要将鸡蛋放在一个篮子里。将一笔钱分散投资到 几支股票,有利于降低投资风险。选股池是投资者的股票蓄水池,通过分析筛选把需要关注的股 票放入这个池子,并跟踪关注它们的基本面和估值的变化,从而更容易抓住良好的建仓机会。进 入选股池的股票不一定是要购买的股票,但是选股池的股票提供了持续关注的可能,一旦行业发 生积极的变化或者估值跌到很低的水平,就可能提供了绝佳的买入机会。 交易者结合市场结构制定操作策略,然后根据操作策略进行相应的选股,建立股票池。下面 编写 Python 程序,模拟交易者,建立选股池,并对选股池进行一系列的操作:添加股票、遍历、 合并、查找、淘汰、清空、排序等。 任务一 建立选股池 【设计实现思路】 选股池用于存放投资者跟踪的多只股票,并且选股池的股票和大小要能够根据需要而改变, 因此,列表适合作为选股池存储的数据结构。下面创建两个列表对象,分别表示高科技类和消费 类选股池。其中,消费类选股池为空列表,由投资者添加看好的股票。最后,遍历列表,分别打 印两个选股池清单。 73 Python 程序设计 【参考代码】 print("——建立选股池——") highTech = [" 歌尔股份 "," 海康威视 "," 启明信息 "," 比亚迪 "," 隆基股份 "," 韦尔股份 "] consumer = list() for i in range(6): tempStock = input(" 请输入您看好的消费类个股:") if tempStock in consumer: # 检查该只股票是否已经添加到选股池中 print(" 已添加 {}".format(tempStock)) else: consumer.append(tempStock) print(" 高科技类选股池:") for lst in highTech: # 遍历列表,打印选股池清单 print(lst) print(" 消费类选股池:") for i in range(len(consumer)): print(consumer[i]) 任务二 对选股池进行操作 【设计实现思路】 选股池一旦建立,就可以根据投资者的需要对选股池进行一系列的操作,例如: (1)合并现有的两个选股池,产生名称为“全部自选”的选股池。 (2)在高科技类选股池中查找指定的个股。 (3)淘汰指定的消费类个股。 (4)删除高科技类选股池最后 3 只股票。 (5)清空“全部自选”的选股池。 (6)对消费类选股池股按从大到小进行排序。 对选股池即列表进行操作,使用列表的基本操作和常用方法即可实现。可以使用操作符“+”、 in 方法实现合并与查找,使用 remove(x) 方法、del 语句、clear() 方法删除列表元素、清空列表, 使用 sort() 方法排序列表。 【参考代码】 allStock = highTech+consumer # 合并列表,产生新的选股池 print("——全部自选——") # 打印全部自选清单 for lst in allStock: print(lst) tempStock = input(" 请输入您要查找的高科技类个股:") if tempStock in highTech: # 查找指定的个股 print("{} 在高科技类选股池中 ".format(tempStock)) else: print("{} 不在高科技类选股池中 ".format(tempStock)) tempStock = input(" 请输入您要淘汰的消费类个股:") if tempStock in consumer: # 判断指定的个股是否存在 consumer.remove(tempStock) print(" 已从消费类选股池中删除 {}".format(tempStock)) else: print("{} 不在消费类选股池中 ".format(tempStock)) del highTech[-3:] # 淘汰高科技类选股池最后 3 只股票 print("——更新后高科技类选股池——") 74 第 5 章 Python 的组合数据类型 for lst in highTech: print(lst) allStock.clear() # 清空全部自选选股池 print(" 全部自选选股池:{} 个 ".format(len(allStock))) consumer.sort(reverse = True) # 对消费类选股池进行排序 print("——排序后医药类选股池——") d = iter(consumer) for i in range(len(consumer)): print(next(d)) 程序运行后,部分结果如下: ——建立选股池—— 请输入您看好的消费类个股:贵州茅台 请输入您看好的消费类个股:美的集团 请输入您看好的消费类个股:伊利股份 请输入您看好的消费类个股:长城汽车 请输入您看好的消费类个股:海天味业 请输入您看好的消费类个股:牧原股份 ——高科技类选股池—— 歌尔股份 海康威视 启明信息 比亚迪 隆基股份 韦尔股份 元组 5.3 元组(tuple)是包含 0 个或多个元素的不可变序列类型。元组生成后是固定的,其中任意元 素都不能被替换或删除。元组与列表的区别在于元组中的元素不能被修改。元组常量用圆括号表 示,例如 (1,)、(9,6,2)、("a","b","c","abc")。 需要注意的是,只有一个元素的元组,在定义时必须加一个逗号“,”来消除歧义。 5.3.1 元组的基本特点和操作 元组基本上是不可变的列表,几乎具有列表的所有特性,除了那些违反元组不变性的特点。 元组的主要特点如下: (1)元组可包含任意类型的对象。 (2)元组是有序的,元组中的元素可通过位置进行索引和切片。 (3)元组的大小不能改变,既不能为元组添加元素,也不能删除元组中的元素。 (4)元组中的元素不能被改变,也就是说不能修改元组元素的值。 (5)元组中存储的是对象的应用,而不是对象本身。 元组的基本操作与列表类似,包括:创建元组、求长度、合并、重复、遍历元组、关系判断、 索引和分片等。 1. 创建元组 用逗号隔开的就是元组,但是为了美观和代码可读性,我们一般会加小括号。在 Python 中, 元组通常都是使用一对小括号将所有元素括起来的,但小括号不是必须的,只要将各元素用逗号 75 Python 程序设计 隔开,Python 就会将其视为元组,也会使用 tuple() 函数创建。 【例 5-23】创建元组示例。 >>> () () >>> tuple() () # 创建一个对象的元组,不能缺少逗号 >>> (" 定量分析 ",) (' 定量分析 ',) >>> tuple(" 财务报表分析 ") (' 财 ', ' 务 ', ' 报 ', ' 表 ', ' 分 ', ' 析 ') >>> t1 = 1,2,3 >>>type(t1) >>> t2 = 1, >>>type(t2) >>> tuple(list(range(5))) (0, 1, 2, 3, 4) >>> tuple(x**2 for x in range(5)) (0, 1, 4, 9, 16) # 创建空元组对象 # 创建空元组对象 # 用字符串创建元组 # 没有小括号 # 创建一个元素的元组时必须加逗号 # 用列表创建元组对象 # 用解析结构创建元组对象 2. 求长度 可以使用 len() 方法求元组长度。 【例 5-24】求元组长度示例。 >>> len((" 定量分析 "," 经济学 "," 财务报表分析 ")) 3 >>> len(tuple()) 0 3. 合并 加法运算符“+”可以将多个元组合并,此操作将产生一个新的元组,但原来的元组不会被改变。 【例 5-25】合并元组示例。 >>> (" 定量分析 "," 经济学 ") + (" 财务报表分析 ",) + (" 公司理财 "," 权益投资 ") (' 定量分析 ', ' 经济学 ', ' 财务报表分析 ', ' 公司理财 ', ' 权益投资 ') 4. 重复 “*”运算符可重复一个元组多次。 【例 5-26】元组重复示例。 >>> (" 定量分析 "," 经济学 ") * 3 (' 定量分析 ', ' 经济学 ', ' 定量分析 ', ' 经济学 ', ' 定量分析 ', ' 经济学 ') 5. 遍历元组 遍历元组与遍历列表的方法类似,可以逐个处理元组中的元素,使用 for 循环遍历元组,主 要有两种方式,如例 5-27 所示。 【例 5-27】两种遍历元组的方法示例。 # 第一种遍历元组的方法 >>> cfa = (" 定量分析 "," 经济学 "," 财务报表分析 "," 公司理财 "," 权益投资 ") 76 第 5 章 >>> for tup in cfa: print(tup) 定量分析 经济学 财务报表分析 公司理财 权益投资 Python 的组合数据类型 # tup 依次取元组的每一个元素 # 第二种遍历元组的方法 >>> for i in range(len(cfa)): print(cfa[i]) 定量分析 经济学 财务报表分析 公司理财 权益投资 # 使用 len() 方法求出元组的长度 6. 关系判断 可使用 in 和 not in 操作符判断对象是否属于元组。 【例 5-28】关系判断示例。 >>> cfa = (" 定量分析 "," 经济学 "," 财务报表分析 "," 公司理财 "," 权益投资 ") >>> " 定量分析 " in cfa True >>> " 报表分析 " in cfa False >>> " 经济学 " not in cfa False 7. 索引 与列表类似,可以通过位置来索引元组元素,但是不可以通过索引修改元组元素。除了正向 索引元组中的元素外,也可以逆向索引元组的元素。用元素下标 -1 表示最后一个元素,-2 表示 倒数第二个元素,同样注意不能超出元组长度的限制。 【例 5-29】元组索引示例。 >>> cfa[0] ' 定量分析 ' >>> cfa[1] ' 经济学 ' >>> cfa[-1] ' 权益投资 ' # 输出元组的第 1 个元素 # 输出元组的第 2 个元素 # 从元组末尾开始逆向索引 8. 切片 与列表类似,可通过切片获得元组中连续多个元素。但是,不可以通过切片将元组连续多个 元素替换成新的元素。 【例 5-30】元组切片示例。 >>> cfa = (" 定量分析 "," 经济学 "," 财务报表分析 "," 公司理财 "," 权益投资 ") >>> cfa[1:3] # 返回切片元组,不包括下标为 3 的元素 (' 经济学 ', ' 财务报表分析 ', ' 公司理财 ') >>> cfa[2:] # 省略切片结束位置时,切片直至元组末尾 (' 财务报表分析 ', ' 公司理财 ', ' 权益投资 ') 77 Python 程序设计 >>> cfa[:2] # 省略切片开始位置时,切片从第 1 个元素开始 (' 定量分析 ', ' 经济学 ', ' 财务报表分析 ') >>> cfa[0:3:3] # 指定切片时的步长为 3 (' 定量分析 ', ' 公司理财 ') >>> cfa[4:1:-2] # 步长为负数时,从元组末尾开始切片 (' 权益投资 ', ' 财务报表分析 ') # 省略切片开始、结束位置,步长为 -1,反转元组中的元素 >>> cfa[::-1] (' 权益投资 ', ' 公司理财 ', ' 财务报表分析 ', ' 经济学 ', ' 定量分析 ') 5.3.2 元组常用方法 元 组 一 旦 创 建, 是 不 能 被 修 改 的, 因 此 append()、extend()、insert()、remove()、pop()、 reverse()、sort() 等方法都不能用于元组。但是,元组对象支持 count() 和 index() 方法。 1. count() 方法 count() 方法用于返回指定值在元组中出现的次数。 【例 5-31】求指定值在元组中出现的次数示例。 >>> tup = (" 定量分析 "," 经济学 ")*3 >>> tup >>> tup.count(" 经济学 ") 3 >>> tup.count(" 公司理财 ") 0 2. index(value[,start[,end]]) 方法 index(value[,start[,end]]) 方法用于在元组中查找指定的值。指定范围时,返回指定值在范围内 第一次出现的位置;未指定范围时,返回指定值在元组中第一次出现的位置。 【例 5-32】在元组中查找指定的值示例。 >>> tup = (" 定量分析 "," 经济学 ") * 3 >>> tup (' 定量分析 ', ' 经济学 ', ' 定量分析 ', ' 经济学 ', ' 定量分析 ', ' 经济学 ') >>> tup.index(" 经济学 ") # 查找全部元组 1 >>> tup.index(" 定量分析 ",3) # 从下标 4 开始到元组末尾查找 3 >>> tup.index(" 经济学 ",2,5) # 在下标 [2:5] 范围内查找 3 >>> tup.index(" 公司理财 ") # 元组不包含指定的值,则出错 Traceback (most recent call last): File "", line 1, in tup.index(" 公司理财 ") ValueError: tuple.index(x): x not in tuple 5.3.3 元组和列表的转换 元组与列表类似,只是元组中的元素值不能被修改。如果需要修改元组的元素值,可以将元 组转换为列表,修改完后,再转换为元组。 list(tup) 方法可以将元组转换为列表,参数 tup 是要转换的元组对象。 tuple(lst) 方法可以将列表转换为元组,参数 lst 是要转换的列表对象。 78 第 5 章 Python 的组合数据类型 【例 5-33】元组与列表的转换示例。 >>> tup = (" 定量分析 "," 经济学 ") * 3 >>> tup (' 定量分析 ', ' 经济学 ', ' 定量分析 ', ' 经济学 ', ' 定量分析 ', ' 经济学 ') >>> lst = list(tup) # 将元组 tup 转换为列表 lst >>> lst [' 定量分析 ', ' 经济学 ', ' 定量分析 ', ' 经济学 ', ' 定量分析 ', ' 经济学 '] >>> lst[1] = " 西方经济学 " # 修改 lst 下标为 1 的元素 >>> lst [' 定量分析 ', ' 西方经济学 ', ' 定量分析 ', ' 经济学 ', ' 定量分析 ', ' 经济学 '] >>> tuple(lst) # 将列表 lst 转换为元组 (' 定量分析 ', ' 西方经济学 ', ' 定量分析 ', ' 经济学 ', ' 定量分析 ', ' 经济学 ') 5.3.4 提升训练 【训练内容】 王小明是某商学院经济学专业的学生,即将开始大学二年级的学习旅程。开学前,王小明收 到了新学期的课程表。请编写 Python 程序,帮助王小明同学管理课程清单,包括课程清单的创建、 合并、查找和增加。 任务一 创建课程清单 【设计实现思路】 一般情况下,每学期的课程由相关文件决定,且具有不可随意更改的特征。课程可分为 必修课和选修课。根据这些客观情况,结合元组是不可变序列的特点,本例使用元组作为存 储课程的数据结构。创建两个元组对象分别表示必修课和选修课,然后遍历元组对象,打印 课程清单。 【参考代码】 required_course = (" 政治经济学 "," 经济数学 "," 金融市场学 "," 会计学原理 ") elective_course = tuple([" 证券市场法律法规 "," 计算机程序设计语言 "]) print(" 必修课:") for course in required_course: # 遍历元组对象 print(course) print("\n 选修课:") for course in elective_course: print(course) 任务二 管理课程清单 【设计实现思路】 接下来,对课程清单进行一系列简单的操作,具体有: (1)统计王小明同学一共要学习多少门课程? (2)在课程清单中,查找指定的课程,如果找到,输出“存在”,否则,输出“不存在”。 (3)王小明同学在开学后,选修了一门新课,请将该课程添加到课程清单中。 由于元组是不可改变的序列,适合用于为了保护数据安全,不可随意修改数据的场景。本例中, 对课程清单的管理只限于统计和查找,可以使用 len() 函数和 index(value[,start[,end]]) 方法实现。在 特殊情况下,若必须修改元组,则需要结合列表才能实现。 79 Python 程序设计 【参考代码】 all_course = required_course + elective_course # 合并元组对象 print("\n 王小明共需要学习 %d 门课程 "%len(all_course)) find_course = input(" 请输入要查找的课程:") try: n = all_course.index("find_course") # 查找元组对象中指定的元素 print("%s 课程存在 "%find_course) except: print("%s 课程不存在 "%find_course) new_course = input(" 请输入选修课程:") list_course = list(all_course) list_course.append(new_course) all_course = tuple(list_course) print(" 全部课程:") for course in all_course: print(course) # 将元组转换为列表 # 向列表中添加元素 # 将列表转换为元组 任务一、任务二的程序运行后,结果如下: 必修课: 政治经济学 经济数学 金融市场学 会计学原理 选修课: 证券市场法律法规 计算机程序设计语言 王小明共需要学习 6 门课程 请输入要查找的课程:经济学 经济学课程不存在 请输入选修课程:电影赏析 全部课程: 政治经济学 经济数学 金融市场学 会计学原理 证券市场法律法规 计算机程序设计语言 电影赏析 5.4 集合 集合(set)不同于列表和元组类型,集合存储的元素是无序且不能重复的。元素类型只能是 不可变数据类型,如整数、浮点数、字符串、元组等。由于元素间没有顺序,因此不能比较,不 能排序。 5.4.1 集合基本特点和操作 集合常量用大括号表示,例如,{1,3.14, "abc"}。集合的基本特点如下: (1)集合是 0 个或多个元素的无序组合,因此,没有索引位置的概念,因此集合没有索引 80 第 5 章 Python 的组合数据类型 和切片。 (2)集合的元素是不能重复的,元素具有唯一性,这是集合的一个重要特点。因此,如果 需要对数据进行去重或数据重复处理,一般可以通过集合来完成。 (3)集合中的元素不可改变,不支持修改,只能是整数、浮点数、字符串、元组等基本的 数据类型。不能将可变对象放入集合中,集合、列表和字典均不能加入集合中。由于元组是不可 改变的,因此,元组可以作为一个元素加入集合中。 (4)集合是可变的,所以可以向集合中添加或移除元素。 集合的基本操作与列表类似,包括:创建集合、求长度、复制集合、添加元素、删除元素、 遍历集合、关系判断等。 1. 创建集合 集合常量用“{ }”表示,可以使用内置的 set() 方法创建集合。set() 方法最多有一个参数,如 果没有参数,则会创建一个空集合,如果只有一个参数,那么参数必须是可迭代的类型,例如列 表或字符串。注意:{ } 表示空字典对象,而不是空集合。 【例 5-34】创建集合示例。 >>> s = {1,4,8,9} >>> s {8, 1, 4, 9} >>> set() set() >>> set([4,5,6,7,8]) {4, 5, 6, 7, 8} >>> set("abcdef") {'c', 'f', 'd', 'a', 'e', 'b'} >>> set({1,4,8,9}) {8, 1, 4, 9} >>> set([4,8,9,8,4]) {8, 9, 4} >>> {5,5,5,5,5} {5} >>> type({ }) # 直接使用集合常量 # 创建空集合 # 使用列表创建集合 # 使用字符串创建集合 # 使用集合常量创建集合 # 自动去除集合中重复值 #{ } 表示空字典 从运行结果可以看出,集合中的重复值被自动过滤掉。集合的初始顺序和显示顺序是不同的, 这说明集合中的元素是无序的。 2. 求长度 可以使用 len() 方法求集合长度。 【例 5-35】求集合的长度示例。 >>> s = set(["abc",45,7,9,"d"]) >>> s {7, 9, 'd', 'abc', 45} >>> len(s) 5 >>> x = set() >>> len(x) 0 # 求集合对象 s 中元素的个数 81 Python 程序设计 3. 复制集合 集合中元素的值不支持修改,但是可以使用 copy() 方法复制集合。该函数返回被复制集合的 一个副本。 【例 5-36】复制集合示例。 >>> x = s.copy() >>> x {7, 9, 'd', 'abc', 45} # 复制集合 s 4. 添加元素 集合中元素的值不支持修改,但是可以向集合中添加元素。 (1)添加单个元素。 可以使用 add(x) 方法向集合中添加单个元素 x,如果元素 x 不在集合中,就将该元素添加到 集合中。因此,可以重复添加某个元素,但是不会有效果。 【例 5-37】向集合添加单个元素示例。 >>> s.add("new") >>> s {7, 9, 'd', 'abc', 45, 'new'} >>> s.add("new") >>> s {7, 9, 'd', 'abc', 45, 'new'} # 重复添加元素 new,被过滤掉 (2)添加多个元素。 可以使用 update(x) 方法向集合中添加多个元素,参数 x 为可迭代对象。 【例 5-38】向集合添加多个元素示例。 >>> s.update([40,79]) >>> s {7, 40, 9, 'd', 'abc', 45, 79, 'new'} 5. 删除元素 集合中元素的值不支持修改,但是可以删除集合中的元素。 (1)pop() 方法。 使用 pop() 方法可以随机删除集合中的一个元素,并返回该元素。 【例 5-39】使用 pop() 方法删除集合元素示例。 >>> d = s.pop() >>> d 7 >>> s {40, 9, 'd', 'abc', 45, 79, 'new'} (2)remove(x) 方法。 使用 remove(x) 方法可以从集合中删除指定的元素 x。如果该元素不存在,会产生 KeyError 异常。 【例 5-40】使用 remove(x) 方法删除集合元素示例。 >>> s.remove("d") >>> s {40, 9, 45, 79, 'abc', 'new'} >>> s.remove(80) Traceback (most recent call last): 82 第 5 章 Python 的组合数据类型 File "", line 1, in s.remove(80) KeyError: 80 (3)discard(x) 方法。 使用 discard (x) 方法可以从集合中删除指定的元素 x。如果该元素不存在,不会产生异常。 【例 5-41】使用 discard(x) 方法删除集合元素示例。 >>> s.discard(9) >>> s {40, 45, 79, 'abc', 'new'} >>> s.discard(80) # 删除不存在的元素时不报异常 (4)clear() 方法。 使用 clear() 方法可以清除集合中的所有元素,使之成为空集合。 【例 5-42】使用 clear() 方法清空集合示例。 >>> s.clear() >>> s set() 6. 遍历集合 可以使用 for 循环遍历集合。 【例 5-43】遍历集合示例。 >>> s = set(["abc",45,7,9,"d"]) >>> for a in s: print(a,end=" ") 7 9 45 d abc 7. 关系判断 可使用 in 和 not in 操作符判断对象是否属于集合。 【例 5-44】关系判断示例。 >>> "abc" in s True >>> "b" not in s True >>> 8 in s False 5.4.2 集合的运算 set() 方法创建的集合,与数学中的无序和无重复元素的集合概念是一致的。因此,两个集合 可以做数学意义上的交集、并集、差集、补集等计算。 1. 交集 s.intersaction(t) 方法创建新集合,该集合为 s、t 两个集合的共有部分元素。s&t 也表示取 s、t 两个集合的交集。 【例 5-45】求集合的交集示例。 >>> s = set("Python") >>> t = set("Python 3.0") >>> s.intersection(t) 83 Python 程序设计 {'y', 't', 'o', 'h', 'n', 'P'} >>> s&t {'y', 't', 'o', 'h', 'n', 'P'} 2. 并集 s.union(t) 方法创建新集合,该集合为 s、t 两个集合的所有元素。s|t 也表示取 s、t 两个集合的 并集。 【例 5-46】求集合的并集示例。 >>> s.union(t) {'0', 'n', 'P', '3', 't', ' ', 'o', 'h', '.', 'y'} >>> s|t {'0', 'n', 'P', '3', 't', ' ', 'o', 'h', '.', 'y'} 3. 差集 s.difference(t) 方法创建新集合,该集合包含在集合 s 中但不在集合 t 中的元素。s-t 也表示取 s、 t 两个集合的差集。 【例 5-47】求集合的差集示例。 >>> s.difference(t) set() >>> s - t set() >>> t.difference(s) {'3', ' ', '0', '.'} >>> t - s {'3', ' ', '0', '.'} 4. 补集 s.symmetric_difference(t) 方法创建新集合,该集合为 s、t 两个集合中的元素,但不包括共有部 分的元素。s ^ t 也表示取 s、t 两个集合的补集。 【例 5-48】求集合的补集示例。 >>> s.symmetric_difference(t) {'3', ' ', '.', '0'} >>> s^t {'3', ' ', '.', '0'} 集合运算中的求补集,也称为求对称差。 5. 子集测试 可以使用 s.issubset(t) 方法进行子集测试。如果集合 s 与集合 t 相同,或者集合 s 是集合 t 的子集, 该方法返回 True,否则返回 False。也可以使用 s<=t 进行子集测试。 【例 5-49】子集测试示例。 >>> s.issubset(t) True >>>s<= t True >>> t.issubset(s) False >>>t<= s False 84 第 5 章 Python 的组合数据类型 6. 超集测试 可以使用 s.issuperset(t) 方法进行超集测试。如果集合 s 与集合 t 相同,或者集合 s 是集合 t 的 超集,该方法返回 True,否则返回 False。也可以使用 s>=t 进行子集测试。 【例 5-50】超集测试示例。 >>> s.issuperset(t) False >>> s >= t False >>> t.issuperset(s) True >>> t >= s True 字典 5.5 字典(dict)是 Python 中内置的映射类型。映射,是指每个结构中包含 key-value 的键值对, 即通过 key 可以找到其映射的值 value。在搜索字典时,首先查找键,当查找到键后就可以直接获 得该键对应的值,这是一种高效的查找方法。 字典是一种无序的映射集合,包含一系列的键值对。在 Python 字典中,为了实现快速搜索, 键没有按顺序排列。因此,字典集合是无序的。当添加键值对时,Python 会自动修改字典排列顺序, 以提高搜索效率,且这种排列方式对用户是隐藏的。 5.5.1 字典基本特点和操作 字典包含一系列的键值对,常量用“{ }”表示,每个元素都包含键和值两个部分,例如: {"name":"Python","author":"Gudio" ,"age":32}。其中,字符串 "name"、"author" 和 "age" 为键(key), 字符串 "Python"、"Gudio"、数字 32 为值(value)。字典的主要特点如下: (1)字典的键名一般采用字符串,也可以用数字、元组等不可变类型的数据,同一字典中 各个元素的键必须唯一。 (2)字典的值可以是任意类型的数据。 (3)字典可以嵌套,也就是说字典中键映射的值可以是一个字典。 (4)字典是无序的,通过键映射到值,由键访问映射的值,而不是通过位置来索引的。 (5)字典的映射是可变的,可以修改键映射的值。 (6)字典的长度是可变的,可以添加或删除字典的键值对。 (7)字典存储的是对象的引用,而不是对象本身。 字典的基本操作包括创建字典、求长度、关系判断、字典索引、修改字典元素、添加字典元素、 删除字典元素。 1. 创建字典 字典可以用“{ }”创建,字典中每个元素都包含键和值两个部分,元素之间用逗号分隔,还 可以使用 dict() 函数创建字典。 【例 5-51】创建字典示例。 >>> { } { } # 创建空字典 85 Python 程序设计 >>> dict() # 创建空字典 { } >>> {"course":"Python 编程 ","class":{"school":"The School of Economics", "grade":2021, "major":"finance"}} # 字典的嵌套 {'course': 'Python 编程 ', 'class': {'school': 'The School of Economics', 'grade': 2021, 'major': 'finance'}} >>> {1:"monday",2:"thesday",3:"wednesday"} # 使用数字作为键 {1: 'monday', 2: 'thesday', 3: 'wednesday'} >>> {(1,3,5,7,9):" 奇数 ",(2,4,6,8):" 偶数 "} # 使用元组作为键 {(1, 3, 5, 7, 9): ' 奇数 ', (2, 4, 6, 8): ' 偶数 '} # 使用赋值格式的键值对创建字典 >>> dict(name = "Pyhon",author = "Gudio",age = 32) {'name': 'Pyhon', 'author': 'Gudio', 'age': 32} # 使用包含键值对元组的列表创建字典 >>> dict([("name","Python"),("author","Gudio"),("age",32)]) {'name': 'Python', 'author': 'Gudio', 'age': 32} 2. 求长度 可以使用 len() 方法求字典长度,即字典中键值对的个数。 【例 5-52】求字典长度示例。 >>> len(dict(name = "Pyhon",author = "Gudio",age = 32)) 3 3. 关系判断 可使用 in 和 not in 操作符测试某个键是否存在于字典中。 【例 5-53】关系判断示例。 >>> dict1 = dict([("name","Python"),("author","Gudio"),("age",32)]) >>> "name" in dict1 True >>> "Python" in dict1 #"Python" 是 dict1 中键的值,不是键 False >>> "age" not in dict1 False 4. 字典索引 字典定义好后,可以通过键来索引其映射的值。使用表达式 dicts[key],返回 key 所对应的值。 【例 5-54】字典索引。 >>> dict1["name"] 'Python' >>> dict1["age"] 32 5. 添加与修改字典元素 字典的长度是可变的,可以向字典中添加键值对。字典的映射是可变的,可以修改键映射的值。 添加字典元素与修改字典元素的方法相同,都使用 dicts[key]=value 的形式。如果字典中已经存在 这个键值对,则修改该键映射的值;否则,向字典中添加该键值对。 【例 5-55】添加与修改字典示例。 >>> dict1["author"] = "Guido van Rossum" >>> dict1 86 # 修改字典元素 第 5 章 Python 的组合数据类型 {'name': 'Python', 'author': 'Guido van Rossum', 'age': 32} >>> dict1["version"] = "3.8" # 为不存在的键赋值,添加字典元素 >>> dict1 {'name': 'Python', 'author': 'Guido van Rossum', 'age': 32, 'version': '3.8'} 6. 删除字典元素 字典的长度是可变的,可以通过索引删除字典的键值对。使用 del dicts[key] 语句,删除字典 中指定的键值对。 【例 5-56】使用 del 语句删除字典元素示例。 >>> del dict1["age"] >>> dict1 {'name': 'Python', 'author': 'Guido van Rossum', 'version': '3.8'} 5.5.2 字典常用方法 Python 内置了一系列字典的处理方法。下面通过示例代码介绍这些常用方法。 1. copy() 和 clear() 方法 copy() 方法用于复制字典对象,返回一个字典副本。新的字典与原字典的 id 是不同的。clear() 方法用书删除字典中全部的键值对,使之成为一个空字典。 【例 5-57】复制字典和清空字典示例。 >>> dict2 = dict1.copy() >>> dict2 {'name': 'Python', 'author': 'Guido van Rossum', 'version': '3.8'} >>> dict2["name"] = "Python" # 修改 dict2 的元素 >>> dict2 {'name': 'Python', 'author': 'Guido van Rossum', 'version': '3.8'} >>> dict1 # 对 dict2 的修改,不会影响 dict1 {'name': 'Python', 'author': 'Guido van Rossum', 'version': '3.8'} >>> dict2.clear() >>> dict2 {} 2. get(key,[default]) 方法 get(key,[default]) 方法返回键 key 映射的值。如果键 key 不存在,返回空值。default 参数可以 指定键 key 不存在时返回的值。 【例 5-58】返回字典中指定键的键值示例。 >>> dict1.get("version") '3.8' >>> dict1.get("age") >>> dict1.get("age","1989") '1989' # 键 age 在字典中不存在,返回空值 # 键 age 在字典中不存在,返回默认值 3. setdefault(key,[default]) 方法 setdefault(key,[default]) 方法用于返回键 key 映射的值或者为字典添加键值对。如果键 key 在字 典中存在,则返回其映射的值;如果键 key 在字典中不存在,则将键值对“key:default”添加到字 典中,并返回值 default。省略 default 时,添加的映射值为 None。 87 Python 程序设计 【例 5-59】返回或者添加字典的键值对示例。 >>> dict1.setdefault("name") 'Python' # 返回指定建 name 的值 # 键 website 不存在,为字典添加键值对,映射值默认为 None >>> dict1.setdefault("website") >>> dict1 {'name': 'Python', 'author': 'Guido van Rossum', 'version': '3.8', 'website': None} >>> dict1.setdefault("age",32) # 添加键值对 32 >>> dict1 {'name': 'Python', 'author': 'Guido van Rossum', 'version': '3.8', 'website': None, 'age': 32 } 4. update(other) 方法 update(other) 方法用于实现使用一个字典更新另一个字典。参数 other 可以是一个字典。如果 两个字典有相同的键存在,则键值对会进行覆盖。 【例 5-60】更新字典的键值对示例。 >>> dict2 = {"rank":3,"website":"www.Python.org"} >>> dict2 {'rank': 3, 'website': 'www.Python.org'} >>> dict1.update(dict2) # 使用 dict2 更新 dict1 >>> dict1 #dict1 中同名键 website 映射的值被修改 {'name': 'Python', 'author': 'Guido van Rossum', 'version': '3.8', 'website': 'www.Python.org', 'age': 32, 'rank': 3} update(other) 方法的参数还可以是用赋值格式的键值对。 【例 5-61】使用赋值格式的键值对更新字典的键值对示例。 >>> dict1.update(author = "Guido") # 修改键 author 映射的值 >>> dict1 {'name': 'Python', 'author': 'Guido', 'version': '3.8', 'website': 'www.Python. org', 'age': 32, 'rank': 3} >>> dict1.update(feature = " 简单、优美、开源 ") # 添加键值对 >>> dict1 {'name': 'Python', 'author': 'Guido', 'version': '3.8', 'website': 'www.Python. org', 'age': 32, 'rank': 3, 'feature': ' 简单、优美、开源 '} 5. pop(key,[default]) 和 popitem() 方法 pop(key,[default]) 方法从字典中删除键 key 指定的键值对,并返回被删除的映射值。如果键 key 不存在,就返回默认值 default;如果键 key 不存在且未指定 default 参数,则会产生 KeyError 错误。 popitem() 方法从字典中随机删除一个键值对,并返回被删除键值对元组 (key,value)。空字典 调用该方法会产生 KeyError 错误。 【例 5-62】删除字典的键值对示例。 >>> dict1.pop("feature") ' 简单、优美、开源 ' 88 # 删除键 feature,返回映射值 第 5 章 Python 的组合数据类型 >>> dict1 {'name': 'Python', 'author': 'Guido', 'version': '3.8', 'website': 'www.Python. org', 'age': 32, 'rank': 3} >>> dict1.pop("feature"," 该键不存在 ") # 删除不存在的键,返回默认值 ' 该键不存在 ' >>> dict1.pop("feature") # 删除不存在的键,未指定 default,出错 Traceback (most recent call last): File "", line 1, in dict1.pop("feature") KeyError: 'feature' >>> dict1.popitem() # 随机删除一个键值对 ('rank', 3) >>> dict1 {'name': 'Python', 'author': 'Guido', 'version': '3.8', 'website': 'www.Python. org', 'age': 32} >>> dict2 = dict() #dict2 为空字典 >>> dict2 {} >>> dict2.popitem() # 产生 KeyError 错误 Traceback (most recent call last): File "", line 1, in dict2.popitem() KeyError: 'popitem(): dictionary is empty' 6. keys()、values() 和 items() 方法 使用 keys()、values() 和 items() 方法可以分别返回字典的键视图、值视图和键值对视图。视图 对象与列表不同,不支持索引,但是可以迭代访问,通过遍历视图可以获得字典的信息。 keys() 方法可以返回字典中所有键的视图。 【例 5-63】获取字典的键视图示例。 >>> keys_1 = dict1.keys() # 返回字典 dict1 的键视图 >>> keys_1 # 键视图为 dict_keys 对象 dict_keys(['name', 'author', 'version', 'website', 'age']) >>> for k in keys_1: print(k,end=" ") name author version # 遍历键视图 website age >>> list(keys_1) # 将键视图转换为列表 ['name', 'author', 'version', 'website', 'age'] values() 方法可以返回字典中所有值的视图。 【例 5-64】获取字典的值视图示例。 >>> values_1 = dict1.values() # 返回字典 dict1 的值视图 >>> values_1 # 值视图为 dict_values 对象 dict_values(['Python', 'Guido', '3.8', 'www.Python.org', 32]) >>> for v in values_1: print(v,end=" ") Python Guido 3.8 www.Python.org >>> list(values_1) # 遍历值视图 32 # 将值视图转换为列表 89 Python 程序设计 ['Python', 'Guido', '3.8', 'www.Python.org', 32] items() 方法可以返回字典中所有键值对的视图,每一个键值对为一个元组。 【例 5-65】获取字典的键值对视图示例。 items_1 = dict1.items() # 返回字典 dict1 的键值对视图 >>> items_1 # 键值对视图为 dict_items 对象 dict_items([('name', 'Python'), ('author', 'Guido'), ('version', '3.8'), ('website', 'www.Python.org'), ('age', 32)]) >>> for i in items_1: print(i) ('name', 'Python') ('author', 'Guido') ('version', '3.8') ('website', 'www.Python.org') ('age', 32) # 遍历键值对视图 >>> list(items_1) # 将键值对视图转换为列表 [('name', 'Python'), ('author', 'Guido'), ('version', '3.8'), ('website', 'www.Python.org'), ('age', 32)] 5.5.3 提升训练 【训练内容】 在 5.1.3 小节中建立的选股池仅仅涉及了个股的名称。显然,这在实际应用中是不够的。选股时, 投资者需要知道个股详细信息,包括股票名称、代码、开盘价、最新价、涨跌幅、市值、市盈率、 所属板块等。因此,本案例将对 5.1.3 小节的内容进行扩展,模拟个股信息更为详细的选股池, 进行一系列操作,包括:创建个股、重建选股池、更新股票价格、查看个股属性、添加个股属性、 删除个股属性、模拟投资策略交易个股等。 任务一 创建个股,重建选股池 【设计实现思路】 选股池使用列表数据类型存储,那么个股的详细信息用什么类型的数据存储呢? 个股的详细信息:股票名称、代码、开盘价、最新价、涨跌幅、市值、市盈率等就是个股的 属性和属性值。这样就构成了“键值对”,它表示键和对应的值构成的映射关系,可以通过一个 特定的键(股票代码)来访问值(002241)。由于键不是序号,无法使用列表进行有效存储和索引。 同时,根据字典的特点,选用字典存储个股数据。 【参考代码】 def printStockInfo(stockDict): # 定义函数,输出个股详细信息 # 获取字典的键值对视图,并转换为列表 stockItem = list(stockDict.items()) for item in stockItem: print("{}:{}".format(item[0],item[1])) print("-"*30) # 打印分隔线 print("———重建后的高科技类选股池———") 歌尔股份 ={" 名称 ":" 歌尔股份 "," 代码 ":"002241"," 开盘价 ":46.11," 最新价 ":45.55," 涨跌幅 ":" -2.33%"," 市值 ":"1557 亿 "} # 创建个股的字典 90 第 5 章 Python 的组合数据类型 海康威视 ={" 名称 ":" 海康威视 "," 代码 ":"002415"," 开盘价 ":54," 最新价 ":54.10," 涨跌幅 ":" -0.48%"," 市值 ":"5058 亿 "} 启明信息 ={" 名称 ":" 启明信息 "," 代码 ":"002232"," 开盘价 ":11.32," 最新价 ":11.16," 涨跌幅 ":" -1.24%"," 市值 ":"45.6 亿 "} 隆基股份 =dict( 名称 =" 隆基股份 ", 代码 ="601012", 开盘价 =83.2, 最新价 =83.57, 涨跌幅 =" +2.06%", 市值 ="4521 亿 ") # 使用赋值格式的键值对创建字典 韦尔股份 =dict( 名称 =" 韦尔股份 ", 代码 ="603501", 开盘价 =234.80, 最新价 =230.50, 涨跌幅 =" -1.98%", 市值 ="1996 亿 ") 比亚迪 ={} # 创建空字典 比亚迪 [" 名称 "]=" 比亚迪 " #" 名称 " 键值对不存在,则添加 比亚迪 [" 代码 "]="002594" 比亚迪 [" 开盘价 "]="267.89" 比亚迪 [" 最新价 "]="263.6" 比亚迪 [" 涨跌幅 "]="-2.10%" 比亚迪 [" 市值 "]="7539 亿 " # 创建选股池列表,以个股字典对象为元素 highTech=[ 歌尔股份 , 启明信息 , 海康威视 , 比亚迪 , 隆基股份 , 韦尔股份 ] for stockDict in highTech: # 遍历列表 printStockInfo(stockDict) # 调用函数 任务二 对选股池的个股进行操作 【设计实现思路】 选股池一旦建立,就可以根据投资者的需要对选股池进行一系列的操作,如: (1)更新歌尔股份的最新价。 (2)查看隆基股份的市盈率,如果没有该属性,则添加。 (3)删除比亚迪的开盘价键值对。 (4)为比亚迪添加流通市值键值对。 (5)为海康威视添加流通股本键值对。 (6)查看指定个股的所有信息。 通过任意键信息查找一组数据中值信息的过程成为映射。Python 语言中通过字典实现映射。 字典属于可变映射,可通过键来索引其映射的值,也可以通过索引修改键映射的值。 字典的常用方法 pop(key,[default]) 方法、get(key,[default]) 方法、setdefault(key,[default]) 方法和 update(other) 方法可以实现对字典键值对的查看、添加、删除。 【参考代码】 歌尔股份 [" 最新价 "] = 46.31 # 查看隆基股份的市盈率,如果没有该属性,则添加 隆基股份 .setdefault(" 市盈率 ",46.07) print( 比亚迪 .pop(" 开盘价 "," 该属性不存在 ")) 比亚迪 .update({" 流通市值 ":"2980 亿 "}) 海康威视 .update( 流通股本 ="98.43 亿 ") lookup = input(" 请输入要查看的个股名称:") for stockDict in highTech: if stockDict [" 名称 "] == lookup: printStockInfo(stockDict) break else: print("%s 不在选股池中。"%lookup) # 更新歌尔股份的最新价 # 删除比亚迪的市值键值对 # 为比亚迪添加流通市值键值对 # 为海康威视添加流通股本键值对 # 查看指定个股的所有信息 91 Python 程序设计 任务三 对选股池按个股涨跌幅由高到低排序 【设计实现思路】 目前,选股池的个股按照添加顺序排列,可以利用列表的 sort() 方法对选股池的个股排序。 当 reverse 参数为 True 时,可以按从大到小排序。当用 key 参数指定一个函数,sort() 方法将列表 元素作为参数调用该函数,用函数返回值代替列表元素完成排序。 【参考代码】 def getKey(A): #getKey(A)函数定 义排序规则 b = eval(a[" 涨跌幅 "][0:-1]) # 获取涨跌幅中的数字部分 return b print("———排序前选股池———") for stockDict in highTech: # 输出排序前选股池部分信息 print(stockDict [" 名称 "], stockDict [" 涨跌幅 "]) highTech.sort(key=getKey,reverse=True) # 排序 print("———排序后选股池———") for stockDict in highTech: # 输出排序后选股池部分信息 print(stockDict [" 名称 "], stockDict [" 涨跌幅 "]) 任务四 模拟投资策略进行个股交易 【设计实现思路】 在建立选股池的基础上,投资者持续关注股票重要信息的变动,然后根据投资策略选择目标 股票,进行买入。 在这个环节,最重要的问题就是如何模拟投资者的选股策略?所谓选股策略,本质上就是选 股的一系列条件。这些条件根据股票的属性进行设置。一旦条件满足,该股票成为目标股票,可 以进行买入操作。 如何模拟股票的买入操作呢?在这里,主要有两个操作:①为买入的股票设置标识;②将买 入的股票添加进“持仓股”列表。 【参考代码】 holdStock = [] # 创建 " 持仓股 " 空列表 # 选股策略:最新价大于 100,市值大于 500 亿 for lst in highTech: latestPrice = lst.get(" 最新价 ") marketValue = eval(lst.get(" 市值 ")[0:-1]) if latestPrice > 100 and marketValue > 500: lst.update( 当前状态 =" 持有 ") # 为符合条件个股添加标识键值对 holdStock.append(lst) # 将符合条件个股添加进 " 持仓股 " print("———我的持仓———") for stockDict in holdStock: # 打印持仓清单 printStockInfo(stockDict) 程序运行后,部分结果如下: ———重建后的高科技类选股池——— 名称 : 歌尔股份 代码 :002241 开盘价 :46.11 最新价 :45.55 涨跌幅 :-2.33% 92 第 5 章 Python 的组合数据类型 市值 :1557 亿 -----------------------------名称 : 启明信息 代码 :002232 开盘价 :11.32 最新价 :11.16 涨跌幅 :-1.24% 市值 :45.6 亿 ------------------------------ 习 题 一、选择题 1. 下列选项中,存在语法错误的是 。 (A)dict1={1:"one",2:"two"} (B)dict1={"A":65,"B":66} (C)dict1={{1,2}:"tuple",[1,2]:"list"} (D)dict1={"tuple":(1,2),"list":[1,2]} 2. 下列类型的对象属于不可变序列的是 。 (A)集合 (C)列表 (D)元组 (C)7 (D)5 (C)"c" (D)程序出错 (B)字典 3. 运行下面语句后,输出结果是 。 lst=["a","b","c",1,2] lst.extend("123") print(len(lst)) (A)6 (B)8 4. 运行下面语句后,输出结果是 。 x = 4 lst = ["a","b","c",1,2] y = lst[x<6] print(y) (A)"a" (B)"b" 5. 若 s='Python 程序 设计 语言 ',要输出成 ['Python', ' 程序 ', ' 设计 ', ' 语言 '] 形式,应使用语 句 。 (A)print(s.split()) (B)print(s.encode()) (C)print(s.endswith()) (D)print("".join(s)) 6. 下列关于元组和列表的描述错误的是 。 (A)元组中的元素值是不允许删除的,但列表元素值可以删除 (B)元组作为很多内置函数和序列类型方法的返回值存在 (C)列表比元组的访问和处理速度更快 (D)列表和元组都是序列类型,但列表属于可变序列,元组属于不可变序列 7. 设列表对象 list1 的值为 [1,2,3,4,5,6],那么执行 list1[2::-2] 得到的值是 (A)[3,1] (B)[ ] (C)[3,5] 。 (D)[3,2,1] 8. 设元组对象 tuple2 的值为 (2,4,3,5,4,6,7,8,6,1),那么 tuple2[8:2:-4] 得到的值是 (A)[6,4] (B)(4,6) (C)6,4 。 (D)(6,4) 93 Python 程序设计 9. 表达式 {4,6,5}<={5,6,4} 的值为 。 (A)True (B)False 10. 若 lst=["a","b","c","d"],要输出成 ["d","c","b","a"] 形式,应使用语句 (A)lst.sort() (B)lst.copy() (C)lst.reverse() 。 (D)lst.clear() 二、填空题 1. 表达式 {x**2 for x in [3,2,3,1]} 的值为 。 2. 若 list1 中存放 5 个元音字母,现发现 'x' 不是,请删除,'e' 是,请添加进去,最后按字母 从大到小排列顺序。请补全横线处的代码。 list1=['i','x','u','a','o'] list1. ('x') list1. ('e') list1. print(list1) 3. dict1 中存放截至 2020 年 2 月 1 日 24 时全国部分省确诊新型冠状病毒感染的肺炎人数最新 数据实时统计,广东省核减 1 例,把每个省的肺炎人数数据存在列表 lst1 中,返回湖北的数值。 请补全横线处的代码。 dict1 = {' 四川 ':231,' 山东 ':225,' 江西 ':333,' 河南 ':493,' 湖南 ':463,' 江苏 ':262,' 浙江 ' :661,' 重庆 ':236,' 广东 ':604,' 湖北 ':9074,' 北京 ':183} = 604-1 # 广东省核减 1 例 lst1 = list(dict1. ()) # 把每个省的数据存在列表 lst1 中 print(list1) print(dict1. (' 湖北 ')) # 返回上海的数值 print(dict1) 4. 以下代码定义了一个统计字符串中含有 'a' 的单词的函数,请补充横线处的代码。 def findwords(txt): result = [ ] words = txt. for word in words: if word. ('a') != -1: result. return result (word) # 如果包含 'a',则返回开始的索引值,否则返回 -1 5. dict1 中存放各个国家的国花,现发现:澳大利亚的多写一个,请把 ' 桉树 ' 这个键值删除; ' 菊花 ' 不是美国的国花,而是日本的,请更正过来;把中国的国花 ' 牡丹 ' 添加上去。请补全横 线处的代码。 dict1 = {'矢车菊':'德国','金合欢':'澳大利亚','桉树':'澳大利亚','玫瑰':'英国','菊花': ' 美国 ',' 野百合 ':' 智利 '} dict1. (' 桉树 ') = ' 日本 ' = ' 中国 ' print(dict1) 三、编程题 1. 请计算集合 setA:{8,6,7,4,5} 和集合 setB:{1,2,3,4} 两个集合的并集、交集和差集。 94 第 5 章 Python 的组合数据类型 2. 从键盘输入一个列表,并删除列表中重复的元素。 3. 下面的表格是 3 位同学 3 门课程的考试成绩,其中的成绩都是整数。格式见表 5-1。 表 5-1 3 位同学 3 门课程的考试成绩 姓名 大学英语 高等数学 Python 小张 65 84 90 小李 92 95 88 小王 88 79 70 编写程序,完成下列操作: (1)将每个学生的高等数学考试成绩增加 5 分。注意:成绩最高为 100 分。 (2)按照每个学生的 Python 成绩从高到低排序,输出排序结果,如下所示: 名次 姓名 大学英语 高等数学 Python 1 小张 65 89 90 2 小李 92 100 88 3 小王 88 84 70 (提示:将每个学生的成绩作为一个字典对象存入列表,用列表 sort() 完成自定义排序。) 4. 编写程序,在 26 个大小写字母和 9 个数字组成的列表中随机生成 10 个 6 位的密码。 95 第6章 Python 文件操作 学习目标 (1)理解文件的概念和特点。 (2)理解 CSV 文件的基本概念。 (3)掌握文件打开和关闭的方法。 (4)掌握文本文件的读 / 写方法。 (5)掌握二进制文件的读 / 写方法。 (6)掌握文件目录的操作方法。 (7)掌握 CSV 文件读 / 写数据的方法。 (8)通过文件读取的操作,让学生养成认真仔细的习惯。语法上的任何错误都可能导致文 件读写的失败,要遵守语法规则,养成严谨的科学作风。在生活中要遵纪守法,严格遵守各项 规章制度,坚持走中国特色社会主义道路,增强国家意识和使命感。 章节导学 文件是保存于存储介质中的数据集合。按存储格式,文件分为文本文件和二进制文件。 Python 程序打开文件,可以从文件读取数据,也可以向文件写入数据。用户在处理文件的过 程中,除了读 / 写文件中的数据,还可以管理文件目录。本章将详细介绍文本文件、二进制 文件和 CSV 文件的概念以及这些文件的打开、读 / 写和关闭的方法。 文件 6.1 文件是数据的集合,以文本、图像、音频、视频等形式存储在计算机的外部介质中。Python 3 使用文件对象来处理文件。 6.1.1 文件的概述 根据文件的存储格式不同,文件可以分为文本文件和二进制文件两种形式。 1. 文本文件 文本文件由字符组成,这些字符按 ASCII 码、UTF-8 或 Unicode 等格式进行编码。文本文件 可以被多种编辑软件创建、修改和阅读。常见的编辑软件有记事本、Notepad++ 等。Windows 记 事本创建的 .txt 格式的文件就是典型的文本文件。以 .py 为扩展名的 Python 源文件、以 .html 为 96 第 6 章 Python 文件操作 扩展名的网页文件等都是文本文件。文本文件按字符读取文件,一个字符占用一个或多个字节, 整个文件可以看作一个长字符串。 2. 二进制文件 二进制文件存储的是数据的二进制代码,由 0 和 1 组成。二进制文件没有字符编码,存储格 式与用途有关。二进制文件通常用于保存图像、音频和视频等数据。典型的二进制文件包括:.bmp 格式的图片文件、.avi 格式的视频文件、各种计算机语言编译后生成的文件等。二进制文件通常 按字节读取文件。 无论是文本文件还是二进制文件,都可以用“文本文件方式”和“二进制文件方式”打开, 但打开后的操作是不同的。 按文本文件格式读取数据时,Python 会根据字符编码将数据解码为字符串,即将数据转换为 有意义的字符。按二进制文件格式读取数据时,数据为字节流,Python 不执行解码操作,读取的 数据作为 Bytes 字符串,并按 Bytes 字符串的格式输出。 6.1.2 文件的打开和关闭 无论是文本文件还是二进制文件,进行文件的读 / 写操作时,都需要先打开文件,操作结束 后再关闭文件。打开文件是指将文件从外部存储介质读取到内存中,文件被当前程序占用,其他 程序不能操作这个文件。在某些写文件的模式下,当打开不存在的文件时,Python 程序可以创建 该文件。 文件操作之后需要关闭文件,释放程序对文件的占用和控制,将文件数据存储到外部介质, 其他程序才能够操作这个文件。 1. 文件指针 Python 使用文件指针表示当前读 / 写文件的位置。以只读方式打开文件时,文件指针会指向 文件开头;以覆盖写或追加写的方式打开文件时,文件指针会指向文件末尾。Python 始终在文件 指针的位置读 / 写数据,读取或写入一个数据后,根据数据的长度,向后移动文件指针。在文件 的读 / 写过程中,文件指针是自动移动的。可以使用 tell() 方法获取文件指针的位置,使用 seek() 方法移动文件指针的位置。 2. 打开文件 Python 使用内置的 open() 函数来打开文件,实现该文件与一个程序变量的关联,即返回一个 文件对象。通过该对象,对文件进行各种操作。open() 函数的基本格式如下: myfile = open(filename[,mode]) 其中,myfile 为引用文件对象的变量名;filename 为文件名字符串,可以是文件的实际名字, 也可以是包含完整路径的名字;mode 为文件的读 / 写模式,通过读 / 写模式决定将要对文件采取 的操作。文件读 / 写模式见表 6-1。 表 6-1 文件读 / 写模式 读 / 写模式 说 明 r 只读模式。该模式打开的文件必须存在;如果不存在,返回异常 FileNotFoundError;默认值 r+ 读 / 写模式。该模式打开的文件必须存在;如果不存在,返回异常 FileNotFoundError w 覆盖写模式。文件不存在,则创建;存在则完全覆盖 97 Python 程序设计 读 / 写模式 说 续上表 明 w+ 读 / 写模式。文件存在,则原来的文件被覆盖 a 追加写模式。写入的内容追加到文件尾。文件不存在则创建;存在则在文件最后追加内容 a+ 读 / 写模式。写入的内容追加到文件尾,还可以读取文件数据 x 创建写模式。文件不存在则创建;存在则返回异常 FileExistsError t 文本文件模式。按文本文件格式读 / 写数据,默认值 rb 二进制只读模式。按二进制格式从文件读取数据 rb+ 二进制读 / 写模式。文件指针将会放在文件的开头 wb 二进制覆盖写模式 wb+ 二进制读 / 写模式。文件存在,原来的文件被覆盖;不存在,则创建 ab 二进制追加写模式 ab+ 二进制读 / 写模式。文件存在,文件指针将会放在文件的末尾;不存在,则创建 【例 6-1】打开文件示例。 #open() 函数默认采用 "rt"(文本只读)模式打开文件,文件不存在时报异常 >>> myfile_1 = open("notes.txt") # 打开项目文件夹中不存在的 notes.txt 文件时,出现异常 Traceback (most recent call last): File "", line 1, in myfile_1=open("notes.txt") FileNotFoundError: [Errno 2] No such file or directory: 'notes.txt' >>> myfile_1 = open(r"d:\example6\notes.txt","r") # 以只读模式打开文件,指明文件的路径 >>> myfile_2 = open(r"d:\python38\test.py","w+") # 以读 / 写模式打开文件,指明文件的路径 >>> myfile_3 = open(r"d:\example6\idea.jpg","rb") # 以读的模式打开二进制文件 # 以读 / 写的模式打开二进制文件,如果文件不存在,则创建该文件 >>> myfile_4 = open("newTu.bmp","ab+") 3. 关闭文件 通常情况下,Python 操作文件时,使用内存缓冲区缓存文件数据。关闭文件时,Python 将缓 冲的数据写入文件,然后关闭文件,并释放对文件的引用。可以使用 close() 方法关闭文件,基本 语法格式如下: myfile.close() flush() 方法可将缓冲区的数据写入文件,但不关闭文件,基本语法格式如下: myfile.flush() 6.1.3 读 / 写文本文件 如果以文本文件方式打开文件,程序会按照当前系统的编码方式来读 / 写文件,用户也可以 指定编码方式来读 / 写文件。文本文件相关的读 / 写方法见表 6-2。 98 第 6 章 Python 文件操作 表 6-2 文本文件的读 / 写方法 方 法 说 明 myfile.read([size]) 读取文件指针位置开始到文件末尾的全部内容;如果给出参数 size,读取文件指针位 置开始的前 size 长度的字符串 myfile.readline([size]) 从文件指针位置开始读取一行内容;如果给出参数,读取该行前 size 长度的字符串 myfile.readlines() 读取文件指针位置开始到文件末尾的所有行内容,将读取每一行字符串作为列表的一 个元存放到列表中 myfile.write(s) myfile.writelines(lst) myfile.seek(offset) myfile.tell() 在文件指针位置写入一个字符串 将列表中的元素合并为一个字符串写入文件指针位置 移动文件指针的位置;offset 的值为 0,文件指针位于文件开头;offset 的值为 2,文件 指针位于文件结尾 返回文件指针的当前位置 文本文件按字符读取数据。文本文件每行末尾以回车换行符号结束,在读取的字符串中, Python 用“\n”代替回车换行符号。 下面访问文本文件“d:\notes.txt”,以该文件为例,详细说明如何进行文本文件的读 / 写操作。 文件内容如下: 第一节:大学英语 第二节:概率论与统计 第三节:货币银行学 1. 以“r”模式打开文件读数据 以“r”(只读)模式打开文本文件时,文件指针位于文件开头,只能从文件中读取数据。 【例 6-2】以“r”模式打开文件读数据示例。 >>> myfile = open(r"d:\example6\notes.txt","r") # 以只读模式打开文本文件 >>> myfile.tell() # 返回文件指针的当前位置 0 >>> content = myfile.read() # 读取文件的全部内容 >>> content ' 第一节:大学英语 \n 第二节:概率论与统计 \n 第三节:货币银行学 ' >>> print(content) # 打印读取出来的文件内容 第一节:大学英语 第二节:概率论与统计 第三节:货币银行学 >>> myfile.tell() # 获得文件指针位置 58 >>> myfile.read() # 指针已指向文件末尾,返回空字符串 '' >>> myfile.seek(0) # 将文件指针移到文件开头 0 >>> myfile.read(4) # 读取 4 个字符 99 Python 程序设计 ' 第一节:' >>> myfile.tell() # 返回文件指针的当前位置 8 >>> myfile.readline() # 读取文件指针位置到当前行末尾的字符串 ' 大学英语 \n' >>> myfile.readline() # 读取下一行 ' 第二节:概率论与统计 \n' >>> myfile.seek(0) 0 >>> myfile.readline(2) # 读取文件指针所在行的前 2 个字符 ' 第一 ' >>> myfile.seek(0) 0 >>> myfile.readlines() # 将全部文件内容读到列表中 [' 第一节:大学英语 \n', ' 第二节:概率论与统计 \n', ' 第三节:货币银行学 '] >>> myfile.close() # 关闭文件 2. 以“r+”模式打开文件读 / 写数据 以“r+”模式打开文本文件时,文件指针指向文件开头,可从文件读取数据,也可向文件写 入数据。 【例 6-3】以“r+”模式打开文件示例。 >>> myfile = open(r"d:\example6\notes.txt","r+") >>> myfile.read() # 读取全部数据 ' 第一节:大学英语 \n 第二节:概率论与统计 \n 第三节:货币银行学 ' >>> myfile.write("\n 第四节:体育 ") # 在文件末尾写入数据 7 >>> myfile.seek(0) 0 >>> myfile.read() ' 第一节:大学英语 \n 第二节:概率论与统计 \n 第三节:货币银行学 \n 第四节:体育 ' >>> myfile.seek(0) 0 >>> myfile.read(10) # 读取 10 个字符 ' 第一节:大学英语 \n 第 ' >>> myfile.tell() # 查看文件指针的位置 20 >>> myfile.write("\n 午休:2 个小时 ") 12 >>> myfile.seek(0) 0 >>> myfile.read() ' 第一节:大学英语 \n 第二节:概率论与统计 \n 第三节:货币银行学 \n 第四节:体育 \n 午休:2 个小时 ' >>> myfile.close() 以“r+”模式打开文本文件,在执行完读操作后,立即执行写操作时,不管文件指针的位置 在哪里,都将数据写入文件末尾。要在指定位置写入数据,需要先执行 seek() 函数移动指针到指 定位置,然后写入数据。 另外,在向文本文件写入需要换行的数据时,应在字符串适当的位置嵌入“\n”,否则数据 不会自动换行。 100 第 6 章 Python 文件操作 3. 以“w”模式打开文件写数据 以“w”模式打开文本文件时,只能向文件写入数据。如果文件存在,原来的数据会被覆盖; 如果文件不存在,会创建一个新文件。 【例 6-4】以“w”模式打开文件示例。 >>> myfile = open(r"d:\example6\notes.txt","w") # 文件存在,原来的数据被覆盖 >>> myfile.read() # 以 "w" 模式打开文件, 读数据出错 Traceback (most recent call last): File "", line 1, in myfile.read() io.UnsupportedOperation: not readable >>> myfile.write(" 第一节:大学英语 \n") 9 >>> myfile.writelines([" 第二节:概率论与统计 \n"," 第三节:货币银行学 \n"]) # 将列表元素写入文件 >>> myfile.tell() # 查看指针位置:在文件末尾 60 >>> myfile.close() # 关闭文件 >>> myfile = open(r"d:\example6\notes.txt") # 重新打开文件,读取数据 >>> myfile.read() ' 第一节:大学英语 \n 第二节:概率论与统计 \n 第三节:货币银行学 \n' >>> myfile.close() 4. 以“w+”模式打开文件读 / 写数据 以“w+”模式打开文本文件时,允许同时读 / 写文件。如果文件存在,原来的数据会被覆盖; 如果文件不存在,会创建一个新文件。 【例 6-5】以“w+”模式打开文件示例。 >>> myfile = open(r"d:\example6\data.txt","w+") >>> myfile.read() # 新建文件,没有数据,返回空字符串 '' >>> myfile.write("1.C++\n") # 将字符串写入文件 6 >>> myfile.writelines(["2.java\n","3.Python\n","4.PHP\n"]) >>> myfile.seek(0) 0 >>> myfile.readline() # 读第 1 行 '1.C++\n' >>> myfile.readline() # 读第 2 行 '2.java\n' >>> myfile.readline() # 读第 3 行 '3.Python\n' >>> myfile.readline() # 读第 4 行 '4.PHP\n' >>> myfile.close() 5. 以“a”模式打开文件追加数据 以“a”模式打开文件时,只能向文件写入数据。文件打开时文件指针指向文件末尾,写入 的数据始终添加到文件的末尾。 101 Python 程序设计 【例 6-6】以“a”模式打开文件追加数据示例。 >>> myfile = open(r"d:\example6\data.txt","a") >>> myfile.read() # 以 "a" 模式打开文件,读数据出错 Traceback (most recent call last): File "", line 1, in myfile.read() io.UnsupportedOperation: not readable >>> myfile.seek(8) # 文件指针指向第 8 个字符 >>> myfile.write("5.javascript\n") # 仍然在文件末尾写入数据 12 >>> myfile.close() >>> myfile = open(r"d:\example6\data.txt") # 以只读方式打开文件 >>> myfile.read() '1.C++\n2.java\n3.Python\n4.PHP\n5.javascript\n' >>> myfile.close() 6. 以“a+”模式打开文件读 / 写数据 以“a+”模式打开文件时,除了可以向文件写入数据,还可以读取文件数据。文件打开时文 件指针指向文件末尾,写入的数据始终添加到文件的末尾。 【例 6-7】以“a+”模式打开文件示例。 >>> myfile = open(r"d:\example6\data.txt","a+") >>> myfile.tell() # 文件指针在文件末尾 46 >>> myfile.write("6.Scala\n") # 在文件末尾写入数据 8 >>> myfile.seek(0) 0 >>> myfile.read() '1.C++\n2.java\n3.Python\n4.PHP\n5.javascript\n6.Scala\n' >>> myfile.seek(6) # 将文件指针移到第 6 个字符之后 6 >>> myfile.write("7.Go 语言 \n") # 在文件末尾写入字符串 7 >>> myfile.seek(0) 0 >>> myfile.read() '1.C++\n2.java\n3.Python\n4.PHP\n5.javascript\n6.Scala\n7.Go 语言 \n' 以“a”和“a+”模式打开文件,不管文件指针在哪个位置,向文件写入的数据始终添加在 文件的末尾。 7. 遍历文件 Python 将文件看作由行组成的序列,可以通过迭代的方式遍历文件,逐行读取文件的内容。 【例 6-8】遍历文件示例。 >>> myfile = open(r"d:\example6\notes.txt","r") >>> for line in myfile: print(line) 第一节:大学英语 第二节:概率论与统计 第三节:货币银行学 102 第 6 章 Python 文件操作 >>> myfile.close() 因为文本文件每行结尾处都有换行符“\n”,print() 语句默认的结尾符号也是回车换行符“\n”, 所以,在输出文件内容时,行和行之间增加了空行。可以用 end 参数为 print() 函数指定结尾符号 以解决这个问题。 【例 6-9】用 end 参数指定结尾符号的遍历文件示例。 >>> myfile = open(r"d:\example6\notes.txt","r") >>> for line in myfile: print(line,end="") 第一节:大学英语 第二节:概率论与统计 第三节:货币银行学 >>> myfile.close() 6.1.4 读 / 写二进制文件 读 / 写文本文件的各个方法同样适用于读 / 写二进制文件,但二进制文件只能读 / 写 bytes 字 符串。传统字符串加前缀“b”构成了 bytes 对象,即 bytes 字符串,可以写入二进制文件。整型、 浮点型、序列等数据类型在写入二进制文件前,需要先转换为字符串,再使用 bytes() 方法转换为 bytes 字符串,之后再写入文件。 默认情况下,二进制文件是按顺序读 / 写的,可以使用 seek() 方法和 tell() 方法移动和查看文 件指针的当前位置。 【例 6-10】向二进制文件读 / 写字符串示例。 >>> bfile = open(r"d:\example6\test.py","wb") # 以 "wb" 模式创建二进制文件 >>> bfile.write("a=") # 向二进制文件写入字符串出错 Traceback (most recent call last): File "", line 1, in bfile.write("a=") TypeError: a bytes-like object is required, not 'str' >>> bfile.write(b"a=") # 向二进制文件写入 bytes 字符串 2 >>> bfile.write(2) # 向二进制文件写入整数出错 Traceback (most recent call last): File "", line 1, in bfile.write(2) TypeError: a bytes-like object is required, not 'int' # 将整数转换为 bytes 字符串,写入二进制文件 >>> bfile.write(bytes(str(2),encoding="utf-8")) 1 >>> bfile.write(b"\nb=") 3 >>> bfile.write(bytes(str(4.5),encoding="utf-8")) 3 >>> bfile.write(b"\nlst=") 5 # 将列表转换为 bytes 字符串,写入二进制文件 >>> bfile.write(bytes(str([1,2,3]),encoding="utf-8")) 9 103 Python 程序设计 >>> bfile.close() >>> bfile = open(r"d:\test.py","r") >>> print(bfile.read()) a=2 b=4.5 lst = [1, 2, 3] >>> bfile.close() >>> bfile = open(r"d:\test.py","rb") >>> print(bfile.readline()) b'a = 2\n' >>> print(bfile.readline()) b'b = 4.5\n' >>> print(bfile.readline()) b'lst = [1, 2, 3]' >>> bfile.close() 6.1.5 # 以 "r" 模式打开二进制文件 # 打印读取的全部内容 # 以 "rb" 模式打开二进制文件 # 打印读取的第一行 bytes 字符串 目录操作 目录即文件夹,是操作系统用于组织和管理文件的逻辑对象。它存储当前目录中的子目录和 文件的相关数据。Python 的 os 模块提供了常用的目录操作函数,见表 6-3。 表 6-3 os 模块常用的目录操作函数 函数名 功能说明 os.getcwd() 返回当前 Python 脚本的工作路径 os.mkdir(path) 创建参数 path 指定的目录 os.makedirs(path) 创建参数 path 指定的多级目录 os.listdir(path) 返回指定目录 path 下的所有文件和子目录名称 os.chdir(path) 切换当前目录为参数 path 指定的目录 os.rename(old,new) 将目录或文件 old 重命名为 new os.remove(myfile) 删除参数 myfile 指定的文件 os.rmdir(path) 删除参数 path 指定的目录,目录必须为空 os.removedirs(path) 删除参数 path 指定的多级目录 在使用 Python 的 os 模块之前,应该先将其导入,基本语法格式如下: import os 1. 创建与查看目录 Python 程序中可以创建单个目录,也可以创建多级目录。 【例 6-11】创建目录示例。 >>> import os >>> os.getcwd() 104 # 查看 Python 脚本的当前目录 第 6 章 Python 文件操作 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python38' >>> os.chdir(r"d:\example6") # 改变当前目录为 "d:\example6" >>> os.getcwd() 'd:\\example6' >>> os.listdir() # 查看当前目录中的子目录和文件 ['data.txt', 'idea.jpg', 'myTime.log', 'newTu.bmp', 'notes.txt', 'test.py'] >>> os.mkdir("newforder") # 创建单个目录 >>> os.listdir() ['data.txt', 'idea.jpg', 'myTime.log', 'newforder', 'newTu.bmp', 'notes.txt', 'test.py'] >>> os.makedirs("f1/f2/f3") # 创建多级目录 >>> os.listdir() ['data.txt', 'f1', 'idea.jpg', 'myTime.log', 'newforder', 'newTu.bmp', 'notes. txt', 'test.py'] >>> os.chdir("f1") >>> os.listdir() ['f2'] >>> os.chdir("f2") >>> os.listdir() ['f3'] 2. 重命名目录 使用 Python 的 os 模块中的 rename(old,new) 方法可以修改目录的名称。 【例 6-12】重命名目录示例。 >>> os.rename("f3","forder3") >>> os.listdir() ['forder3'] 3. 删除目录 Python 程序中可以删除单个目录,也可以删除多级目录。 【例 6-13】删除目录示例。 >>> os.chdir(r"d:\example6\f1") # 改变当前目录为 "d:\example6\f1" >>> os.rmdir("f2") # 删除子目录 f2,f2 不是空目录,出错 Traceback (most recent call last): File "", line 1, in os.rmdir("f2") OSError: [WinError 145] 目录不是空的。: 'f2' >>> os.chdir("f2") >>> os.rmdir("forder3") >>> os.listdir() [ ] # 改变当前目录为 "d:\example6\f1\f2" # 删除子目录 "forder3" # 子目录 "f2" 为空目录 >>> os.chdir(r"d:\example6\f1") >>> os.rmdir("f2") >>> os.listdir() [ ] # 改变当前目录为 "d:\example6\f1" # 删除子目录 "f2" >>> os.makedirs("f2/f3") # 创建新的多级目录 105 Python 程序设计 >>> os.listdir() ['f2'] >>> os.removedirs("f2/f3") >>> os.listdir() [ ] # 直接删除多级目录 4. 文件的复制 在 Python 中,复制文件可以使用 shutil 模块中的 copyfile(myfile1,myfile2) 函数。shutil 模块在 使用之前,需要先导入。 【例 6-14】文件的复制示例。 >>> import shutil >>> import os >>> os.getcwd() 'd:\\example6\\f1' >>> os.chdir(r"d:\example6") >>> os.listdir() ['data.txt', 'f1', 'idea.jpg', 'myTime.log', 'newforder', 'newTu.bmp', 'notes.txt', 'test.py'] >>> shutil.copyfile("test.py","test2.py") # 复制文件 "test.py" >>> os.listdir() ['data.txt', 'f1', 'idea.jpg', 'myTime.log', 'newforder', 'newTu.bmp', 'notes.txt', 'test.py', 'test2.py'] 5. 文件的重命名 使用 Python 的 os 模块中的 rename(old,new) 方法可以修改文件的名称。如果名称为 old 的文件 不存在,会提示“FileNotFoundError”异常;名称为 new 的文件存在,会提示“FileExistsError”异常。 【例 6-15】文件的重命名示例。 >>> os.rename("test.txt","test1.txt") # 文件 "test.txt" 不存在,出错 Traceback (most recent call last): File "", line 1, in os.rename("test.txt","test1.txt") FileNotFoundError: [WinError 2] 系统找不到指定的文件。: 'test.txt' -> 'test1.txt' >>> os.rename("data.txt","notes.txt") # 文件 "notes.txt" 存在,出错 Traceback (most recent call last): File "", line 1, in os.rename("data.txt","notes.txt") FileExistsError: [WinError 183] 当文件已存在时,无法创建该文件。: 'data.txt' -> 'notes.txt' >>> os.rename("data.txt","mydata.txt") >>> os.listdir() ['f1', 'idea.jpg', 'mydata.txt', 'myTime.log', 'newforder', 'newTu.bmp', 'notes.txt', 'test.py', 'test2.py'] 针对上面的问题,可以使用 os.path 模块的 exists(path) 函数先判断文件是否存在。 【例 6-16】使用 os.path 模块判断文件是否存在示例。 >>> import os.path >>> import os 106 # 导入 os.path 模块 第 6 章 Python 文件操作 >>> os.listdir() ['data.txt', 'f1', 'idea.jpg', 'myTime.log', 'newforder', 'newTu.bmp', 'notes.txt', 'test.py', 'test2.py'] >>> if os.path.exists("data.txt"): if not os.path.exists("mydata.txt"): os.rename("data.txt","mydata.txt") # 判断文件是否存在 # 执行文件复制 >>> os.listdir() ['f1', 'idea.jpg', 'mydata.txt', 'myTime.log', 'newforder', 'newTu.bmp', 'notes.txt', 'test.py', 'test2.py'] 6. 文件的删除 文件删除前,也要使用 os.path.exists(path) 函数先判断文件是否存在。只有文件存在,才可以 执行删除操作。 【例 6-17】文件的删除示例。 >>> if os.path.exists("test2.py"): os.remove("test2.py") >>> os.listdir() ['f1', 'idea.jpg', 'mydata.txt', 'myTime.log', 'newforder', 'newTu.bmp', 'notes.txt', 'test.py'] 6.1.6 提升训练 【训练内容】 生活中不仅有 Python 代码,还有诗和远方。本例将计算机程序之美与中国古诗词之美结合, 使用 Python 读 / 写文本文件“静夜思 .txt”,对古诗《静夜思》进行一系列操作,包括: (1)为了保留原始数据,复制文本文件“静夜思 .txt”,以便进行处理。 (2)将《静夜思》每行诗句的英语译文插入到该行下方。 (3)统计汉字字符、英文字母、空格及其他字符的个数。 (4)逐行打印文本文件内容。 文本文件“静夜思 .txt”的内容如下: 静夜思 李白 床前明月光, 疑是地上霜。 举头望明月, 低头思故乡。 任务一 复制文本文件“静夜思 .txt” 【设计实现思路】 在复制文本文件“静夜思 .txt”之前,使用 os.path.exists(path) 函数判断文件是否存在。若文 件存在,使用 shutil 模块中的 copyfile(myfile1,myfile2) 函数实现文件的复制。 【参考代码】 import os.path import shutil 107 Python 程序设计 if os.path.exists(" 静夜思 .txt"): shutil.copyfile(" 静夜思 .txt","Thoughts on a Tranquil Night.txt") else: print(" 静夜思 .txt 不存在 ") 任务二 将《静夜思》每行诗句的英语译文插入到该行下方 【设计实现思路】 实现将新的字符串插入到文本文件指定的位置,首先需要在打开文本文件时选用读写数据的 模式;其次,将文本文件的内容读取到一个列表中;然后,将每一行英语诗句插入到列表相应的 位置;最后,将列表重新写回文本文件,以实现文本文件的修改。 在这里,要注意在向列表插入元素时,列表大小处于更新状态,插入新元素的位置要根据实 际情况设置。 【参考代码】 myfile = open("Thoughts on a Tranquil Night.txt","r+") content_lst=myfile.readlines() # 将文本文件内容读取到列表中 content_lst.insert(1,"Thoughts on a Tranquil Night\n") content_lst.insert(4,"Before my bed a pool of light—\n") content_lst.insert(6,"O can it be hoar-frost on the ground?\n") content_lst.insert(8,"BLooking up, I find the moon bright;\n") content_lst.insert(10,"\nBowing, in homesickness I'm drowned.\n") content_lst.insert(11,"(许渊冲 译 2006)") myfile.seek(0) # 移动文件指针到文件开头 myfile.writelines(content_lst) # 将修改后的列表重新写回文本文件 任务三 统计汉字字符、英文字母、空格及其他字符的个数 【设计实现思路】 实现统计文本文件中各类字符的个数,首先要将文本文件内容读出来,存放到一个字符串对 象中。遍历该字符串,使用字符串函数 isalpha() 找出汉字字符和英文字母的个数,使用字符串函 数 isspace() 找出空格的个数,剩下的就是其他字符个数。其中,使用 if...else... 选择结构区分出汉 字汉字与英文字母。 【参考代码】 myfile.seek(0) content_str = myfile.read() # 读取文本文件全部内容,存放在 content_str count_en = count_zh = count_space = count_other = 0 for i in content_str: if i.isalpha(): if (i>="a" and i<="z") or (i>="A" and i<="Z"): # 找出英文字母 count_en += 1 else: # 剩下的字符是汉字 count_zh += 1 elif i.isspace(): count_space += 1 else: count_other += 1 print(" 汉字 :{}, 英文字母 :{}, 空格 :{}, 其他字符 :{}".format(count_zh,count_en, count_space,count_other)) 108 第 6 章 Python 文件操作 任务四 逐行打印文本文件内容 【设计实现思路】 在前面操作的基础上,实现逐行将文本文件的内容打印出来还是比较简单的。直接打印存放 文本文件全部内容的字符串对象 content_str 即可。 【参考代码】 print(content_str) myfile.close() # 打印字符串对象 content_str # 关闭文件 程序运行后,结果如下: 汉字 :29 个 , 英文字母 :132 个 , 空格 :40 个 , 其他字符 :18 个 静夜思 Thoughts on a Tranquil Night 李白 床前明月光, Before my bed a pool of light— 疑是地上霜。 O can it be hoar-frost on the ground? 举头望明月, Blooking up, I find the moon bright; 低头思故乡。 Bowing, in homesickness I'm drowned. (许渊冲 译 2006) 6.2 6.2.1 读 / 写 CSV 文件 CSV 文件的概念 CSV 是逗号分隔值的英文 Comma-Separated Values 的缩写,是一种通用的、相对简单的文本 文件格式。CSV 文件由任意数目的行组成,一行被称为一条记录。记录之间用换行符分隔。每条 记录由若干数据项组成,这些数据项被称为字段。字段间的分隔符通常是逗号,也可以是空格、 制表符、其他字符或字符串。CSV 文件有以下特点: (1)纯文本格式,通过单一编码表示字符。 (2)以行为单位,开头不留空行,行之间没有空行。 (3)每行表示一个一维数据,多行表示二维数据。 (4)以逗号(英文,半角)分隔每列数据,列数据为空也要保留逗号。 (5)对于表格数据,可以包含或不包括列名,包含列名放置在文件的第一行。 通常,CSV 文件所有记录都有完全相同的字段序列。第 1 行记录为记录的各个字段名称,第 2 行开始为记录数据。下面是一个典型的 CSV 文件的内容。 名称 , 代码 , 现价 天宇股份 ,300702,60.00 海南海药 ,000556,4.72 精华制药 ,002349,5.98 科兴制药 ,688136,27.40 鱼跃医疗 ,002223,34.67 CSV 格式存储的文件一般采用 .csv 为扩展名。CSV 文件是纯文本文件,可以用 Windows 记事 109 Python 程序设计 本按照 CSV 文件的规则来创建,也可以使用 Office Excel 录入数据,另存为 CSV 格式即可。保存 CSV 文件时,如使用 UTF-8 编码格式,使用 open() 函数打开文件时,应通过设置参数“encoding=utf-8” 指定编码格式,这样 Python 程序才能正确读取其中的汉字,否则汉字会乱码。 Python 的 csv 模块提供了 CSV 文件的读 / 写功能,可以通过 import csv 语句导入 csv 模块。csv 模块最常用的方法是 csv.reader() 和 csv.writer(),分别用于读和写 CSV 文件。 6.2.2 读 CSV 文件数据 cvs 模块提供了两种读取器对象来读取 CSV 文件数据,分别是常规读取器和字典读取器。 1. 常规读取器 csv 模块中的 reader() 函数用于创建常规读取器对象,基本语法格式如下: csvreader = csv.reader(csvfile,delimiter = " 分隔符 ") 其中,变量 csvreader 用于引用读取器对象;csvfile 是 open() 函数返回的文件对象;delimite 参 数指定 CSV 文件使用的数据分隔符,默认为逗号。 常规读取器对象是一个可迭代对象,其每次迭代返回一个包含一行数据的列表,列表元素对 应 CSV 记录的各个字段。可用 for 循环或 next() 函数迭代常规读取器对象。 【例 6-18】迭代常规读取器对象示例。 >>> import csv >>> myfile = open(r"d:\example6\stocks.csv",encoding="utf-8") >>> myreader = csv.reader(myfile,delimiter=",") # 创建常规读对象 >>> for n in myreader: # 迭代读取 CSV 文件 print(n) # 输出每一行数据的列表 [' 名称 ', ' 代码 ', ' 现价 '] [' 天宇股份 ', '300702', '60.00'] [' 海南海药 ', '000556', '4.72'] [' 精华制药 ', '002349', '5.98'] [' 科兴制药 ', '688136', '27.40'] [' 鱼跃医疗 ', '002223', '34.67'] >>> myfile.seek(0) 0 >>> next(myreader) [' 名称 ', ' 代码 ', ' 现价 '] >>> next(myreader) [' 天宇股份 ', '300702', '60.00'] >>> next(myreader) [' 海南海药 ', '000556', '4.72'] >>> next(myreader) [' 精华制药 ', '002349', '5.98'] >>> myfile.seek(0) 0 >>> content = "" >>> for row in myreader: for item in row: content += item+"\t" content += "\n" 110 # 使用 next() 函数迭代读取 CSV 文件的每一行 # 迭代包含每一行数据的列表 # 增加数据项的间距 # 增加换行 第 6 章 >>> print(content) Python 文件操作 # 输出整洁的二维数据 名称 代码 现价 天宇股份 300702 60.00 海南海药 000556 4.72 精华制药 002349 5.98 科兴制药 688136 27.40 鱼跃医疗 002223 34.67 >>> myfile.close() 2. 字典读取器 csv 模块中的 DictReader() 函数用于创建字典读取器对象,基本语法格式如下: csvreader = csv.DictReader(csvfile) 其中,变量 csvreader 用于引用读取器对象;csvfile 是 open() 函数返回的文件对象。 字典读取器对象是一个可迭代对象,每次迭代返回一个包含一行数据的排序字典对象 (OrderedDict,即排好序的字典对象)。字典读取器对象默认将 CSV 文件第 1 行作为字段名称, 将字段名称作为字典的键。CSV 文件中第 2 行开始的每行数据顺序作为键映射的值。可用 for 循 环或 next() 函数迭代字典读取器对象。 【例 6-19】迭代字典读取器对象示例。 >>> import csv >>> myfile = open(r"d:\example6\stocks.csv",encoding="utf-8-sig") >>> csvDictReader = csv.DictReader(myfile) >>> for row in csvDictReader: print(row) # 输出包含 CSV 文件数据行的字典对象 {' 名称 ': ' 天宇股份 ', ' 代码 ': '300702', ' 现价 ': '60'} {' 名称 ': ' 海南海药 ', ' 代码 ': '000556', ' 现价 ': '4.72'} {' 名称 ': ' 精华制药 ', ' 代码 ': '002349', ' 现价 ': '5.98'} {' 名称 ': ' 科兴制药 ', ' 代码 ': '688136', ' 现价 ': '27.4'} {' 名称 ': ' 鱼跃医疗 ', ' 代码 ': '002223', ' 现价 ': '34.67'} >>> myfile.seek(0) 0 >>> for row in csvDictReader: print(row[" 名称 "],row[" 代码 "],row[" 现价 "],sep="\t") # 用 " 键 " 索引输出数据 天宇股份 300702 60 海南海药 000556 4.72 精华制药 002349 5.98 科兴制药 688136 27.4 鱼跃医疗 002223 34.67 >>> myfile.seek(0) 0 >>> next(csvDictReader) {' 名称 ': ' 名称 ', ' 代码 ': ' 代码 ', ' 现价 ': ' 现价 '} >>> next(csvDictReader) {' 名称 ': ' 天宇股份 ', ' 代码 ': '300702', ' 现价 ': '60'} >>> next(csvDictReader) {' 名称 ': ' 海南海药 ', ' 代码 ': '000556', ' 现价 ': '4.72'} >>> next(csvDictReader) 111 Python 程序设计 {' 名称 ': ' 精华制药 ', ' 代码 ': '002349', ' 现价 ': '5.98'} >>> myfile.close() 6.2.3 将数据写入 CSV 文件 可以使用常规写对象或字典写对象向 CSV 文件写入数据。 1. 用常规写对象写数据 常规写对象由 csv.writer() 函数创建,基本语法格式如下: csvwriter = csv.writer(csvfile) 其中,变量 csvwriter 用于引用写对象,csvfile 是 open() 函数返回的文件对象。 写对象的 writerow() 用于向 CSV 文件写入一行数据,基本语法格式如下: csvwriter.writerow(data) 其中,data 是一个列表对象,其中包含一行 CSV 数据。将数据写入 CSV 后,writerow() 方法 会在每行数据末尾添加两个换行符号。 【例 6-20】用常规写对象写数据示例。 >>> import csv >>> myfile = open(r"d:\example6\stocks.csv","w") >>> mywriter = csv.writer(myfile) >>> mywriter.writerow([" 名称 "," 代码 "," 现价 "]) 10 >>> mywriter.writerow([" 天宇股份 ","300702",60]) 16 >>> mywriter.writerow([" 海南海药 ","000566",4.72]) 18 >>> mywriter.writerow([" 精华制药 ","002349",34.67]) 19 >>> myfile.close() # 以写模式打开文件 # 创建常规写对象 # 写第一行数据 # 写第二行数据 # 写第三行数据 # 写第三行数据 >>> myfile = open(r"d:\example6\stocks.csv") # 以只读模式打开文件 >>> myreader = csv.reader(myfile) # 创建常规读对象 >>> for row in myreader: print(row) # 因为每行末尾有两个换行符号,所以有空列表出现 [' 名称 ', ' 代码 ', ' 现价 '] [ ] [' 天宇股份 ', '300702', '60'] [ ] [' 海南海药 ', '000566', '4.72'] [ ] [' 精华制药 ', '002349', '34.67'] [ ] >>> myfile.close() 将数据写入 CSV 后,writerow() 方法会在每行数据末尾添加两个换行符号。因此,在打开 CSV 文件的 open() 方法中,指定 newline="" 选项,可以防止向 CSV 文件中写入空行。 【例 6-21】指定 newline 选项防止向 CSV 文件中写入空行示例。 >>>import csv >>> data = [[" 名称 ", " 代码 ", " 现价 "], 112 第 6 章 Python 文件操作 [" 天宇股份 ", "300702", 60], [" 海南海药 ", "000566", 4.72], [" 精华制药 ", "002349", 34.67]] >>> myfile = open(r"d:\example6\stocks.csv","w",newline="") # 设置 newline="",可以防止将空行写入 CSV 文件 >>> mywriter = csv.writer(myfile) >>> for i in range(len(data)): mywriter.writerow(data[i]) 10 16 18 19 >>> myfile.close() >>> myfile = open(r"d:\example6\stocks.csv") >>> myreader = csv.reader(myfile) >>> for row in myreader: print(row) [' 名称 ', ' 代码 ', ' 现价 '] [' 天宇股份 ', '300702', '60'] [' 海南海药 ', '000566', '4.72'] [' 精华制药 ', '002349', '34.67'] >>> myfile.close() 2. 字典写对象 字典写对象由 csv.Dictwriter() 函数创建,基本语法格式如下: csvwriter = csv.Dictwriter(csvfile,fieldnames= 字段名列表 ) 其中,变量 csvwriter 用于引用写对象,csvfile 是 open() 函数返回的文件对象。参数 fieldnames 用列表指定字段名,它决定将写入 CSV 文件时,键值对中各个值的顺序。 字典对象的 writerow() 用于向 CSV 文件写入一行数据,基本语法格式如下: csvwriter.writerow(data) 其中,data 是一个字典对象,其中包含一行 SCV 数据。 【例 6-22】字典写对象示例。 >>> import csv # 设置 newline="",可以防止将空行写入 CSV 文件 >>> myfile = open(r"d:\example6\stocks.csv","w",newline="") >>> mywriter = csv.DictWriter(myfile,fieldnames=[" 名称 "," 代码 "," 现价 "]) # 创建字典写对象 >>> mywriter.writeheader() # 写入字段名 10 >>> mywriter.writerow({" 名称 ": " 天宇股份 ", " 代码 ": "300702", " 现价 ": 60}) # 写数据 16 >>> mywriter.writerow({" 名称 ": " 海南海药 ", " 代码 ": "000556", " 现价 ": 4.72}) 18 >>> mywriter.writerow({" 名称 ": " 精华制药 ", " 代码 ": "002349", " 现价 ": 5.98}) 18 >>> myfile.close() 113 Python 程序设计 >>> myfile = open(r"d:\example6\stocks.csv") >>> myreader = csv.reader(myfile) >>> for row in myreader: print(row) # 以只读模式打开文件 # 创建常规读对象 [' 名称 ', ' 代码 ', ' 现价 '] [' 天宇股份 ', '300702', '60'] [' 海南海药 ', '000556', '4.72'] [' 精华制药 ', '002349', '5.98'] >>> myfile.close() 6.2.4 提升训练 【训练内容】 投资机构为个人投资者开设账户前,将记录、收集、储存个人的一系列信息,包括账户名、 密码、姓名、证件类型、证件号码、交易单号、交易金额或份额、交易费用、交易时间、产品名称、 产品代码等信息,此类信息仅为投资者能实现投资交易服务所需而进行处理。 对投资者个人信息进行管理旨在帮助个人提升工作效率,最终有利于个人的工作和生活,提 高个人的能力和竞争力,为实现个人价值和可持续发展打下坚实的基础。同时,随着互联网应用 的普及和人们对互联网的依赖,个人信息受到极大的威胁。为了提高投资者个人信息的安全性, 防止个人信息被不法分子获取,设置账户和密码以验证身份。 在本案例中,编写 Python 程序,通过一系列的操作,模拟投资者个人信息管理的过程。具体 任务有: (1)输入投资者个人信息,用 CSV 文件保存数据。 (2)查看个人信息,需要进行身份验证,通过后才方可查看。 任务一 将投资者个人信息写入 CSV 文件 【设计实现思路】 在本例中,投资者个人基本信息有账户名、密码、姓名、证件类型、证件号码五项。个人信 息由键盘输入,并写入 CSV 文件。因此,要新建、打开一个 CSV 文件,并将每一条个人信息写 到 CSV 文件的末尾。完成写数据后,关闭 CSV 文件。 【参考代码】 import csv csvfile = open("data.csv","a",newline="") mywriter = csv.DictWriter(csvfile,fieldnames=[" 账户名 "," 密码 "," 姓名 "," 证件 类 型 "," 证件号码 "]) mywriter.writeheader() item = {} while True: item[" 账户名 "] = input(" 请输入账户名:") item[" 密码 "] = input(" 请输入密码:") item[" 姓名 "] = input(" 请输入姓名:") item[" 证件类型 "] = input(" 请输入证件类型:") item[" 证件号码 "] = input(" 请输入证件号码:") mywriter.writerow(item) isnext = input(" 是否继续输入(Y/N):") 114 第 6 章 Python 文件操作 if isnext in ["N","n"]: break csvfile.close() 任务二 验证投资者身份,查看个人信息 【设计实现思路】 在查看个人信息前,验证投资者的身份,也就是将 CSV 文件中存放的个人信息的账户名、密 码与用户输入的账户名、密码进行比较,若两者相同,通过身份验证,只输出该用户的个人信息 以供查看;若两者不相同,则不能输出文件中的任何信息。 首先,为了数据安全,以只读的方式打开文件。其次,创建字典读取器对象读取 CSV 文件。 然后,用循环迭代 CSV 文件的内容,将包含 CSV 文件数据行的每一个字典对象的“账户名”和“密 码”的键值与用户输入数据比较,进行身份验证。身份验证通过后,输出该字典对象。最后,关 闭 CVS 文件。 【参考代码】 csvfile = open("data.csv","r") myreader = csv.DictReader(csvfile) account = input(" 请输入账户名:") password = input(" 请输入密码:") for row in myreader: if row[" 账户名 "] == account and row[" 密码 "] == password: find = list(row.items()) for tup in find: print(tup[0],":",tup[1]) break else: print(" 账户名或者密码错误! ") csvfile.close() 程序运行后,结果如下: 请输入账户名:harley 请输入密码:harley123 请输入姓名:何莉 请输入证件类型:身份证 请输入证件号码:450117********1513 是否继续输入(Y/N):y 请输入账户名:richard 请输入密码:richard123 请输入姓名:刘瑞 请输入证件类型:身份证 请输入证件号码:432266********1234 是否继续输入(Y/N):y 请输入账户名:peppa 请输入密码:peppa123 请输入姓名:李琦 请输入证件类型:身份证 请输入证件号码:441524********1418 是否继续输入(Y/N):n 请输入账户名:harley 115 Python 程序设计 请输入密码:harley 账户名或者密码错误 请输入账户名:peppa 请输入密码:peppa123 账户名: peppa 密码: peppa123 姓名: 李琦 证件类型: 身份证 证件号码: 441524********1418 6.3 6.3.1 Python 文件数据组织的维度 数据组织基本概念 数据组织是将具有某种逻辑关系的一批数据组织起来,按一定的存储表示方式和规则配置在 计算机的存储器中,以便使计算机处理时速度快、占用存储器的容量少。数据组织的维度是数据 的组织形式。根据数据的关系不同,数据组织的维度可以分为:一维数据、二维数据和高维数据。 高维数据由键值对类型的数据构成,采用对象方式组,可以多层嵌套。高维数据在 Web 系统中十 分常见,作为当今 Internet 组织内容的主要方式。本节不做详细介绍。 6.3.2 一维数据 1. 一维数据的表示 一维数据是由对等关系的有序或无序数据构成,采用线性方式组织。一维数据是最简单的数 据组织类型。一维数据对应了传统 Python 程序中的列表、数组和集合等类型的概念。 2. 一维数据的存储 一维数据的文件存储有多种方式,总体思路是采用特殊字符分隔各数据。常用存储方法有 4 种, 见表 6-4。 表 6-4 一维数据的 4 种存储方法 存储方法 例 子 采用空格分隔元素 鱼跃医疗 海南海药 天宇股份 采用逗号分隔元素 鱼跃医疗,海南海药,天宇股份 采用换行分隔元素 鱼跃医疗 海南海药 天宇股份 采用其他特殊符号元素 鱼跃医疗;海南海药;天宇股份 这 4 种方法中,逗号分隔的存储格式称为 CSV 格式,即逗号分隔值。存储的文件一般采用 .csv 为扩展名,这里的逗号是英文逗号。大部分编辑器都支持直接读入或保存文件为 CSV 格式。 一维数据保存成 CSV 格式后,各元素采用逗号分隔,形成一行。从 Python 表示到数据存储, 需要将列表对象输出为 CSV 格式以及将 CSV 格式读入成列表对象。 3. 一维数据的处理 对一维数据进行处理指的是数据存储与数据表示之间的转换,也就是如何将存储的一维数据 116 第 6 章 Python 文件操作 读入程序并表达为列表或集合,或者反过来,如何将程序表示的数据写入到文件中。 【例 6-23】将程序的一维数据写入到文件示例。 list1 = [' 鱼跃医疗 ',' 海南海药 ',' 天宇股份 '] f = open("one_dim.txt","w") f.write(' '.join(list1)) #join() 函数是将前面的字符串作为分隔符添加到后面的字符 串的字符之间(或列表的元素之间) f.close() # 文件内容变为 " 鱼跃医疗 海南海药 天宇股份 " list2 = [' 鱼跃医疗 ',' 海南海药 ',' 天宇股份 '] f = open("two_dim.txt","w") f.write('&'.join(list2)) f.close() # 文件内容变为 " 鱼跃医疗 & 海南海药 & 天宇股份 " 【例 6-24】从文件中读取一维数据示例。 # 文件内容为 " 鱼跃医疗 海南海药 天宇股份 " f = open("one_dim.txt") str = f.read() list1 = str.split() f.close() print(list1) # 输出结果为 [' 鱼跃医疗 ',' 海南海药 ',' 天宇股份 '] # 文件内容为 " 鱼跃医疗 & 海南海药 & 天宇股份 " f = open("two_dim.txt") str = f.read() list2 = str.split('&') f.close() print(list2) # 输出结果为 [' 鱼跃医疗 ',' 海南海药 ',' 天宇股份 '] 6.3.3 二维数据 1. 二维数据的表示 二维数据,也称表格数据,由多个一维数据构成,可以看作是一维数据的组合形式。因此, 二维数据可以采用二维列表来表示,即列表的每个元素对应二维数据的一行,这个元素本身也是 列表类型,其内部各元素对应这行中的各列值。 使用二维列表表达二维数据时,可以使用两层 for 循环遍历每一个元素。在外围列表中,每 一个元素可以对应二维数据的一行,也可以对应一列,具体对应一行还是一列,要根据具体应用 确定。 2. 二维数据的存储 二维数据由多个一维数据构成,用 CSV 格式文件存储。CSV 文件的每一行是一维数据,整个 CSV 文件是一个二维数据。 在 CSV 数据存储格式中还有以下约定: (1)如果某个元素在二维数据中缺失了,那么必须要为它保留一个逗号。 (2)在二维数据的表中,它的表头可以作为数据存储,也可以另行存储。 (3)逗号是英文半角逗号,逗号与数据之间没有额外的空格。 (4)二维数据可以按行存,也可以按列存,具有由程序决定,但是默认是先行后列,即外 117 Python 程序设计 围列表的每一个元素是一行。 (5)在不同的编辑软件中转换 CSV 格式,当元素中需要包含逗号时,那么可能会在元素两 端出现引号包裹,也可能采用转义符,需要按照实际情况进行处理。 二维数据存储为 CSV 格式,需要将二维列表对象写入 CSV 格式文件以及将 CSV 格式读入成 二维列表对象。 3. 二维数据的处理 二维数据处理等同于二维列表的操作。与一维列表不同,二维列表一般需要借助循环遍历实 现对每个数据的处理。 【例 6-25】将保存在列表中二维数据写入 csv 文件中示例。 list1 = [[' 鱼跃医疗 ',' 海南海药 ',' 天宇股份 '],[' 科兴制药 ',' 精华制药 ',' 华森制药 ']] f = open("two_dim.csv","w") for item in list1: f.write(','.join(item)+'\n') f.close() # 文件内容变为: 鱼跃医疗 海南海药 天宇股份 科兴制药 精华制药 华森制药 【例 6-26】从 CSV 格式的文件读入数据示例。 fo = open("two_dim.csv") list1 = [] for line in fo: line = line.replace("\n","") list1.append(line.split(",")) fo.close() print(list1) # 输出结果为: [[' 鱼跃医疗 ', ' 海南海药 ', ' 天宇股份 '], [' 科兴制药 ', ' 精华制药 ', ' 华森制药 ']] 习 题 一、选择题 1. 下列关于文件的描述,错误的是 。 (A)根据文件的存储格式不同,文件可以分为文本文件和二进制文件 (B)以 .py 为扩展名的 Python 源文件、以 html 为扩展名的网页文件都是文本文件 (C)二进制文件只能用“二进制文件方式”打开 (D)文本文件按字符读取文件,一个字符占用一个或多个字节 2. 关于文件操作的说法中,错误的是 。 (A)open() 函数可以打开文件,并返回一个文件对象 (B)当文件被打开后,该文件被当前程序占用,其他程序不能操作这个文件 (C)在某些写文件的模式下,当打开不存在的文件时,Python 程序可以创建该文件 (D)文件操作之后不需要关闭,也可以将文件数据存储到外部介质 3. 下列关于文件指针的描述,正确的是 118 。 第 6 章 Python 文件操作 (A)使用 tell() 方法可获取文件指针的位置 (B)以只读方式打开文件时,文件指针会指向文件末尾 (C)以覆盖写或追加写的方式打开文件时,文件指针会指向文件开头 (D)在文件的读 / 写过程中,只能使用 seek() 方法移动文件指针的位置 4. 下列选项中,不能读文件的模式是 (A)"r+" 。 (B)"a+" 5. 下列选项中,不能向文件写数据的模式是 (A)"w+" (B)"r" 6. 下列关于文件的读 / 写模式,说法错误的是 (C)"+" (D)"w+" 。 (C)"a" (D)"rb+" 。 (A)打开文件时,省略读 / 写模式,表示以按文本文件格式只读数据 (B)“w+”模式打开文件时,若文件不存在,会创建文件 (C)“rb+”模式打开文件,文件指针会放在文件开头 (D)“a+”模式打开文件,若文件存在,原来的数据会被覆盖 7. 文件“data.txt”不存在,下面的语句 会出错。 (A)myfile_1 = open("data.txt","w") (B)myfile_1 = open(data.txt","r+") (C)myfile_1 = open("data.txt","a") (D)myfile_1 = open("data.txt","ab") 8. 文件“d:\example6\data.txt”的内容为: 苹果 & 梨子 & 水蜜桃 草莓 & 葡萄 & 橙子 下面代码的执行结果是 。 myfile=open(r"d:\example6\data.txt") content=myfile.read().split("&") myfile.close() print(content) (A)[' 苹果 ', ' 梨子 ', ' 水蜜桃 ', '\n', 草莓 ', ' 葡萄 ', ' 橙子 '] (B)[' 苹果 ', ' 梨子 ', ' 水蜜桃 \n 草莓 ', ' 葡萄 ', ' 橙子 '] (C)[' 苹果 ', ' 梨子 ', ' 水蜜桃 \n ', ' 草莓 ', ' 葡萄 ', ' 橙子 '] (D)[' 苹果 ', ' 梨子 ', ' 水蜜桃 ', ' 草莓 ', ' 葡萄 ', ' 橙子 '] 9. 文件“d:\example6\data.txt”的内容为: 苹果 & 梨子 & 水蜜桃 下面代码的执行结果是 。 myfile = open(r"d:\example6\data.txt","w+") myfile.write(" 柚子 & 西瓜 & 蓝莓 ") myfile.seek(0) content = myfile.read().split("&") myfile.close() print(content) (A)[' 柚子 ', ' 西瓜 ', ' 蓝莓 '] (B)[' 苹果 ', ' 梨子 ', ' 水蜜桃 \n ',' 柚子 ', ' 西瓜 ', ' 蓝莓 '] (C)[' 柚子 ', ' 西瓜 ', ' 蓝莓 \n'] (D)[' 苹果 ', ' 梨子 ', ' 水蜜桃 ',' 柚子 ', ' 西瓜 ', ' 蓝莓 '] 119 Python 程序设计 10. 下面程序执行的结果是 。 myfile = open(r"d:\example6\data.txt","wb+") myfile.writelines([1,2,3,4]) myfile.seek(0) content = myfile.read() myfile.close() print(content) (A)[1,2,3,4] (B)"1,2,3,4" (C)程序出错 11. 下列选项中,可用于获取指定目录下所有文件和子目录名称的方法是 (A)os.makedirs(path) (B)os.getcwd() (C)os.chdir(path) (D)os.listdir(path) 12. 关于 CSV 文件的说法,正确的是 (D)"1234" 。 。 (A)CSV 文件中的数据只能使用逗号分隔 (B)CSV 文件中,记录的字段名称通常出现在第一行 (C)CSV 文件中,记录的字段序列可以不相同 (D)CSV 文件可以是文本文件,也可以是二进制文件 二、填空题 1. 下面的程序,将《静夜思》写入文件“test6-1.txt”。请将程序补充完整。 myfile = open(r"d:\example6\test6-1.txt", ) data = [" 静夜思 "," 李白 "," 窗前明月光,"," 疑是地上霜。"," 举头望明月,"," 低头思故乡。"] myfile. 2. 下面的程序,将删除文件“test6-2.txt”最后一行的内容。请将程序补充完整。 myfile = open(r"d:\example6\test6-2.txt","r") content = myfile.close() lst = myfile = open(r"d:\example6\test6-2.txt", myfile.writelines("\n".join(lst)) ) 3. 下面的程序,统计字符串“共同富裕”在文件“test6-3.txt”中出现的次数,并将统计结果 以“共同富裕:6”的格式写到文件最后一行。请将程序补充完整。 myfile = open(r"d:\example6\test6-3.txt", content = myfile.read() newstr = "\n 共同富裕 :"+str(n) myfile.close() 4. 有如下数据: s="abcdef" 6 120 ) 第 6 章 Python 文件操作 下面的程序,将数据写入二进制文件“test6-4.dat”文件中,并能够读取文件的内容。请将程 序补充完整。 myfile = open(r"d:\example6\test6-4.dat", ) myfile.seek(0) s = print(s) myfile.close() 5. 下面的程序,将删除文件夹“d:\example6\test6”里的文件“test6-5.txt”和多级目录“f6\f6-1”。 请将程序补充完整。 import ,os.path (r"d:\example6\test6") delfile = input(" 请输入要删除的文件名:") if : else: print("{} 文件不存在 ".format(delfile)) (r"f6\f6-1") print(os.listdir()) 三、编程题 1. 编写程序,完成下列任务。 (1)将下面的数据存入文件“test6-6.csv”中。 股票名称 , 代码 , 最新价 , 涨跌价 , 涨跌幅 智飞生物 ,300122,135.71,-1.29,-0.94% 泸州老窖 ,000568,222.32,0.52,0.23% 上海机场 ,600009,49.67,0.61,1.24% 牧原股份 ,002714,58.66,-0.08,-0.14% 招商银行 ,600036,52.15,-0.35,-0.67% (2)读取 CSV 文件“test6-6.csv”中的数据,将其按涨跌幅从大到小排序,并按以下示例运 行结果格式输出。 数据已存入文件 :test6-6.csv 股票名称 代码 最新价 涨跌价 涨跌幅 上海机场 600009 49.67 0.61 1.24% 泸州老窖 000568 222.32 0.52 0.23% 牧原股份 002714 58.66 -0.08 -0.14% 招商银行 600036 52.15 -0.35 -0.67% 智飞生物 300122 135.71 -1.29 -0.94% 2. 文件“test6-7.txt”中的数据如下: 45-9 9%2 67/4 5**3 len("python 3.0") 56//7 121 Python 程序设计 编写程序,从文件中读取这些表达式,计算结果,并将结果写回文件“test6-7.txt”中,示例 运行结果如下: 45-9 = 36 9%2 = 1 67/4 = 16.75 5**3 = 125 len("python 3.0") = 10 56//7 = 8 3. 编写程序,完成下列任务。 (1)输入 n 位学生的成绩,并使用文件“test6-8.csv”保存各个学生的成绩数据,字段名包 括学号、姓名、高等数学、计算机程序设计、专业英语,接下来是各条记录,每条记录由学生的 学号、姓名、高等数学成绩、计算机程序设计成绩、线性代数成绩组成。 (2)从文件“test6-8.csv”中读取数据,计算平均分,并打印成绩表,示例运行结果如下: 共有 3 个学生 学号 姓名 01 李白 02 王一博 03 李佳琪 122 数学 80 90 100 语文 100 91 100 英语 60 99 80 平均分 80.0 93.33 93.33 第7章 函数及模块 学习目标 (1)掌握函数的定义和调用方式。 (2)掌握函数的参数和返回值。 (3)掌握函数的变量作用域。 (4)掌握匿名函数和高阶函数的使用。 (5)掌握模块的使用。 (6)掌握包的使用。 (7)掌握第三方库的安装方法及使用。 (8)通过函数结构化的程序分析,培养学生工程项目分析能力和管理能力;培养其分而治 之、化繁为简的思维方法,把复杂的系统问题拆分为一个个简单的小问题,当每个小问题都解决了, 大问题也就迎刃而解;使学生能从整体与部分的辩证关系中认识团队哲学,培养学生的全局意识、 团队合作精神和探索精神。 章节导学 计算机程序中,函数是一段具有特定功能的、可重用的语句组。函数能提高应用的模块 性和代码的重复利用率。同一函数,通过接收不同的参数,实现不同的功能,大大提高程序 的适应性。Python 3 中提供了很多内置函数,用户也可以自定义函数。本章将介绍 Python 3 中函数的定义、调用及参数传递,以及一些内置函数及模块的应用。 7.1 函数 在编程世界中,函数是指可重复使用的程序片段。在程序中允许为某个代码块赋予特定名字, 允许通过这一特定名字在程序任何地方来运行代码块,并可重复任何次数。这就是所谓的函数 调用。 7.1.1 函数的定义和调用 在 Python 3 中定义一个特定功能的函数,要使用关键字 def 来声明,将任何需要传入的参数 和自变量放到函数后面的括号中,在代码块的最后可以选择 return、yield 关键字返回值给该函数, 没有关键字就返回 None。其语法格式如下: 123 Python 程序设计 def 函数名 ( 参数 1, 参数 2, ...., 参数 n) : 代码块 (函数体) return 返回值 遵守的规则如下: (1)函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。 (2)任何传入参数和自变量必须放在圆括号中间。 (3)函数体以冒号起始,并且缩进。 (4)return [ 表达式 ] 结束函数,选择性地返回一个值给调用方。不带表达式的 return 相当于 返回 None。 需要注意的是,函数定义时参数和返回值都可以省略。 【例 7-1】不带参数的函数定义示例。 def printhello(): print("hello") # 函数名 # 函数体 【例 7-2】带参数的函数定义示例。 def add(x,y): z = x+y return z # 定义带参数 x,y 的函数 函数的调用就是通过函数名 ( 参数 ) 来调用。调用函数的基本格式如下: 函数名 ( 参数表 ) 在例 7-2 的程序中,定义了一个 add(x,y) 函数。该函数的功能是计算 x+y 的值,并通过 return 语句返回给函数。那么函数的调用,使用函数名加上需要传入的响应参数即可。 【例 7-3】函数调用和类型测试示例。 def add(x,y): z = x+y return z # 定义带参数 x,y 的函数 sum = add(2,3) print(sum) print(type(add)) print(add) # 调用 add() 函数 # 返回 add 的类型 # 返回函数名变量在内存中的地址 注意: (1)def 也是一条可执行语句,它完成函数的定义。 (2)在 Python 3 中,函数也是对象(function 对象)。 (3)def 语句在执行时会创建一个函数对象。 (4)函数名是一个变量,它引用 def 语句创建的函数对象。 (5)可将函数名赋值给变量,使变量引用同一个函数。 7.1.2 函数参数 在 Python 3 程序设计中函数参数有两个比较通用的概念:形参和实参。 形参:即形式上的参数,不占用内存空间。 实参:是实际真正的参数。 形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参出 124 第 7 章 函数及模块 现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参的功能是作数据传送。 发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数 的数据传送。 在 Python 3 中函数的参数类型有:默认参数、可变参数、必选参数、关键字参数、组合参数。 参数放置的顺序为:必选参数、默认参数、可变参数、关键字参数。 1. 必选参数 必选参数按照位置关系一一对应,多个参数按照一定的顺序依次传入函数,其值依次对应赋 给函数声明时的参数。 【例 7-4】必选参数的使用示例。 # 必选参数严格按照参数的顺序来读取 def user_logon(user,pwd): print(" 用户名为:",user) print(" 用户登录密码为:",pwd) user_logon("Henson","123456") # 形参 # 实参,实际参数 2. 默认参数 默认参数就是函数参数中指定默认的值,调用函数时如果不传入这个参数,则函数内容使用 带有默认值的参数,若传入了这个参数,则执行指定的参数值。 【例 7-5】默认参数的使用示例。 def func(x,n=2): print(x**n) # 默认第二个参数的值为 2 func(3) func(3,4) # 使用默认参数进行运算 函数结果为 9 # 第二个参数不使用默认值进行运算,函数结果为 81 在使用默认参数的时候要特别注意的一点是:默认参数必须要指向不可变对象,可变对象是 不能被用作默认参数的。 【例 7-6】默认参数值是可变对象情况示例。 ''' 如果使用 list 数组作为默认参数,多次调用函数的返回值会发生变化,在使用过程要特别注意 ''' def default_Params(list = [ ]): list.append("C") print(list) return list default_Params(["A","B"]) default_Params() default_Params() default_Params() 程序运行结果: ['A', 'B', 'C'] ['C'] ['C', 'C'] ['C', 'C', 'C'] 125 Python 程序设计 3. 可变参数 有时候我们在声明函数时,无法确定函数的参数个数,或者我们本身就想设计一种动态的参 数,这种参数称为可变参数。可变参数在定义的时候用符号 * 表示,而且在函数被调用的时候参 数会被组装成一个元组(tuple)。 【例 7-7】可变参数的使用示例。 def say_hello(*args): for name in args: print('hello:',name) say_hello("Tim","Henson","Dean") list1 = ["Tim","Henson","Dean"] # 如果已经存在了一个 list1 列表,我们可以使用列表中单个元素作为可变参数传入 say_hello(list1[0],list1[1],list1[2]) # 为了简化上面的参数调用方式,可以使用 * 来把参数当作可变参数传入 say_hello(*list1) 4. 关键字参数 关键字参数和可变参数类似,参数的个数都是可变的,所以也常被称作可变关键字参数,与 可变参数的不同在于关键字参数在调用的时候会被组装成一个字典(dict),而且参数是带参数 名的。关键字参数在定义的时候用两个符号 ** 表示。 【例 7-8】关键字参数的使用示例。 # 关键字参数 def key_Params(**params): print(params) # 关键字参数会被组装成一个字典 dict dict = {"name":"Henson","age":3} key_Params(name="Henson",age=3) # 如果已经存在了一个 dict,可以使用 ** 来把参数当做关键字参数传入 key_Params(**dict) 5. 组合参数 在 Python 3 函数中,以上参数可组合使用。当参数组合使用时,需要注意以下原则: (1)有默认值的参数需要放在无默认值参数的后边。 (2)当必选参数和可变参数混合使用时,可变参数的取值为超出必选参数的实参部分,且 可变参数在顺序参数之后。 【例 7-9】组合参数的使用示例。 # 使用组合参数定义一个函数,显示个人信息。 def per_inf(name,age=30,*args,**kwargs): print(" 姓名:",name) print(" 年龄 ",age) print(" 曾经生活过的城市:",args) for key,item in kwargs.items(): print("{0}--->{1}".format(key,item)) # 调用函数 per_inf(" 李梅丽 ",35," 北京 "," 上海 "," 深圳 "," 广州 ",hobby1=" 唱歌 ",hobby2=" 跳舞 ",hobby3=" 弹琴 ") 程序运行结果: 126 第 7 章 函数及模块 姓名:李梅丽 年龄 35 曾经生活过的城市:(' 北京 ', ' 上海 ', ' 深圳 ', ' 广州 ') hobby1---> 唱歌 hobby2---> 跳舞 hobby3---> 弹琴 在函数调用的时候,Python 3 解释器自动按照参数位置和参数名把对应的参数传进去。 7.1.3 函数的返回值 返回值指函数调用结束后返回需要的结果及告知调用者函数执行完成。Python 3 中使用 return 语句指定应该返回的值。语法格式如下: return [expression] 函数体中 return 语句有指定返回值时返回的就是其值。函数体中没有 return 语句时,函数运 行结束会隐含返回一个 None 作为返回值,类型是 NoneType,与 return、return None 等效,都是返 回 None。 【例 7-10】隐含 return None 的使用示例。 # 定义函数,无 return 语句 def show(x): print(x) # 调用函数 plus=show(6) print(plus) print(type(plus)) 程序的运行结果: 6 None 【例 7-11】指定 return 返回值的使用示例。 # 统计字符串中含有 'a' 的单词 # 定义函数,有 return 语句 def find_word(txt): result = [ ] words = txt.split() for word in words: #find() 函数如果包含子字符串返回开始的索引值,否则返回 -1。 if word.find('a') != -1: result.append(word) return result # 有返回值 # 调用函数 sen = "installers are available for download for all versions" print(find_word(sen)) 程序运行结果: ['installers', 'are', 'available', 'download', 'all'] 一个函数可以存在多条 return 语句,但只有一条可以被执行。 127 Python 程序设计 7.1.4 函数参数传递 Python 3 中,函数参数由实参传递给形参的过程,是由参数传递机制来控制的。根据实际参 数的类型不同,函数参数的传递方式可分为两种,分别为值传递和引用(地址)传递。 1. 值传递 所谓值传递,实际上就是将实际参数值的副本(复制品)传入函数,而参数本身不会受到任 何影响。 【例 7-12】值传递的使用示例。 def double(arg): print('Before:', arg) arg = arg * 2 print('After:', arg) num = 10 double(num) print(' 函数调用后变量 num 值为:', num) saying = 'Hello' double(saying) print(' 函数调用后变量 saying 值为:', saying) 程序的运行结果: Before: 10 After: 20 函数调用后变量 num 值为:10 Before: Hello After: HelloHello 函数调用后变量 saying 值为:Hello 每次调用都确认了作为参数传入的值在函数代码组已经改变,但是在函数外部 print 出来的值 仍保持不变。也就是说,函数参数看起来遵循按值调用语义。 2. 引用(地址)传递 引用(地址)传递中,传入被调函数的是实参变量的地址,形参的操作就是寻址处理,被调 函数中对形参的操作会影响实参变量。 【例 7-13】引用(地址)传递的使用示例。 def change(arg): print('Before:', arg) arg.append('data') print('After:', arg) numbers = [42, 256] change(numbers) print(" 函数调用后变量 numbers 的值为:",numbers) 程序运行结果: Before: [42, 256] After: [42, 256, 'data'] 函数调用后变量 numbers 的值为: [42, 256, 'data'] 这一次不仅函数中的参数值改变了,函数外部 print 出来的值也改变了。 Python 3 不允许程序员选择采用传值还是传引用。Python 参数传递采用的肯定是“传对象引用” 128 第 7 章 函数及模块 的方式,解释器会查看对象引用(内存地址)指示的那个值的类型,如果函数收到的是一个可变 对象(比如字典或者列表)的类型,就能修改对象的原始值,即通过“引用(地址)传递”来传 递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修 改原始对象,即通过“值传递”来传递对象。 7.1.5 提升训练 本节设计一段小程序,主要是要综合运用各种参数来定义函数。 【训练内容】 编写一个函数 cacluate,要求:有一个必须参数、一个默认参数,用户从键盘上输入任意多个数, 返回的是一个元组。元组的值均要大于所有参数的平均值,且元组的第一个值为所有参数的平均 值四舍五入后的最小整数。 【参考代码】 # 接收用户输入的数字,以空格分割,将分割后的字符存入列表赋值给变量 n n = input(" 请输入整数,以空格分隔:").split(" ") # 定义函数 def cacludte(x,y = 2,*n): #*n 接受实参列表 sum = 0 big_num = [ ] # 定义一个列表用于存放大于平均值 for i in n: i = int(i) sum += i ave = (sum + x + y) / (len(n) + 2) # 计算平均值 print(" 所以数值的平均值为:",ave) if x > ave: big_num.append(x) if y > ave: big_num.append(y) for j in n: j = int(j) if j > ave: big_num.append(j) return tuple(big_num) # 将列表转换为元组类型返回 nb = [] # 定义一个空列表 for m in n: nb.append(m) js = cacludte(3,4,*nb) print(" 满足条件的数值:",js) 程序运行结果: 请输入整数,以空格分隔:1 2 3 4 5 6 所以数值的平均值为:3.5 满足条件的数值:(4, 4, 5, 6) 7.2 变量的作用域 在一个程序中使用变量名时,Python 创建、改变或者查找变量名都是在命名空间中进行的。 作用域指的就是命名空间。 129 Python 程序设计 在创建变量时,Python 将变量名被创建的地点关联给一个特定的命名空间。也就是说在代码 中变量创建的位置决定了这个变量将存在于哪个命名空间,也就是它可以被访问的范围。 作用域,就是变量的有效范围,是变量可以在哪个范围以内使用。有些变量可以在整段代码 的任意位置使用,有些变量只能在函数内部使用,有些变量只能在 for 循环内部使用。变量的作 用域由变量的定义位置决定。在不同位置定义的变量,它的作用域是不一样的。 Python 3 的作用域一共有 4 种:局部作用域、闭包函数外的函数中、全局作用域、内建作用域。 本节我们只讲解两种变量:局部变量和全局变量。 7.2.1 Python 局部变量 局部变量是指在函数内定义的变量,只能被函数内部引用,不能在函数外引用这个变量,这 个变量的作用域就是局部的。 当函数被执行时,Python 3 会为其分配一块临时的存储空间,所有在函数内部定义的变量, 都会存储在这块空间中。而在函数执行完毕后,这块临时存储空间随即会被释放并回收,该空间 中存储的变量自然也就无法再被使用。 【例 7-14】局部变量的使用示例。 def func(): loc = 15 #loc 在函数内定义的变量,为局部变量 print(" 函数内部变量 loc=",loc) func() # 在函数外面引用局部变量 print(" 函数外部引用变量 loc=",loc) 程序执行结果为: 函数内部变量 loc= 15 Traceback (most recent call last): File "C:\Users\10471\Desktop\123.py", line 7, in print(" 函数外部引用变量 loc=",loc) NameError: name 'loc' is not defined 如果试图在函数外部访问其内部定义的变量,Python 解释器会报 NameError 错误,并提示没 有定义要访问的变量 . 这也证实了当函数执行完毕后,其内部定义的变量会被销毁并回收。 另函数的参数也属于局部变量,只能在函数内部使用。 【例 7-15】函数参数也是局部变量的使用示例。 def func(x,y): loc = 15 #loc 在函数内定义的变量,为局部变量 print(" 函数内部变量 loc=",loc) print(" 函数内参数 x=",x) print(" 函数内参数 y=",y) func(1,2) # 在函数外面引用函数参数 print(" 函数外部引用参数 x=",x) print(" 函数外部引用参数 y=",y) 程序执行结果为: 函数内部变量 loc= 15 130 第 7 章 函数及模块 函数内参数 x= 1 函数内参数 y= 2 Traceback (most recent call last): File "C:\Users\10471\Desktop\123.py", line 9, in print(" 函数外部引用参数 x=",x) NameError: name 'x' is not defined 由于 Python 解释器是逐行运行程序代码,因此运行程序后仅提示“x 没有定义”,实际上在 函数外部访问 y 变量也会报同样的错误。 7.2.2 Python 全局变量 除了在函数内部定义变量,Python 还允许在所有函数的外部定义变量,在函数外面、全局范 围内定义的变量,被称为全局变量。 和局部变量不同,全局变量的默认作用域是整个程序,既可以在各个函数的外部使用,也可 以在各函数内部使用。全局变量可作用于程序中的多个函数,在各函数内部只是可以访问,其使 用是受限制的。 1. 在函数内部读取全局变量 【例 7-16】函数外部定义的变量在函数内部引用示例。 z = 5 def func(): print(z) # 调用函数 func() #z 为全局变量 # 直接在函数内部引用全局变量 2. 在函数内部定义了与全局变量同名的变量 【例 7-17】函数中定义了与全局变量同名的变量,局部变量会将全局变量覆盖。 x = "AAA" # 这里 x 为全局变量 def func(x): print('x=', x) # 这里打印的是全局变量 x = 2 # 这里的 x 为局部变量 print(' 局部变量 x=', x) func(x) print('x 一直是 ', x) # 这里打印的是全局变量 程序运行结果: x= AAA 局部变量 x= 2 x 一直是 AAA 上面程序工作原理: 第一次,使用函数体中第一行打印变量 x 的值,Python 使用在主块中,函数定义上声明的实参。 接下来,给 x 赋值为 2,变量为 x 对函数来说是局部变量,因此在函数中当改变 x 的值时,在主 块中定义的变量 x 不受影响。最后调用的 print 函数,显示在主块中定义的变量 x,因此证实,它 不受在前面调用函数的局部变量的影响。 注意: (1)全局变量可以和局部变量重名,遵循局部变量优先原则。 (2)在函数中可随意修改全局变量赋值。 131 Python 程序设计 7.2.3 global 语句 全局变量所有作用域都可用,局部变量只能在本函数可用。变量的使用顺序是:局部变量→ 全局变量。也就是说,优先使用局部变量。那么问题来了,如果想在函数内改变全局变量的值, 应该怎么做呢? 为了解决函数内使用全局变量的问题,Python 增加了 global 关键字,利用它的特性可以指定 变量的作用域。 global 关键字的作用是:声明变量 var 是全局的。 1. 函数优先使用局部变量 【例 7-18】函数中优先使用局部变量示例。 language = 'Python' # 这里 language 是全局变量 def fun1(): language = 'JAVA' # 这里 language 是局部变量 print(language) # 调用函数 fun1() print(" 现在变量 language=",language) # 这里打印的是全局变量 程序运行的结果: JAVA 现在变量 language= Python 以上程序可以看出,局部变量改变不了全局变量的值。 2. 使用 global 语句,修改全局变量的值 【例 7-19】在函数中使用 global 语句修改全局变量的值示例。 language = 'Python' def fun1(): global language language = 'JAVA' print(language) # 调用函数 fun1() print(" 现在变量 language=",language) # 这里 language 是全局变量 # 将 language 声明为全局变量 # 这里打印的是全局变量 程序运行后的结果: JAVA 现在变量 language= JAVA 以上程序可以看出,使用 global 语句,修改全局变量的值。我们也可以使用同一个 global 语 句指定多个全局变量。 3. 特殊类型 字符串、数字类型是不能在函数内部修改全局变量的值,除非使用 global 关键字。列表、 字典是可修改的,但不能重新赋值。如果需要重新赋值,需要在函数内部使用 global 定义全局 变量。 【例 7-20】全局变量为列表,函数中不使用 global 语句,也能修改全局变量的值,示例如下。 list1 = ['python',' 列表 '] def func(): 132 # 全局变量为列表 第 7 章 list1.append(' 修改全局变量 ') print(" 追加后:",list1) # 调用函数 func() print(" 全局变量 list1:",list1) 函数及模块 # 修改全局变量 程序运行结果: 追加后:['python', ' 列表 ', ' 修改全局变量 '] 全局变量 list1:['python', ' 列表 ', ' 修改全局变量 '] 发现上面的程序中 list1 并没有使用 global,但是值却改变了,说明列表是可以在局部被修改的。 【例 7-21】全局变量为列表,函数中局部变量赋值,但不能改变全局变量的值,示例如下: list1 = ['python',' 列表 '] def func(): list1 = [' 修改全局变量 '] print(list1) # 全局变量为列表 # 给全局变量重新赋值 # 调用函数 func() print(" 全局变量 list1:",list1) 程序运行结果: [' 修改全局变量 '] 全局变量 list1:['python', ' 列表 '] 从运行结果看,局部变量赋值不能改变全局变量的值。 【例 7-22】全局变量为列表,函数中不使用 global 语句,也能修改全局变量的值,示例如下。 list1 = ['python',' 列表 '] # 全局变量为列表 def func(): global list1 list1 = [' 修改全局变量 '] # 给全局变量重新赋值 print(" 修改后 list1 值为:",list1) # 调用函数 func() print(" 全局变量 list1:",list1) 程序运行结果: 修改后 list1 值为:[' 修改全局变量 '] 全局变量 list1:[' 修改全局变量 '] 从运行结果看,使用了 global 关键字后,变量被重新赋值。 7.2.4 提升训练 【训练内容】 编写一个名为 collatz 的函数,它有一个名为 num 的参数。如果参数是偶数,那么 collatz() 就 打印出 num//2,并返回该值。如果 num 是奇数,collatz() 就打印并返回 3*num+1。然后编写一个程序, 让用户输入一个整数,并不断对这个数调用 collatz (),直到函数返回值 1(令人惊奇的是,这个序 列对于任何整数都有效,利用这个序列,你迟早会得到 1。即使数学家也不能确定为什么。你的 程序在研究所谓的“Collatz 序列”,它有时候被称为“最简单的、不可能的数学问题”)。 133 Python 程序设计 【参考代码】 def collatz(num): if num == 1: # 结束程序 exit(0) exit(0) elif num % 2 == 0: return num // 2 else: return 3 * num + 1 num = int(input(' 请输入一个整数:')) while True: num = collatz(num) # 因为 num 不断变化,所以 num 既是参数也是变量 print(num) 程序运行结果: 请输入一个整数:5 16 8 4 2 1 7.3 特殊函数 为实现某些特定功能,在 Python 3 中提供一些特殊函数。灵活运用这些内建函数可以提高代 码的简洁性和开发速度。 7.3.1 匿名函数 lambda 匿名函数是指一类无须定义标识符 def 的函数或子程序。Python 用 lambda 语法定义匿名函数, 只需用表达式而无须申明。 lambda 语法的定义如下: lambda [arg1 [,arg2, ... argN]] : expression lambda 函数能接收任何数量(可以是 0 个)的参数,但只能返回一个表达式的值,lambda 函 数是一个函数对象,直接赋值给一个变量,这个变量就成了一个函数对象。 lambda 函数使用场景: (1)需要将一个函数对象作为参数来传递时,可以直接定义一个 lambda 函数(作为函数的 参数或返回值)。 (2)要处理的业务符合 lambda 函数的情况(任意多个参数和一个返回值),并且只有一个 地方会使用这个函数,不会在其他地方重用,可以使用 lambda 函数。 (3)与一些 Python 的内置函数配合使用,提高代码的可读性。 1. 匿名函数与普通函数的对比 【例 7-23】使用匿名函数与普通函数定义的一个函数,实现求两个数的乘积。 # 普通定义函数的方法 def func(x,y): s = x*y 134 第 7 章 函数及模块 return s # 运用匿名函数 lambda 定义 g = lambda x,y:x*y print(g(2,4)) # 调用方法 2. 匿名函数的参数 lambda 函数能接收任何数量的参数,还可以是表达式分支。 【例 7-24】匿名函数的多种形式示例。 # 无参数 lambda_1 = lambda: "A" print(lambda_1()) # 一个参数 lambda_2 = lambda x: x**3 print(lambda_2(5)) # 多个参数 lambda_3 = lambda x,y,z:x+y+z print(lambda_3(2,3,4)) # 其他形式的参数 lambda_4 = lambda x: x if x % 3 == 0 else x-1 print(lambda_4(5)) print(lambda_4(6)) 可以看到,lambda 的参数可以 0 个到多个,并且返回的表达式可以是一个复杂的表达式,只 要最后的结果是一个值就行了。 3. lambda 作为一个参数传递 lambda 返回值是一个函数的地址,也就是函数对象,通常都是在定义 lambda 函数的同时 将其应用作为参数传递给另一个函数,该函数在其处理过程中对 lambda 定义的函数进行调用。 【例 7-25】ambda 作为一个参数传递的使用示例。 # 定义函数 def func(x,y,f): print(" 第一个参数:",x) print(" 第二个参数:",y) print(" 第三个参数:",f(x,y)) # 调用函数 func(10,3,lambda a,b:a*b) 程序运行结果: 第一个参数: 10 第二个参数: 3 第三个参数: 30 上面的代码中,func 中需要传入一个函数 f,然后这个函数在 func 里面执行,这时候我们就 可以使用 lambda 函数,因为 lambda 就是一个函数对象。 135 Python 程序设计 4. lambda 函数与 Python 内置函数配合使用 有时 lambda 函数搭配 Python 内置函数使用,能大大简化程序的代码。 【例 7-26】lambda 函数与 Python 内置函数配合使用示例。 # 列表中是某学校发表论文统计的情况 # 根据论文数量从高到低排名 list1 = [(" 信工学院 ",562),(" 经济学院 ",438),(" 管理学院 ",601),(" 法学院 ",152),(" 外 国语学院 ",208)] list1.sort(key=lambda x:x[1],reverse=True) # 使用匿名函数对第二列进行排序 for item in list1: k,v = item print("{0:<7} {1:}".format(k,v)) 程序运行结果: 管理学院 信工学院 经济学院 外国语学院 法学院 601 562 438 208 152 5. lambda 作为函数的返回值 【例 7-27】lambda 作为函数的返回值示例。 def func(x, y): return lambda z: x*y+z func = func(3,4) print(func) print(func(5)) #lambda 作为函数的返回值 # 这里打印的是 func 返回的函数对象 匿名函数可以作为一个函数的返回值。在上面的代码中,func 返回的是一个匿名函数,返回 的是一个函数对象。当执行这个函数时,可以得到 lambda 函数的结果。其中的 x、y 两个参数是 func 中的参数,但执行返回的函数 func 时,已经不在 func 的作用域内了,而 lambda 函数仍然能 使用 x、y 参数,说明 lambda 函数会将它的运行环境保存一份,一直保留到它自己执行的时候使用。 注意: (1)lambda 是一个表达式,而不是一个语句。 (2)lambda 主体是一个单一的表达式,而不是一个代码块。 7.3.2 高阶函数 一个函数可以作为参数传给另外一个函数,或者一个函数的返回值为另外一个函数(若返回 值为该函数本身,则为递归)。若函数满足上述其一,则该函数称为高阶函数。 【例 7-28】高阶函数的使用示例。 # 参数为函数 def f1(): print("in the f1") def gj1(func): func() print("in the gj1..") gj1(f1) 136 # 函数 f1 作为参数传入到 gj1() 函数中 第 7 章 函数及模块 # 返回值为函数 def f2(): print("in the f2..") def gj2(func): print("in the gj2..") return f2 #f2 作为 gj2() 函数的返回值 res = gj2(f2) res() 程序运行结果: in the f1 in the gj1.. in the gj2.. in the f2.. 注意:函数名(例如 f1、gj1),其为该函数的内存地址;函数名 + 括号(例如 f1()、gj1() ), 可调用该函数。 高阶函数是函数式编程的一种。Python 3 中内置的高阶函数主要有 filter、map、zip、reduce 等, 结合匿名函数 lambda、列表解析一起使用,功能更加强大。 1. map map() 函数接收一个函数 func() 和一个序列 seq。遍历序列 seq 中的每一个元素来调用函数 func(),返回到一个新的列表。其基本格式如下: map(func,seq) 其中,func 为处理函数,seq 为序列。该方法返回一个迭代器对象,可以使用 list() 方法使其 变成列表类型。 【例 7-29】map() 函数的使用示例。 map_obj = map(abs,[-20,2,-5,6,5]) print(type(map_obj)) # 打印其类型 print(list(map_obj)) # 使用 list() 方法使其变成列表 # 提供了两个列表,对相同位置的列表数据进行相加 res = map(lambda x,y:x+y,[2,3,5],[4, 6, 8]) print(list(res)) 程序运行输出结果: [20, 2, 5, 6, 5] [6, 9, 13] 2. zip zip() 函数是 Python 的一个内建函数,它接收一系列可迭代的对象作为参数,将对象中对应的 元素打包成一个个 tuple(元组),然后返回由这些 tuples 组成的 list(列表)。若传入参数的长 度不等,则返回 list 的长度等于参数中长度最短的对象的长度。其语法格式如下: zip(seq1, seq2, … ,seqM) 其中,seq1, seq2, … ,seqM 为序列。该方法返回一个迭代器对象。若提供的序列长度不同, 则返回的对象长度与最短序列的长度相同。 137 Python 程序设计 【例 7-30】zip() 函数的使用示例。 # 只有 2 个参数,且参数序列长度相同 res1 = zip([1,2,3],["A","B","C"]) print(type(res1)) print(list(res1)) # 参数序列长度相同 # 打印其类型 # 使用 list() 方法使其变成列表 # 多个参数,且参数序列长度相同 res2 = zip([1,2,3],["A","B","C"],[4,5,6]) print(list(res2)) # 多个参数 # 只有 2 个参数,但参数序列长度不相同 res3 = zip([1,2],["A","B","C","D"]) print(list(res3)) # 参数序列长度不相同 程序运行结果: [(1, 'A'), (2, 'B'), (3, 'C')] [(1, 'A', 4), (2, 'B', 5), (3, 'C', 6)] [(1, 'A'), (2, 'B')] 3. filter filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。 基本语法格式如下: filter(function, sequence) 该函数接收两个参数:第一个为判断函数,第二个为序列。序列的每个元素作为参数传递给 函数进行判断,然后返回 True 或者 False,最后将返回 True 的元素放到新列表中,常和 lambda 函 数一起使用。 【例 7-31】filter() 函数的使用示例。 # 定义一个函数,能被 5 整除但不能被 7 整除 def func(x): if x%5 == 0 and x%7 != 0: return x list1 = [10,35,40,70] res1 = filter(func,list1) print(type(res1)) print(list(res1)) # 使用筛选函数 # 打印类型 # 与 lambda 函数配合使用 res2 = filter(lambda x:x<40,list1) print(list(res2)) 程序运行结果: [10, 40] [10, 35] 这个 filter() 函数类似于一个 for 循环,但它是一个内置函数,并且运算速度更快。大部分情 况下推导式的可读性更好。 138 第 7 章 函数及模块 【例 7-32】将 20~30 间的所有质数列出来。 res = filter(lambda x: not [x for i in range(2,x) if x%i == 0],range(20,30)) print(list(res)) 程序运行结果: [23, 29] 4. reduce reduce() 函数将一个可以迭代的对象应用到两个带有参数的方法上,对参数序列中元素进行累积。 注意:Python 3.x 中,reduce() 已经被移到 functools 模块里。使用时必须导入 functools 模块, 导入方法为:from functools import reduce。reduce 返回的必然是一个值,可以有初始值。 reduce() 函数基本语法结构如下: reduce(function, iterable[, initializer]) 其中,function 为函数,有两个参数:iterable 为可迭代对象,initializer 为可选,初始参数。 【例 7-33】reduce() 函数的使用示例。 from functools import reduce # 计算 1 到 4 的和 res1 = reduce(lambda x,y:x+y,[1,2,3,4]) print(res1) # 计算 1 到 4 的积 res2 = reduce(lambda x,y:x*y,[1,2,3,4]) print(res2) #lambda 函数中表达式复杂情况 list1 = [1,2,3,4] print(reduce(lambda x,y: x*y+5, list1)) ''' 计算过程如下: 这个式子只有两个参数,没有初始化值,那么就取列表前 2 项,通过 lambda 函数计算结果: 1*2+5=7, 上面计算的结果在与列表第 3 个元素通过 lambda 函数计算: 7*3+5=26 上面计算的结果在与列表第 4 个元素通过 lambda 函数计算: 26*4+5=109 ''' # 有初始化值,这时就不是取列表的前两项, # 而是取初始值为第 1 个,序列的第 1 个元素为第 2 个元素,开始进行 lambda 函数的应用计算。 print(reduce(lambda x,y: x+y, list1,6)) ''' 6 是初始值,也可以理解为第 3 个参数 计算过程: -->6+1=7 -->7+2=9 -->9+3=12 -->12+4=16 ''' 7.3.3 闭包和递归函数 在 Python 中有时要在一个函数内部定义另外一个函数,这时就可能形式闭包。有时要在函数 139 Python 程序设计 内部调用函数自身,这就会形成递归调用。 1. 闭包 在函数中嵌套定义另一个函数时,如果内部的函数引用了外部的函数的变量,那么内部函数 就被认为是闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数 被多次调用的过程中,这些私有变量能够保持其持久性。 在 Python 3 中,支持将函数当作对象使用,也就是可以将一个函数当作普通变量一样用作另 一个函数的参数和返回值。 闭包中被内部函数引用的变量,不会因为外部函数结束而被释放掉,而是一直存在内存中, 知道内部函数被调用结束。 【例 7-34】闭包函数的定义示例。 def outer(): out_var = "Python" def inner(): print("language:",out_var) return inner # 外部函数的变量 # 外部函数里面再定义一个函数 # 内层函数引用了外部函数的变量 # 外层函数返回的是内层函数的函数名称 g = outer() g() # 用 g 变量来接收 inner 函数的地址(函数名称) # 调用 inner 函数 上面程序中在函数 outer 中定义了一个 inner 函数。inner 访问了外部函数 outer 的变量 out_ var,并且函数返回值为 inner 函数。 在 Python 3 中创建一个闭包函数,必须满足以下条件: (1)闭包函数必须有内嵌函数。 (2)内嵌函数必须要引用外层函数的变量。 (3)闭包函数返回内嵌函数的地址(函数名称)。 如何判断一个函数是否是闭包函数?可以使用 _ _closure_ _ 内置属性,如果是则返回一个 cell 元素;不是闭包时,返回 None。 【例 7-35】闭包函数的判断示例。 # 闭包函数 def outer(): out_var = "Python" def inner(): print("language:",out_var) # 判断闭包函数的方法:_ _closure_ _ 内置属性 print(inner._ _closure_ _) return inner g = outer() g() # 非闭包函数 lang = 'python' def outer(): def inner(): print("language:",lang) print(inner._ _closure_ _) 140 第 7 章 函数及模块 return inner f = outer() f() 程序运行结果: (,) language: Python None language: python 闭包函数可以无参数也可以有参数。 【例 7-36】闭包函数的参数示例。 # 闭包函数无参数 def outer1(): # 无参数 name = 'Henson' def inner1(): print(name) print(inner1._ _closure_ _) # 使用 _ _closure_ _ 内置属性判断是否是一个闭包,打印出来有 cell,就说明是一个闭包 return inner1 # 返回内置函数的地址 g1 = outer1() print(g1) g1() # 函数名 () 本质上是内存地址 () # 用 g 变量接收 inner1 函数的地址 # 调用 inner1() 函数 # 闭包函数有参数 def outer2(age): # 有参数 age name = 'Anlic' def inner2(): print("my name is {},{}age.".format(name,age)) print(inner2._ _closure_ _) return inner2 g2= outer2(10) g2() # 10 程序运行结果: (,).inner1 at 0x0000025769EF4940> Henson (,) my name is Anlic,10age. 2. 递归函数 在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。 函数递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。 141 Python 程序设计 【例 7-37】使用递归函数的解阶乘问题。 # 编写一个阶乘函数 def fact(n): if n == 1: return 1 return n * fact(n - 1) print(fact(5)) # 调用 fact() 本身一次 函数计算过程如下 : ==> fact(5) ==> 5 * fact(4) ==> 5 * (4 * fact(3)) ==> 5 * (4 * (3 * fact(2))) ==> 5 * (4 * (3 * (2 * fact(1)))) ==> 5 * (4 * (3 * (2 * 1))) ==> 5 * (4 * (3 * 2)) ==> 5 * (4 * 6) ==> 5 * 24 ==> 120 【例 7-38】使用递归函数的输出斐波那契数列。 # 斐波那契数列指的是数列从第 3 项开始,每一项都等于前两项之和 def fibo(n): if n <= 1: return n else: return(fibo(n-1) + fibo(n-2)) #2 次调用 fibo() 本身 print(fibo(6)) 使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。 注意: (1)递归函数必须有一个明确的结束条件。 (2)每进入更深一层的递归时,问题规模相对于上一次递归都应减少。 (3)相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次输出就作为 后一次的输入)。 (4)递归效率不高,递归层次过多会导致栈溢出。 7.3.4 提升训练 本节主要是综合运用以上函数,解决实际问题。 【训练内容】 利用 map 和 reduce 编写一个 str_float 函数,把数字字符串转换为浮点数类型。例如,将“789.456” 转换成浮点数 789.456。 【设计实现思路】 把字符串转换成列表形式,利用列表的下标找到小数点的位置,再将列表转换为数字(浮点型)。 142 第 7 章 函数及模块 【参考代码】 from functools import reduce def str_float(s): list1 = [ ] # 整数位为 2 的处理 if s[0] == ".": # 当字符串第一位是 "." 时的处理方法 s = "0" + s # 整数位不为 0 时 for n in range(0, len(s)): # 取得 s 中的每一个字符,添加到列表里 list1.append(s[n]) if s[n] == ".": # 找到 "." 的位置, k = n p = len(list1) - k - 1 # p 代表小数点的位数 del list1[k] return int(reduce(lambda x, y: x + y, list1)) * 10 ** (-p) str = "45678.5689" print(str_float(str)) print(' 把数字字符串转换为浮点数类型:', str_float(str)) 7.4 模块 随着程序越来越长,为了方便维护,通常把脚本拆分成多个文件。Python 把各种定义存入一 个文件,在脚本或解释器的交互式实例中使用,这个文件就是模块。模块中的定义可以导入到其 他模块或主模块。 模块是一个包含所有定义的函数、变量、类和执行语句的文件,其后缀名是 .py。模块就是 Python 程序,换句话说,任何 Python 程序都可以作为模块。 Python 3 中大量使用模块具有以下优点: (1)提高代码的可维护性。为了解决代码量太多、难以维护的问题,Python 把相似功能的 函数分组,分别放到不同功能的文件中,这样每个文件包含的内容就相对较少,而且对应每一个 文件的大致功能都可以用文件名来体现,维护起来就方便。 (2)提高了代码的复用率。编写代码不必从零开始。当一个模块编写完毕,就可以被其他 地方引用。 (3)引用其他的模块。我们在编写程序的时候,也经常引用其他模块,包括 Python 内置的 模块和来自第三方的模块。 (4)避免函数名和变量名的冲突。每个模块有独立的命名空间,因此相同名字的函数和变 量完全可以分别存放在不同的模块中,所以我们自己在编写模块时,不必考虑名字会与其他模块 冲突。 7.4.1 模块的导入 编写程序时,想要调用一个模块中的变量、类或函数,需要先导入该模块。Python 3 中导入 模块的方式有以下几种: import 模块名 from 模块名 import 功能名 143 Python 程序设计 from 模块名 import * import 模块名 as 别名 from 模块名 import 功能名 as 别名 如果使用 from ... import ... 或 from ... import * 导 ? 多个模块的时候,且模块内有同名功能。当 调用这个同名功能的时候,调用到的是后面导入的模块的功能。 【例 7-39】模块的导入方式示例。 # 模块导入方式 1 import math print(math.sqrt(7)) print(math.fabs(-5)) # 模块导入方式 2 from math import sqrt,fabs print(sqrt(7)) print(fabs(-5)) # 模块导入方式 3 from math import* print(sqrt(7)) print(fabs(-5)) # 模块导入方式 4 import math as ma print(ma.sqrt(7)) print(ma.fabs(-5)) # 模块导入方式 5 from math import sqrt as sq,fabs as fa print(sq(7)) print(fa(-5)) 7.4.2 #as 定义别名 #as 定义别名 模块的搜索顺序 当导入一个模块时,Python 解析器需首先查找模块程序的位置。导入模块时不能在 import 语 句或 from 语句中指定模块的文件路径,只能使用 Python 设置的搜索路径。标准模块 sys 的 path 属 性可以用来查看当前搜索路径设置。 【例 7-40】查看当前搜索路径设置示例。 import sys print(sys.path) 程序运行结果: ['C:\\Users\\10471\\Desktop', 'D:\\python\\Lib\\idlelib', 'D:\\python\\ python38.zip', 'D:\\python\\DLLs', 'D:\\python\\lib', 'D:\\python', 'D:\\ python\\lib\\site-packages'] 在 Python 搜索路径列表中,第一个字符串表示 Python 当前工作目录。 Python 的解释器按照先后顺序依次在列表中搜索需要导入的模块。如果有就直接导入。如果 导入模块不在列表中,再搜索 sys.path(在 Shell 变量 pythonpath 下的每个目录)。pythonpath 目录, 包含 Python 的 path 路径:标准库目录和第三方包目录。sys.path 从以下位置初始化: 144 第 7 章 函数及模块 (1)执行文件的当前目录(在所有标准库前面)。 (2)PYTHONPATH(包含一系列目录名)。 (3)依赖安装时默认指定的。 每一个模块中都有一个内置属性 _ _file_ _ 属性,可以查询当前模块所在的路径。 【例 7-41】查询当前模块所在的路径示例。 import http print(http._ _file_ _) 程序运行结果: D:\python\lib\http\_ _init_ _.py 7.4.3 模块的 _ _name_ _ 属性 模块是一个可执行的 .py 文件。一个模块被另一个程序第一次引入时,其主程序将运行。如 果想让模块中的某一程序块不执行,可以使用 _ _name_ _ 属性来使程序仅调用模块中的一部分。 Python 中的模块(.py 文件)在创建之初会自动加载一些内建变量,_ _name_ _ 就是其中之一。 Python 模块中通常会定义很多变量和函数,这些变量和函数相当于模块中的一个功能。模块被导 入到别的文件中,可以调用这些变量和函数。 【例 7-42】_ _name_ _ 属性使用示例。 if _ _name_ _ == '_ _main_ _': print(' 程序自身在运行 ') else: print(' 我来自另一模块 ') 程序运行结果: 程序自身在运行 _ _main_ _ 在 Python 中作可以为函数的入口,而实际工程常用 if _ _name_=='_ _main_ _' 来表示 整个工程开始运行的入口。此外如果不想让程序功能的某部分被别的模块调用执行,如自定义模 块 Student 的“我的密码是 ***”,希望只有自己执行才可以查看密码,那么可以把这部分的功能 代码写在 if 语句里,只有 _ _name_ _=='_ _main_ _' 的时候才能执行。也可以理解为,在 if 语句之 外代码是最外层的,有点“全局变量”的意思,放入 if 里面就成了私有的了。 7.5 包 Python 程序由包、模块和函数组成。包是模块文件所在的目录,模块是实现某一特定功能的 函数和类文件。包是一种管理 Python 模块命名空间的形式,采用“点模块名称”,如图 7-1 所示。 图 7-1 包的基本结构 145 Python 程序设计 包是一个有层次的文件目录结构,它定义了由 n 个模块或 n 个子包组成的 Python 应用程序执 行环境。通俗一点来讲 :包是一个包含 _ _init_ _.py 文件的目录,该目录下一定得有这个 _ _init_ _.py 文件和其他模块或子包。 在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。目录 只有包含一个称为 _ _init_ _.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比 如称为 string)不小心地影响搜索路径中的有效模块。 如果想要引用 formats 文件夹的 aiffread.py 模块,可使用下列语句: from package_1.formats import aiffread import package_1.formats.aiffread 然后就可以调用 aiffread 模块中的类或函数。 7.6 库的应用 Python 3 中为了组织好模块,将多个模块合为一个包。而库就是一个完整的东西,我们可以 认为它是一个完整的项目打包,可直接调用或者运行。一个库中可能有多个包。简单地讲,包是 由很多模块组成,来实现某种功能。模块由函数和类组成。库是抽象概念,也可以是各种模块组成。 Python 3 中库可分为标准库和第三方库。 7.6.1 Python 标准库 Python 标准库也称为内置库或内置模块,是 Python 的重要组成部分。Python 标准库非常庞大, 所提供的组件涉及范围十分广泛,Python 程序员必须依靠它们来实现系统级功能,例如文件 I/O。 此外还有大量以 Python 编写的模块,提供了日常编程中许多问题的标准解决方案。其中有些模块 经过专门设计,通过将特定平台功能抽象化为平台中立的 API 来鼓励和加强 Python 程序的可移植 性。本节将介绍一些编程中经常使用的库。 1.math 库 math 库是 Python 提供内置数学类函数库。math 库一共提供了 4 个数学常数和 44 个函数。44 个函数分为 4 类,包括:16 个数值表示函数、8 个幂对数函数、16 个三角对数函数和 4 个高等特 殊函数,但 math 库不支持复数类型。 在使用 math 库前,用 import 导入该库。要查看 math 库中所包含的函数,可以使用 dir(math) 函数命令。 【例 7-43】看出 math 库的函数。 >>> import math >>> dir(math) 运行得到的结果: ['_ _doc_ _', '_ _loader_ _', '_ _name_ _', '_ _package_ _', '_ _spec_ _', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'comb', 'copysign', cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc'] math 库中的常数和部分常用函数介绍见表 7-1、表 7-2。实际编程中如需使用 math 库,还可 以查看 Python 的帮助文档。 146 第 7 章 表 7-1 常 函数及模块 math 库的 4 个数学常数 数 数学表示 功能描述 math.pi π 圆周率 math.e e 自然常数 e math.inf ∞ 正无穷大,负无穷大为 -math.inf math.nan 非浮点数标记 NaN(Not a Number) 表 7-2 math 库的部分常用函数 函 数 功能描述 示 例 math.fabs(x) 返回 x 的绝对值 >>> math.fabs(-5) 5.0 math.fmod(x,y) 返回 x/y 的余数,其值为浮点数 >>> math.fmod(7,3) 1.0 math.fsum([x,y,…]) 对括号内每个元素求和,其值为浮点数 >>> math.fsum([1,2,3]) 6.0 math.ceil(x) 向上取整,返回不小于 x 的最小整数 >>> math.ceil(1.324) 2 math.floor(x) 向下取整,返回不大于 x 的最大整数 >>> math.floor(1.324) 1 math.factorial(x) 返 回 x 的 阶 乘, 如 果 x 是 小 数 或 负 数, 返 回 ValueError >>> math.factorial(5) 120 math.gcd(a,b) 返回 a 与 b 的最大公约数 >>> math.gcd(28,64) 4 math.modf(x) 返回 x 的小数和整数部分 >>> math.modf(2.362) (0.3620000000000001, 2.0) math.trunc() 返回 x 的整数部分 >>> math.trunc(2.362) 2 math.pow(x,y)? 返回 x 的 y 次幂 >>> math.pow(5,2) 25.0 math.exp(x) 返回 e 的 x 次幂 >>> math.exp(2) 7.38905609893065 math.sqrt(x) 返回 x 的平方根 >>> math.sqrt(9) 3.0 返回 x 的对数值,只输入 x 时,返回 ln x >>> math.log(10) 2.302585092994046 math.log(x[,base]) 147 Python 程序设计 续上表 函 数 功能描述 示 例 math.log2(x) 返回 x 的 2 对数值 >>> math.log2(10) 3.321928094887362 math.log10(x) 返回 x 的 10 对数值 >>> math.log10(10) 1.0 math.degree(x) 角度 x 的弧度值转角度值 >>> math.degree(45) 2578.3100780887044 math.radians(x) 角度值转弧度值 >>> math.radians(45) 0.7853981633974483 math.hypot(x,y) 返回(x,y)坐标到原点(0,0)的距离 >>> math.hypot(10,12) 15.620499351813312 返回 x 的正弦函数值,x 是弧度值 >>> math.sin(45) 0.8509035245341184 math.sin(x) 2. random 库 random 库中函数主要用于产生各种分布的伪随机数序列。random 库中的随机函数是按照梅森 旋转算法模拟产生的,其概率是确定的、可见的,所以被称为伪随机数。而真正意义上的随机数 是按照实验过程中表现的分布概率随机产生的,其结果是不可预测的。 random 库可以生成不同类型的随机数函数,包含基本随机函数和扩展随机函数两类。基本随 机函数有:seed(), random();扩展随机函数有:randint(), getrandbits(), uniform(), randrange(), choice(), shuffle(),具体见表 7-3。 表 7-3 函 数 功能描述 示 例 seed(A) 初始化给定的随机数种子,默认为当前系统时间 >>>random.seed(10) # 产生种子 10 对应的序列 random() 产生 [0.0,1.0) 之间的随机小数 >>> random.random() 0.38806381191008044 randint(a,b) 产生一个 [a,b] 之间的随机整数 >>> random.randint(5,10) 9 getrandbits(s) 产生一个 s 比特长的随机数 >>> random.getrandbits(6) 62 uniform(a,b) 产生 a,b 之间的随机小数 >>> random.uniform(5,10) 6.769534952080674 产生在 [a,b) 中的随机数列,k 为步长 >>> random.randrange(10,90,2) 70 从序列 seq 中随机选择一个数 >>> random.choice((1,2,3,4,5,6)) 4 把序列 seq 随机打乱 >>> s=[1,2,3,4,5,6] >>> random.shuffle(s) >>> s [3, 5, 4, 1, 6, 2] randrange(a,b,k) choice(seq) shuffle(seq) 148 random 库中各函数 第 7 章 函数及模块 3. time 库 在 Python 3 中包含了若干个能够处理时间的库,而 time 库是最基本的一个,是 Python 中处理 时间的标准库。time 库能表达计算机时间,提供获取系统时间并格式化输出的方法,提供系统级 精确计时功能,用于程序性能分析。 time 库包含三类函数: (1)时间获取:time()、ctime()、gmtime()。 (2)时间格式化:strftime()、strptime()。 (3)程序计时:sleep()、perf_counter()。 时间获取函数的介绍见表 7-4。时间格式化函数的介绍见表 7-5。程序计时函数见表 7-6。其 中时间戳是从 1970 年 1 月 1 日 0:00 开始,到当前为止的一个以秒为单位的数值。 表 7-4 函 数 时间获取函数 功能说明 示 例 time() 获取当前时间戳,浮点数形式 >>> time.time() 1635856600.8981085 ctime() 获取当前时间,以可读的方式返回字符串时间 >>> time.ctime() 'Tue Nov 2 20:37:02 2021' 获取当前时间,并返回计算机可处理的时间格式 >>> time.gmtime() time.struct_time(tm_year=2021, tm_ mon=11, tm_mday=2, tm_hour=12, tm_ min=37, tm_sec=32, tm_wday=1, tm_ yday=306, tm_isdst=0) gmtime() 表 7-5 函 数 功能说明 strftime(tpl,ts) strptime(str,tpl) 将时间 ts 按照 tpl 格式格式化为直观的时间 示 例 >>> t=time.gmtime() >>> time.strftime("%y-%m-%d %H:%M:%S",t) '21-11-02 12:42:14' >>> t="2021-11-16" >>> time.strptime(t,"%Y-%m-%d") str 是字符串形式的时间值;tpl 是格式化模板字 time.struct_time(tm_year=2021, tm_ 符串,用来定义输入效果 mon=11, tm_mday=16, tm_hour=0, tm_ min=0, tm_sec=0, tm_wday=1, tm_yday=320, tm_isdst=-1) 表 7-6 函 时间格式化函数 程序计时函数 数 功能说明 示 例 perf_counter() 返回一个 CPU 级别的精确时间计数值, 单位为秒 >>> start_time=time.perf_counter() >>> end_time=time.perf_counter() >>> end_time-start_time 18.858366999999816 sleep(s) 产生时间函数,s 为拟休眠的时间,单 位是秒,可以是浮点数 >>> def wait(): time.sleep(3) >>> wait() # 程序会等待 3 秒才输出 149 Python 程序设计 4. datetime 库 Python 中提供了对时间日期的多种多样的处理方式,主要是在 time 和 datetime 这两个模块 里。在 Python 文档里,time 是归类在 Generic Operating System Services 中,换句话说,它提供的 功能是更加接近于操作系统层面的。通读文档可知,time 模块是围绕着 Unix Timestamp 进行的。 datetime 比 time 高级了不少,可以理解为 datetime 基于 time 进行了封装,提供了更多实用的函数, datetime 模块使用面向对象的交互取代了 time 模块中整形 / 元组类型的时间函数。 datetime 库以类的方式提供多种日期和时间表达式,具体见表 7-7。 表 7-7 datetime 库中的类 类名称 功能描述 datetime.date 日期表达类,可以表达年、月、日等 datetime.time 时间表达类,可以表示小时、分钟、秒、毫秒等 datetime.datetime 日期和时间表示的类,功能覆盖 date 和 time 类 datetime.timedelta 与时间间隔有关的类 datetime.tzinfo 与时区有关的信息表达类 这些类的对象是不可变的。本节将重点介绍 datetime.time 类和 datetime.datetime 类。 (1)datetime.time 类 datetime.time 类的定义格式如下: class datetime.time(hour, [minute[, second, [microsecond[, tzinfo]]]]) 其中,hour 为必须参数,其他为可选参数。各参数的取值范围见表 7-8,类方法和属性见表 7-9, 对象方法和属性见表 7-10。 表 7-8 datetime.time 类各参数的取值范围 参数名称 取值范围 hour [0, 23] minute [0, 59] second [0, 59] microsecond [0, 1000000] tzinfo tzinfo 的子类对象,如 timezone 类的实例 表 7-9 类方法 / 属性名称 描 time.max time 类所能表示的最大时间:time(23, 59, 59, 999999) time.min time 类所能表示的最小时间:time(0, 0, 0, 0) time.resolution 150 类方法和属性 述 时间的最小单位,即两个不同时间的最小差值:1 微秒 第 7 章 表 7-10 对象方法和属性 对象方法 / 属性名称 描 t.hour 时 t.minute 分 t.second 秒 t.microsecond 微秒 t.tzinfo 函数及模块 述 返回传递给 time 构造方法的 tzinfo 对象,如果该参数未给出,则返回 None t.replace(hour[, minute[, second[, microsecond[, tzinfo]]]]) 生成并返回一个新的时间对象,原时间对象不变 t.isoformat() 返回一个“HH:MM:SS.%f”格式的时间字符串 t.strftime() 返回指定格式的时间字符串,与 time 模块的 strftime(format, struct_time) 功能相同 【例 7-44】datetime.time 类使用示例。 >>> from datetime import time >>> time.min datetime.time(0, 0) >>> time.max datetime.time(23, 59, 59, 999999) >>> time.resolution datetime.timedelta(microseconds=1) >>> t = time(21,15,26,867) >>> t.hour 21 >>> t.minute 15 >>> t.second 26 >>> t.microsecond 867 >>> t.tzinfo >>> t.replace(19) datetime.time(19, 15, 26, 867) >>> t.strftime("%H%M%S") '211526' >>> t.isoformat() '21:15:26.000867' (2)datetime.datetime 类 datetime.datetime 类的定义格式如下: class datetime.datetime(year,month,day,hour=0,minute=0,second=0,microseco nd=0, tzinfo=None) 其中,year、 month 和 day 是必须要传递的参数,tzinfo 可以是 None 或 tzinfo 子类的实例。各 参数的取值范围见表 7-11。 151 Python 程序设计 表 7-11 datetime.datetime 类各参数的取值范围 参数名称 取值范围 year [MINYEAR, MAXYEAR] month [1, 12] day [1, 指定年份的月份中的天数 ] hour [0, 23] minute [0, 59] second [0, 59] microsecond [0, 1000000] tzinfo tzinfo 的子类对象,如 timezone 类的实例 注意,如果一个参数超出了这些范围,会引起 ValueError 异常。 datetime.datetime 类的方法和属性见表 7-12。 表 7-12 datetime.datetime 类的方法和属性 类方法 / 属性名称 描述 datetime.today() 返回一个表示当前日期时间的 datetime 对象 datetime.now([tz]) 返回指定时区日期时间的 datetime 对象 datetime.utcnow() 返回当前 utc 日期时间的 datetime 对象 datetime.fromtimestamp(timestamp[, tz]) 根据指定的时间戳创建一个 datetime 对象 datetime.utcfromtimestamp(timestamp) 根据指定的时间戳创建一个 datetime 对象 datetime.combine(date, time) datetime.strptime(date_str, format) 把指定的 date 和 time 对象整合成一个 datetime 对象 将时间字符串转换为 datetime 对象 【例 7-45】datetime.datetime 类的方法和属性使用示例。 >>> from datetime import datetime, timezone >>> datetime.today() datetime.datetime(2021, 11, 3, 10, 57, 33, 515775) >>> datetime.now() datetime.datetime(2021, 11, 3, 10, 57, 52, 140779) >>> datetime.utcnow() datetime.datetime(2021, 11, 3, 2, 58, 19, 952167) >>> import time >>> datetime.fromtimestamp(time.time()) datetime.datetime(2021, 11, 3, 10, 58, 58, 66899) >>> datetime.utcfromtimestamp(time.time()) datetime.datetime(2021, 11, 3, 2, 59, 7, 387416) >>> datetime.strptime('2017/02/04 20:49', '%Y/%m/%d %H:%M') datetime.datetime(2017, 2, 4, 20, 49) 152 第 7 章 函数及模块 datetime.datetime 类的部分对象方法和属性见表 7-13。 表 7-13 datetime.datetime 类的部分对象方法和属性 对象方法 / 属性名称 描 dt.year, dt.month, dt.day 年、月、日 dt.hour, dt.minute, dt.second 时、分、秒 dt.microsecond, dt.tzinfo 述 微秒、时区信息 dt.date() 获取 datetime 对象对应的 date 对象 dt.time() 获取 datetime 对象对应的 time 对象,tzinfo 为 None dt.timetz() 获取 datetime 对象对应的 time 对象,tzinfo 与 datetime 对象的 tzinfo 相同 dt.replace([year[, month[, day[, hour[, minute[, 生成并返回一个新的 datetime 对象,如果所有参数都没有指定,则返回一个与 second[, microsecond[, tzinfo]]]]]]]]) 原 datetime 对象相同的对象 dt.timetuple() 返回 datetime 对象对应的 tuple(不包括 tzinfo) dt.utctimetuple() 返回 datetime 对象对应的 utc 时间的 tuple(不包括 tzinfo) dt.isoformat([sep]) 返回一个“%Y-%m-%d” dt.ctime() 等价于 time 模块的 time.ctime(time.mktime(d.timetuple())) dt.strftime(format) 7.6.2 返回指定格式的时间字符串 Python 第三方库简介及安装 随着 Python 的发展,涉及更多领域、功能更强的应用以函数形式被开发出来,并通过开源形 式发布,这些函数库被称为第三方库。Python 吸引人的一个出众的优点就是它有众多的第三方库 函数,可以更高效率地实现开发。 Python 的标准库是随 Python 安装的时候默认自带的库,而 Python 的第三方库,需要下载后安 装。不同的第三方库安装和使用方法不同,它们都有各自的功能。但它们调用方式是一样的,都 需要用 import 语句调用。本节重点介绍 Python 的第三方库的安装。 安装 Python 库的方法最常用的有 3 种方法:①使用 pip 进行在线安装;②下载资源包,进行 离线安装;③通过 PyCharm 下载所需库包。下面分别介绍。 1. 使用 pip 命令行直接安装 打开 cmd 命令提示符窗口,通过命令“pip install 包名”进行第三库安装,此方法简单快捷, 不需进入 Python 安装目录,因为使用 pip 会自动识别到 Python 3 的安装目录。以安装 keras 为例, 如图 7-2 所示,pip 命令行直接安装第三方库。 图 7-2 pip 命令行直接安装第三方库 153 Python 程序设计 卸载第三方库使用“pip uninstall 库名”命令即可。 pip 默认是通过国外的源进行下载,速度太慢,且经常容易报错。因此推荐大家几个国内常 用的安装源,如下所示: (1)清华:https://pypi.tuna.tsinghua.edu.cn/simple。 (2)中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/。 (3)阿里云:http://mirrors.aliyun.com/pypi/simple/。 (4)华中理工大学:http://pypi.hustunique.com/。 (5)山东理工大学:http://pypi.sdutlinux.org/。 (6)华中科技大学 http://pypi.hustunique.com/。 (7)豆瓣:http://pypi.douban.com/simple/。 在使用 pip 的时候加参数,代码如下: -i https://pypi.tuna.tsinghua.edu.cn/simple。 例如,通过清华的镜像安装 keras,代码如下: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/keras 注意:安装成功会显示 Successfully installed keras。如果出现黄色字体警告,是由于 pip 库包 不是最新的,但 keras 库已成功安装,可随后对 pip 包进行更新。 更新命令如下: python -m pip install --upgrade pip 2. 手动下载第三方库资源包,进行离线安装 首先进入 Python 官网:https://www.lfd.uci.edu/~gohlke/pythonlibs/。以图 7-3 为例。 图 7-3 手动下载第三方库资源包 下载时注意 Python 版本以及位数,图 7-3 中 cp310 为适合 Python 的版本,win32 表示适合操 作系统为 32 位,win_amd64 表示适合操作系统为 64 位。 注意:cmd 命令需要先切换到库包文件路径下,然后再使用命令“pip install 包名”进行第三 库安装。 3. 通过 Pycharm 安装第三方库 通过 Pycharm 直接下载库包,在其菜单栏里就可完成。打开软件,选择“File”→“Settings ”→“Project”→“Project Interpreter”命令,即可进入第三方库安装界面,如图 7-4 所示。 在弹出的窗口中单击“+”按钮,弹出“Available Packages”窗口,如图 7-5 所示。 搜索并选中我们需要的第三方库,选择版本,然后单击“Install Package”按钮即可进行 安装。 154 第 7 章 图 7-4 函数及模块 Pycharm 中第三方库安装界面 图 7-5 “Available Packages”窗口 7.6.3 jieba 库的应用 jieba 是优秀的中文分词第三方库,又称“结巴”。它可以对中文文本进行分词操作,产生包 含词语的列表。它不仅具有分词功能,还能添加用户词典、提取关键词和词性标注等功能。 在英文字符串中可使用 split() 函数进行分割,得到单词的新列表,借助列表和字典等数据类 型进一步实现词频统计。下面的代码可将一个英语句子进行分词。 155 Python 程序设计 >>> str = "Looking for work or have a Python related position" >>> str.split(" ") ['Looking', 'for', 'work', 'or', 'have', 'a', 'Python', 'related', 'position'] 由于英语单词之间是以一个空格分隔的,所以可以适应 split(" ") 就可以把单词分割,但中文 的文本就没有这种空格区隔,因此这种方法对中文就不适用。中文文本需要通过分词获得单个的 词语。目前在 Python 下可以采用的较好的中文分词工具是结巴中文分词和中科院的分词系统。本 节主要介绍结巴中文分词。 jieba 分词算法使用了基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能生成词情 况所构成的有向无环图(Directed Acyclic Graph,DAG),采用了动态规划查找最大概率路径,找 出基于词频的最大切分组合。对于未登录词,采用了基于汉字成词能力的隐马可夫模型(Hidden Markov Model,HMM)模型,使用了 Viterbi 算法。 jieba 支持 3 种分词模式:精确模式、全模式、搜索引擎模式。其中,精确模式试图将句子最 精确地切开,适合文本分析;全模式则把句子中所有的可以成词的词语都扫描出来,速度非常快, 但是不能解决歧义;搜索引擎模式则在精确模式的基础上,对长词再次切分,提高召回率,适合 用于搜索引擎分词。 在中文分词第三方库中,jieba 最为简洁,在 Python 中的使用最为方便,可以满足基本的分词 需求。jieba 库主要功能函数见表 7-14。 表 7-14 jieba 库的主要功能函数 函数名称 cut(s) 功能说明 精确模式,返回生成器,遍历生成器即可获得分词的结果 cut(s,cut_all=True) 全模式,输出文本 s 中所有可能的单词 lcut_for_search(s) 搜索引擎模式,有多余,长词组 lcut(s) 精确模式,返回一个列表类型 lcut(s,cut_all=True) 全模式,返回一个列表类型 lcut_for_search(s) 搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,返回一个列表类型 add_word(w) 向分词词典中增加新词 w 【例 7-46】jieba 的使用示例。 >>> str = " 中华人民共和国是世界上最伟大的国家 " >>> jieba.lcut(str) # 精准模式 [' 中华人民共和国 ', ' 是 ', ' 世界 ', ' 上 ', ' 最 ', ' 伟大 ', ' 的 ', ' 国家 '] >>> jieba.lcut(str,cut_all = True) # 全模式 [' 中华 ', ' 中华人民 ', ' 中华人民共和国 ', ' 华人 ', ' 人民 ', ' 人民共和国 ', ' 共和 ', ' 共和国 ', ' 国是 ', ' 世界 ', ' 上 ', ' 最 ', ' 伟大 ', ' 的 ', ' 国家 '] >>> jieba.lcut_for_search(str) # 搜索引擎模式 [' 中华 ', ' 华人 ', ' 人民 ', ' 共和 ', ' 共和国 ', ' 中华人民共和国 ', ' 是 ', ' 世界 ', ' 上 ', ' 最 ', ' 伟大 ', ' 的 ', ' 国家 '] >>> jieba.add_word(" 与我无关 ") # 向分词词典增加新词 >>> jieba.lcut(" 这件事情与我无关 ") [' 这件 ', ' 事情 ', ' 与我无关 '] 156 第 7 章 7.7 函数及模块 提升训练 【训练内容】 《三国演义》和《水浒传》是我国四大名著中的其中之二。其中《三国演义》将每一个人物 形象刻画得栩栩如生。《水浒传》以记人为主,其主人公不是一两个人,而是一个群体——梁山 好汉。梁山好汉的故事不是围绕一个人展开的,而是围绕不同的人展开的。这两部名著在人物塑 造方面都具有里程碑意义的飞跃。为更进一步了解小说中人物的重要性,请统计两部小说的人物 出场次数。本节使用两种不同思路进行人物统计。 任务一 《三国演义》人物出场次数统计 【设计实现思路】 首先读取文件,用 jieba 库进行分词,对干扰我们统计的词语进行清洗和归一化处理,比如: 清洗时,我们将一些虚词、助词等放到 jieba 的停用词里面;云长、关公统一命名为关羽,还有 类似“关、”这样的,其实也是关羽出现了一次。然后对人物频率进行统计排序。 【参考代码】 import jieba #jieba 库的应用 txt = open(" 三国演义 .txt","r",encoding="utf-8").read() #jieba 分词后把不是人物的词停用,这些文字是多次程序运行所得 excludes = {" 将军 "," 却说 "," 二人 "," 后主 "," 上马 "," 不知 "," 天子 "," 大叫 "," 众将 ", " 不可 "," 主公 "," 蜀兵 "," 只见 "," 如何 "," 商议 "," 都督 "," 一人 "," 汉中 ", " 不敢 "," 人马 "," 陛下 "," 魏兵 "," 天下 "," 今日 "," 左右 "," 东吴 "," 于是 ", " 荆州 "," 不能 "," 如此 "," 大喜 "," 引兵 "," 次日 "," 军士 "," 军马 "," 此人 ", " 一面 "} words = jieba.lcut(txt) # 精确模式分词 counts = { } # 创建字典,存放人名 word 和次数 count for word in words: if len(word) == 1: # 排除一个字(非人名) continue # 同人不同名合并 elif word == " 诸葛亮 " or word == " 孔明曰 ": rword = " 孔明 " elif word == " 关公 " or word == " 云长 ": rword = " 关羽 " elif word == " 玄德 " or word == " 玄德曰 ": rword = " 刘备 " elif word == " 孟德 " or word == " 丞相 ": rword = " 曹操 " else: rword = word counts[rword] = counts.get(rword,0) + 1 # 排除非人名词语 for word in excludes: del counts[word] items = list(counts.items()) # 将字典转换为列表 items.sort(key = lambda x:x[1], reverse = True) # 打印出场数前 13 的人名 for i in range(13): 157 Python 程序设计 word, count = items[i] print("{0:<10}{1:>5}".format(word, count)) 程序运行结果: 曹操 孔明 刘备 关羽 张飞 吕布 孙权 赵云 司马懿 周瑜 袁绍 马超 魏延 1434 1373 1224 779 348 300 265 255 221 217 190 185 177 任务二 《水浒传》人物出场次数统计 【设计实现思路】 首先读取文件,用 jieba 库将文章进行拆分,然后创建一个字典,用来统计每个人物出现的次数。 我们可以先将人名输入到一个列表当中,循环记录次数的时候判断人名是否存在在列表当中。在 列表中,我们统计其出现的次数,否则不予以统计,这样可以很好地把不是人名的词语停用。 【参考代码】 import jieba #jieba 库的应用 txt = open(" 水浒传 .txt","r",encoding='utf-8').read() # 先把 108 位好汉的名字存在姓名列表中 list_names = [' 宋江 ', ' 卢俊义 ', ' 吴用 ', ' 公孙胜 ', ' 关胜 ', ' 林冲 ', ' 秦明 ', ' 呼延灼 ', ' 花荣 ',' 柴进 ', ' 李应 ', ' 朱仝 ', ' 鲁智深 ', ' 武松 ', ' 董平 ', ' 张清 ', ' 杨志 ',' 徐宁 ', ' 索超 ',' 戴宗 ', ' 刘唐 ', ' 李逵 ', ' 史进 ', ' 穆弘 ', ' 雷横 ', ' 李俊 ', ' 阮小二 ',' 张横 ', ' 阮小五 ', ' 张顺 ', ' 阮小七 ', ' 杨雄 ', ' 石秀 ', ' 解珍 ', ' 解宝 ', ' 燕青 ', ' 朱武 ',' 黄信 ', ' 孙立 ',' 宣赞 ', ' 郝思文 ', ' 韩滔 ', ' 彭玘 ', ' 单廷珪 ', ' 魏定国 ', ' 萧让 ', ' 裴宣 ',' 欧鹏 ', ' 邓飞 ',' 燕顺 ', ' 杨林 ', ' 凌振 ', ' 蒋敬 ', ' 吕方 ', ' 郭盛 ', ' 安道全 ', ' 皇甫端 ', ' 王英 ', ' 扈三娘 ',' 鲍旭 ', ' 樊瑞 ', ' 孔明 ', ' 孔亮 ', ' 项充 ', ' 李衮 ', ' 金大坚 ', ' 马麟 ',' 童威 ', ' 童猛 ',' 孟康 ', ' 侯健 ', ' 陈达 ', ' 杨春 ', ' 郑天寿 ', ' 陶宗旺 ', ' 宋清 ', ' 乐和 ',' 龚旺 ', ' 丁得孙 ',' 穆春 ', ' 曹正 ', ' 宋万 ', ' 杜迁 ', ' 薛永 ', ' 施恩 ', ' 李忠 ', ' 周通 ', ' 汤隆 ', ' 杜兴 ', ' 邹渊 ',' 邹润 ', ' 朱贵 ', ' 朱富 ', ' 蔡福 ', ' 蔡庆 ', ' 李立 ', ' 李云 ', ' 焦挺 ', ' 石勇 ',' 孙新 ', ' 顾大嫂 ',' 张青 ', ' 孙二娘 ', ' 王定六 ', ' 郁保四 ', ' 白胜 ', ' 时迁 ', ' 段景住 '] words = jieba.lcut(txt) count = {} # 用来计数 for word in words: if word not in list_names: # 如果根本不是人名,那就不记录这个分词了 continue count[word] = count.get(word,0) + 1 items = list(count.items()) # 将其返回为列表类型 158 第 7 章 函数及模块 items.sort(key = lambda x:x[1],reverse = True) # 排序 for i in range(20): name,numb = items[i] print("{0:<5} 出现次数为:{1:>5}".format(name,numb)) 程序运行结果: 宋江 李逵 武松 林冲 吴用 卢俊义 鲁智深 戴宗 柴进 公孙胜 花荣 秦明 燕青 朱仝 杨志 李俊 史进 石秀 张清 关胜 出现次数为: 2538 出现次数为: 1117 出现次数为: 1053 出现次数为: 720 出现次数为: 654 出现次数为: 546 出现次数为: 356 出现次数为: 312 出现次数为: 301 出现次数为: 272 出现次数为: 270 出现次数为: 258 出现次数为: 252 出现次数为: 245 出现次数为: 231 出现次数为: 230 出现次数为: 226 出现次数为: 197 出现次数为: 189 出现次数为: 186 习 题 一、选择题 1. Python 3 中关于函数参数的描述,以下选项中描述不正确的是 。 (A)可变参数接收的是一个元组 (B)关键字参数接收的是一个字典 (C)默认参数可以是不可变对象,也可以是可变对象 (D)在 Python 中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关 键字参数 2. Python 3 中关于函数形参和实参的描述,以下选项中描述不正确的是 。 (A)实参全称为实际参数,在调用函数时提供的值或者变量称作为实际参数 (B)在进行参数调用时,形参可以传一定的实参,也可以不传 (C)形参可以设置参数默认值,设置遵循从右至左原则 (D)形参全称是形式参数,在用 def 关键字定义函数时函数名后面括号里的变量称作为形式 参数 3. 执行下列代码,运行错误的是 。 def fib(a,b=1,c=1):pass" (A)fib(2) (B)fib(4,2,3) (C)fib(5,,3) (D)fib(5,c=3) 159 Python 程序设计 4. 执行下列代码,运行错误的是 。 def uinfo(**user): for item in user.items(): print(item) " (A)uinfo(name='henson') (B)uinfo(age=[25,15]) (C)uinfo('henson') (D)uinfo(age=28) 5. Python 中关于函数参数传递的描述,以下选项中描述不正确的是 。 (A)可变对象作为函数参数,Python 通过值传递 (B)传递参数可以分为两种:值传递和引用传递 (C)不可变对象作为函数参数,Python 通过值传递 (D)对于值传递,在函数中对参数的任何改动都不会影响到传入的变量 6. 通过下列选项,调用函数 greet()(函数定义如下),运行错误的是 。 def greet(args1,*args2,**args3): print(args1) print(args2) print(args3)" (A)greet({' 参数 1':'1',' 参数 2':'2'}) (B)greet(,(1,2)) (C)greet(['1','2']) (D)greet((1,2),) 7. 关于 Python 3 中的全局变量和局部变量,以下描述中错误的是 。 (A)局部变量只在函数内部有效 (B)全局变量在程序执行全过程中有效 (C)如果试图在函数外部访问其内部定义的变量,Python 解释器会报 NameError 错误 (D)全局变量不能和局部变量重名 8. 下列代码中 n 输出的值是 。 m = lambda x,y,z:(x+y)*z n = m(4,3,2)" (A)9 (B)16 (C)24 (D)14 二、填空题 1. 以下代码定义一个高阶函数,功能是计算第一个参数的绝对值减第二个参数的绝对值,请 补充横线上处的代码。 def absolute(x,y,f): print( absolute (5, -8, abs) ) 2. 模块是一个包含所有定义的函数、变量、类和执行语句的文件,其后缀名是 3. 如果想在函数内改变全局变量的值,则要使用 。 关键字。 4. 在 Python 3 中定义函数时,在代码块的最后可以选择 return、yield 关键字返回值给该函数, 没有关键字就返回 。 三、编程题 1. 定义一个函数,获取传入的序列对象的所有奇数位索引对应的元素,并将其作为新列表返 回给调用者。 160 第 7 章 函数及模块 2. 编写函数,判断一个数是否为素数。 3. 定义一个默认计算平方的幂函数。 4. 定义一个函数 count(str,z),统计句子中含字母 z 的单词的个数,并打印出含字母 z 的单词的 列表。 5. 在我国,公民身份证号码为 18 位,其中倒数第 2 位为性别,奇数为男,偶数为女。请定 义一个函数,用于根据身份证号码来判断性别。 6. 定义一个函数,求任意整数的阶乘。 7. 生成 6 位随机验证码,可包含数字、大写字母、小写字母。 8. 统计《红楼梦》中十二金钗出场次数,并排序打印出来。 《红楼梦》中十二金钗为:[' 林黛玉 ', ' 贾巧姐 ', ' 李纨 ', ' 秦可卿 ',' 薛宝钗 ', ' 贾元春 ', ' 贾探春 ', ' 史湘云 ', ' 妙玉 ', ' 贾迎春 ', ' 贾惜春 ', ' 王熙凤 ']。 161 第8章 面向对象程序设计 学习目标 (1)掌握面向对象程序设计思想。 (2)掌握类和对象的创建和使用。 (3)掌握属性和方法的使用。 (4)了解封装、继承和多态。 (5)通过封装和继承的讲解,让学生知道在编写程序时可以通过继承的方式重复利用原来 写好的代码,进而告诉学生中华民族有很多的传统美德是值得我们继承和发扬光大的,映射出中 华民族的传统美德的继承和弘扬。 章节导学 面向对象程序设计的核心是运用现实世界的概念,抽象地思考、解决问题。面向对象程 序设计把软件系统中相近相似的逻辑和操作、应用数据、状态,以类的型式描述出来,以对 象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。 8.1 8.1.1 面向对象程序设计概述 面向对象程序设计概念 程序设计有面向过程和面向对象两种。 1. 面向过程程序设计 面向过程程序设计就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步地实 现,使用的时候一个一个地依次调用就可以了。 面向过程程序设计的优点有:性能比面向对象高,因为类调用时需要实例化,开销比较大, 比较消耗资源。比如单片机、嵌入式开发、Linux/UNIX 等一般采用面向过程开发,性能是最重要 的因素。 面向过程程序设计的缺点有:不易维护、不易复用、不易扩展。 例如五子棋,面向过程的程序设计思路就是首先分析问题的步骤:开始游戏→黑子先走→绘 制画面→判断输赢→轮到白子→绘制画面→判断输赢→返回步骤“黑子先走”→输出最后结果。 把上面每个步骤用各自的函数来实现,问题就解决了。 162 第 8 章 面向对象程序设计 2. 面向对象程序设计 面向对象程序设计把构成问题的事务分解成各个对象。建立对象的目的不是为了完成一个步 骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。 面向对象程序设计的优点有:易维护、易复用、易扩展。由于面向对象有封装、继承、多态 性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。 面向对象程序设计的缺点有:性能比面向过程程序设计的要低。 例如五子棋,面向对象的程序设计思路则是从另外的思路来解决问题。整个五子棋可以分为 三类对象:①黑白双方,这两方的行为是一模一样的;②棋盘系统,负责绘制画面;③规则系统, 负责判定诸如犯规、输赢等。第①类对象(即玩家对象)负责接收用户输入,并告知第②类对象(即 棋盘对象)棋子布局的变化,棋盘对象接收到棋子的变化就要负责在屏幕上面显示出这种变化, 同时利用第③类对象(即规则系统)来对棋局进行判定。 面向对象编程(Object Oriented Programming,OOP)是一种封装代码的方法。代码封装,其 实就是隐藏实现功能的具体代码,仅留给用户使用的接口,就好像使用计算机,用户只需要使用 键盘、鼠标就可以实现一些功能,而根本不需要知道其内部是如何工作的。 众所周知,Python 3 是一款面向对象程序设计语言。在 Python 3 语言中,可以说 Python 3 的 一切皆对象。在 Python 3 中,所有的变量其实也都是对象,包括整型(int)、浮点型(float)、 字符串(str)、列表(list)、元组(tuple)、字典(dict)和集合(set)。以字典(dict)为例, 它包含多个函数,方便使用,例如 keys()、values()、item() 等。 8.1.2 类和对象 1. 类 类用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性 和方法。类由以下 3 个部分构成: (1)类的名称:类名。 (2)类的属性:一组数据,类中的所有变量称为属性。 (3)类的方法:行为或者动作方法,在 Python 3 中,类里定义的函数通常称为方法。不过, 和函数所不同的是,类方法至少要包含一个 self 参数。 2. 对象 对象是某一个具体事物的存在,在现实世界中是可以看得见、摸得着的,是可以直接使用的 一个具体实体。 对象是类的实例。 8.1.3 面向对象程序设计特点 Python 3 支持面向对象程序设计的三大特征:封装性、继承性和多态性。 1. 封装性 封装是指将一个计算机系统中的数据以及与这个数据相关的一切操作语言(即描述每一个对 象的属性以及其行为的程序代码)组装到一起,一并封装在一个有机的实体中,把它们封装在一 个“模块”中,也就是一个类中,为软件结构的相关部件所具有的模块性提供良好的基础。在面 向对象技术的相关原理以及程序语言中,封装的最基本单位是对象,而使得软件结构的相关部件 实现“高内聚、低耦合”的“最佳状态”便是面向对象技术的封装性所需要实现的最基本的目标。 163 Python 程序设计 对于用户来说,对象是如何对各种行为进行操作、运行、实现等细节是不需要刨根问底了解清楚的, 用户只需要通过封装外的通道对计算机进行相关方面的操作即可。这大大地简化了操作的步骤, 使用户使用起计算机来更加高效、更加得心应手。 2. 继承性 继承性是面向对象技术中的另外一个重要特点,主要指两种或者两种以上的类之间的联系与 区别。继承,顾名思义,是后者延续前者的某些方面的特点。而在面向对象程序设计中,继承则 是指一个对象针对另一个对象的某些独有的特点、能力,进行复制或者延续。如果按照继承源进 行划分,则可以分为单继承(一个对象仅从另外一个对象中继承其相应的特点)与多继承(一个 对象可以同时从另外两个或者两个以上的对象中继承所需要的特点与能力,并且不会发生冲突等 现象);如果从继承中包含的内容进行划分,则继承可以分为 4 类:取代继承(一个对象在继承 另一个对象的能力与特点之后将父对象进行取代)、包含继承(一个对象在将另一个对象的能力 与特点进行完全的继承之后,又继承了其他对象所包含的相应内容,结果导致这个对象所具有的 能力与特点大于等于父对象,实现了对于父对象的包含)、受限继承、特化继承。 3. 多态性 从宏观的角度来讲,多态性是指在面向对象技术中,当不同的多个对象同时接收到同一个完 全相同的消息之后,所表现出来的动作是各不相同的,具有多种形态。从微观的角度来讲,多态 性是指在一组对象的一个类中,面向对象技术可以使用相同的调用方式来对相同的函数名进行调 用,即便这若干个具有相同函数名的函数所表示的函数是不同的。 8.2 创建类和对象 面向对象程序设计是运用现实世界的概念,抽象地思考、解决问题。为了实现这个目标,在 面向对象程序设计中提出了类和对象两个概念。本节介绍类和对象的创建。 8.2.1 创建类 类是创建相同类型对象的蓝图或模板,决定了能够得到什么样的对象。在 Python 3 中创建类, 要使用关键字 class 进行类的声明。其基本语法格式如下: class 类名: 类主体 其中,类名为有效的标识符,一般使用大驼峰命名法命名;类主体有缩进。 在类主体部分里面,主要定义两个部分:一个是用于描述事物特征的属性,例如人的年龄、 姓名等;另一个是用户描述事物行为动作的方法,例如人的微笑、说话、上学等。 【例 8-1】创建一个类示例。 class Person: pass print(Person) print(type(Person)) print(id(Person)) # 定义一个类 Person # 类主体部分为空 # 打印类对象 # 打印类类型 # 打印类 id 程序运行结果: 2730518696120 164 第 8 章 8.2.2 面向对象程序设计 创建对象 创建对象的方法跟调用函数方法相似,直接使用类同名函数完成,基本格式如下: 对象名 = 类名 ([ 参数 ]) 在创建对象后,调用类中的属性和方法时,需要使用“.”运算符来实现,基本格式如下: 对象名 . 方法名 () 对象名 . 属性名 【例 8-2】创建对象并完成属性和方法的调用示例。 class Person: age = 20 name = " 小明 " def study(self): print(" 我热爱学习! ") def eat(self): print(" 今天吃的米饭! ") p1 = Person() print("{} 今年 {} 岁 ".format(p1.name,p1.age)) p1.study() # 定义一个类 Person # 定义属性 age # 定义属性 name # 定义方法 study() # 定义方法 eat() # 创建对象 p1 # 对象 p1 调用 age 和 name 属性 # 对象 p1 调用 study 方法 程序运行结果: 小明今年 20 岁 我热爱学习! 类对象支持两种操作:属性引用和实例化。属性引用使用和 Python 中所有的属性引用一样的 标准语法:obj.name。在例 8-2 中,p1.name 就是属性引用。 类对象创建后,类命名空间中所有的命名都是有效属性名。 8.2.3 提升训练 【训练内容】 创建一个汽车类(Car),根据汽车类,创建多个汽车对象,并完成属性和方法的调用。 【参考代码】 class Car: color = ' 黑色 ' wheelNum = 4 def move(self): print(' 车在奔跑 ...') def whistle(self): print(" 车在鸣笛 ... 嘀嘀 ...") # 创建一个汽车类对象 byd,并实现属性和方法的调用 byd = Car() print(byd.color) print(byd.wheelNum) byd.move() byd.whistle() print("-"*20) # 创建一个汽车类对象 xiaopeng,并实现属性和方法的调用 xiaopeng = Car() print(xiaopeng.color) 165 Python 程序设计 print(xiaopeng.wheelNum) xiaopeng.move() xiaopeng.whistle() 8.3 属性 属性实际上就是类中的变量,是用来存储描述类特征的值。建议用户在类定义的开始位置初 始化类属性,或者在构造方法中初始化成员属性。 8.3.1 成员属性和类属性 类的变量有两种:成员变量(实例变量)和类变量。定义在类中、方法之外的变量,称作类变量。 类变量是所有实例公有的变量,每一个实例都可以访问、修改类变量。 通过“self. 属性名”定义的属性称为成员属性,又称为实例对象属性。成员属性在类的内部 通过 self 访问,在外部通过对象实例访问。 其初始化基本格式如下: self. 属性名 = 初始值 在类的内部方法中访问基本格式如下: self. 属性名 = 值 self. 属性名 # 修改属性值 # 读取属性值 在类外部访问基本格式如下: 对象名 = 类名 () 对象名 . 属性名 = 值 对象名 . 属性名 # 创建对象 # 修改属性值 # 读取属性值 【例 8-3】成员属性示例(Teacher 类)。 class Teacher: def _ _init_ _(self): # 定义并初始化两个成员属性 name,age self.name = " 陈老师 " self.age = 35 # 在类中的方法里面通过 self 调用成员属性 def say_hello(self): print(" 你好,我是 {},今年 {}".format(self.name,self.age)) teacher1 = Teacher() # 创建 Teacher 对象 print(teacher1.name) # 通过对象调用成员属性 name print(teacher1.age) # 通过对象调用成员属性 age teacher1.say_hello() # 调用对象的方法 程序运行结果: 陈老师 35 你好,我是陈老师,今年 35 岁 类属性是定义在类里面、方法外面的一个属性,属于整个类,不是特定实例对象的一部分, 而是所有实例对象公用的一个属性。可以通过对象名调用类属性,而且也支持类名直接调用。 其初始化基本格式如下: 类变量名 = 初始值 166 第 8 章 面向对象程序设计 访问基本格式如下: 类名 . 类变量名 = 值 类名 . 类变量名 对象名 . 类变量名 = 值 对象名 . 类变量名 # 修改类变量值 # 读取类变量值 # 修改类变量值 # 读取类变量值 【例 8-4】类属性示例(Student 类)。 class Student: # 创建并初始化类属性 name = "Jacky" age = 29 def _ _init_ _(self): pass # 通过类名直接访问类属性 print(Student.name) print(Student.age) # 通过对象名访问类属性 student1 = Student() print(student1.name) print(student1.age) 程序运行结果: Jacky 29 Jacky 29 8.3.2 公有属性和私有属性 Python 类的成员没有访问控制权限,这与其他面向对象语言不同。通常约定以两个下画线开头, 但是不以两个下画线结束的属性是私有属性,其他的是共有属性。注意,不能直接访问私有属性, 可以通过方法间接访问,这样可以实现数据的保护。 【例 8-5】私有属性示例(Private 类)。 class Private: _ _privateStr = ' 私有内容 ' publicStr = " 公共内容 " def get_Str(self): print(self._ _privateStr) p1 = Private() p1.get_Str() print(p1.publicStr) print(p1._ _privateStr) # 类私有属性 # 类公有属性 # 类方法访问私有属性 # 通过对象调用成员方法间接访问私有属性 # 对象直接调用共有属性 # 对象直接调用私有属性,导致错误 程序运行结果: 私有内容 公共内容 Traceback (most recent call last): File "D:/python 程序设计 /python 爬虫 /python 程序设计教程案例 / 第 8 章 /8-5.py", line 9, in print(p1._ _privateStr) # 对象直接调用私有属性,导致错误 167 Python 程序设计 AttributeError: 'Private' object has no attribute '_ _privateStr' 8.3.3 提升训练 【训练内容】 定义一个含有成员变量(name、color)和类变量(num)的 Animal 类,其中 color 是私有变量。 【参考代码】 class Animal: num = 10 def _ _init_ _(self): self.name = " 变色龙 " self._ _color = " 红色 " def getColor(self): print(self._ _color) animal = Animal() print(Animal.num) print(animal.name) animal.getColor() 8.4 # 定义类变量 # 定义成员变量 # 定义私有变量 # 通过方法访问私有变量 # 创建 animal 对象 # 通过类名访问类变量 # 通过对象名访问成员变量 # 通过对象调用方法间接访问私有变量 方法 方法是定义在类中的函数。方法的定义同函数的定义一致。类中的方法可以分为 4 种:成员 方法、类方法、静态方法、魔法方法。 8.4.1 成员方法 成员方法是类中方法最常见的一种方法,由对象调用。成员方法的第一个参数一般为 self, 方法的基本格式如下: def 方法名 (self,[ 形参列表 ]): 方法体 成员方法的调用基本格式如下: 对象名称 . 方法名 ([ 实参列表 ]) 注意:方法第一个参数为 self,表示“自己”,也就是对象本身。当对象调用成员方法时, Python 解释器会自动把当前对象作为第一个参数传递给 self,用户只需要给其他形式参数传值就 可以了。 【例 8-6】成员方法示例。 class Person: def _ _init_ _(self,name): self.name = name def say_hello(self): print(" 你好!我叫 "+self.name) def say_age(self,age): print(" 我今年 {} 岁 ".format(age)) p1 = Person("Jacky") p1.say_hello() p1.say_age(35) 168 第 8 章 面向对象程序设计 程序运行结果: 你好!我叫 Jacky 我今年 35 岁 8.4.2 类方法和静态方法 类方法不对特定对象实行操作,是输入类本身的方法。类方法通过装饰器 @classmethod 来定义, 第一个参数为类对象本身,通常是 cls。类方法的基本格式如下: @classmethod def 类方法名 (cls,[ 形参列表 ]): 方法体 类方法的调用基本格式如下: 类名 . 类方法名 ([ 实参列表 ]) 或者: 对象名 . 类方法名 ([ 实参列表 ]) 注意:类方法的第一个参数 cls,表示的类对象。用户在调用类方法时,Python 解释器自动把 类对象传递给该参数。另外如果调用的是子类继承父类的类方法时,传给 cls 的是子类类对象, 而不是父类类对象。 【例 8-7】类方法示例。 class ChinaPerson: contry = " 中国 " # 构造方法 def _ _init_ _(self,name): self.name = name # 成员方法 def say_hello(self): print(" 我的名字叫:"+self.name) # 类方法 @classmethod def say_contry(cls): print(" 我的祖国是:"+ChinaPerson.contry) chinaP1 = ChinaPerson(" 小红 ") chinaP1.say_hello() chinaP1.say_contry() # 通过成员对象调用类方法 ChinaPerson.say_contry() # 通过类对象调用类方法 程序运行结果: 我的名字叫:小红 我的祖国是:中国 我的祖国是:中国 静态方法是类中声明的一个与类和对象实例无关的方法,它不对特定对象实例进行操作,如 果在静态方法中访问对象实例会出现错误。静态方法是通过 @staticmethod 装饰器来定义的,其基 本格式如下: @staticmethod def 静态方法名 ([ 形参列表 ]): 方法体 169 Python 程序设计 静态方法的基本调用格式如下: 类名 . 静态方法名 ([ 实参列表 ]) 或者: 对象名 . 静态方法名 ([ 实参列表 ]) 注意:静态方法是跟类和对象都无关的一个方法,一般使用在辅助功能上,例如计算数据、 绘图、打印等。 【例 8-8】静态方法示例。 class TempConverter: # 把用户输入的摄氏温度转换为华氏温度 @staticmethod def change(num): num = float(num) result = num * 9 / 5 + 32 return result num = input(" 请输入温度值:") result = TempConverter.change(num) print(" 转换后的温度是:" + str(result)) 程序运行结果: 请输入温度值:30 转换后的温度是:86.0 8.4.3 构造方法和析构方法 _ _init_ _() 方法就是构造方法,用于执行创建对象时的初始化工作,在创建完对象后调用,初 始化当前对象的实例。如果类中未定义构造方法,Python 会提供一个默认的构造方法。 【例 8-9】构造方法示例。 class Point: def _ _init_ _(self,x,y): self.x = x self.y = y point1 = Point(10,10) print(" 点 point1 在 ({},{})".format(point1.x,point1.y)) 程序运行结果: 点 point1 在 (10,10) _ _del_ _() 方法即析构方法,用于实现销毁类的对象实例时所需的操作,例如释放占用的网络 通道、占用的文件等。在 Python 收回对象空间是自动调用。 【例 8-10】析构方法示例。 class Dog: def _ _init_ _(self,name,color): self.name = name self.color = color def show(self): print("{} 的颜色是 {}".format(self.name,self.color)) def _ _del_ _(self): print(" 当对象销毁时,系统自动执行 ") 170 第 8 章 面向对象程序设计 dog1 = Dog(" 大黄 "," 黄色 ") dog2 = Dog(" 小白 "," 白色 ") dog1.show() del dog1 # 对象 dog1 执行完毕,主动销毁对象,系统自动调用析构方法 dog2.show() # 在程序结束后,dog2 没有主动销毁,会被 Python 机制清理,系统最后也会调用一次析构方法 程序运行结果: 大黄的颜色是黄色 当对象销毁时,系统自动执行 小白的颜色是白色 当对象销毁时,系统自动执行 8.4.4 提升训练 【训练内容】 编写面向对象程序实现如下功能:小明有 75 kg,爱跑步,爱吃东西,跑步体重减少 0.5 kg, 吃东西体重增加 1 kg。 【参考代码】 class Person: def _ _init_ _(self,name,weight): self.name = name self.weight = weight def run(self): print("%s 爱跑步 "%self.name) self.weight -= 0.5 def eat(self): print("$s 爱吃东西 "%self.name) self.weight += 1 p1 = Person(" 小明 ",75) p1.run() p1.eat() 8.5 继承 继承描述的是事物间的所属关系。通过继承可以使多种事物之间形成一种关系体系。类的继 承是指在一个现有类的基础上去构建一个新的类,构建出来的新类称为子类,被继承的类称为父 类。子类会自动拥有父类所有可以被继承的属性和方法。 8.5.1 单继承 子类在继承父类时,是在子类名称后面加上小括号,写入父类名称即可。如果没有写父类 名称,Python 默认其父类为 object 类。单继承就是指子类继承父类,有且只有一个。继承的基 本格式如下: class 子类名 ( 父类名 ): 类主体 注意:父类中私有的属性、方法不会被子类继承。 【例 8-11】单继承示例。 # 父类 171 Python 程序设计 class Person: def _ _init_ _(self,name,age): self.name = name self.age = age def say_hello(self): print(" 你好!我是 {},今年 {} 岁 ".format(self.name,self.age)) # 子类 class Teacher(Person): pass # 通过调用继承于父类的构造方法,创建子类的对象 t1 = Teacher(" 陈老师 ",35) # 通过子类的对象调用继承于父类的方法 t1.say_hello() 程序运行结果: 你好!我是陈老师,今年 35 岁 8.5.2 多继承 Python 中是支持多继承的,也就是说一个子类可以有多个父类。这个子类可以继承多个父类 的方法和属性。多继承是在子类名称后面的小括号里面添加各个父类名称,并且用逗号隔开。其 基本格式如下: class 子类名 ( 父类 1, 父类 2,...): 类主体 注意:在多继承模式中,子类继承父类属性和方法是有优先级的,写在前面的父类有优先被 继承的权力。 【例 8-12】多继承示例。 # 猫类 class Cat: def _ _init_ _(self,name,color): self.name = name self.color = color def showName(self): print("Cat 子类的名字是 " + self.name) def showColor(self): print("Cat 子类的颜色是 " + self.color) # 龙类 class Dragon: def _ _init_ _(self,name): self.name = name def showName(self): print("Dragon 子类的名字是 " + self.name) # 龙猫类 继承与龙类和猫类 class Totoro(Dragon,Cat): def _ _init_ _(self,name,color): Cat._ _init_ _(self,name,color) Dragon._ _init_ _(self,name) # 创建龙猫对象 totoro = Totoro(" 龙猫 ","black") 172 第 8 章 面向对象程序设计 # 调用了 Dragon 类的 showName 方法 totoro.showName() # 调用了 Cat 类的 showColor 方法 totoro.showColor() 程序运行结果: Dragon 子类的名字是龙猫 Cat 子类的颜色是 black 8.5.3 方法重写 在继承关系中,子类会自动拥有父类的方法和属性。如果父类的方法不能满足子类的需求, 子类可以根据自己的需求重新实现父类的方法,这就是方法的重写。方法重写时需要注意子类重 写的方法名称和参数列表需要同父类的方法一致。 【例 8-13】方法的重写示例。 # 猫类 class Cat: def _ _init_ _(self,name,color): self.name = name self.color = color def showName(self): print("Cat 子类的名字是 " + self.name) def showColor(self): print("Cat 子类的颜色是 " + self.color) # 龙类 class Dragon: def _ _init_ _(self,name): self.name = name def showName(self): print("Dragon 子类的名字是 " + self.name) # 龙猫类,继承于龙类和猫类 class Totoro(Dragon,Cat): def _ _init_ _(self,name,color,foot): Cat._ _init_ _(self,name,color) Dragon._ _init_ _(self,name) self.foot = foot def showName(self): print(" 子类重写父类的方法后的输出内容! ") print("Totoro 类的名字是 " + self.name) # 创建龙猫对象,构造方法进行了重写 totoro = Totoro(" 龙猫 ","black",4) # 调用了 Totoro 类的 showName 方法,这个方法也进行了重写 totoro.showName() # 调用了 Cat 类的 showColor 方法 totoro.showColor() 程序运行结果: 子类重写父类的方法后的输出内容! Totoro 类的名字是龙猫 Cat 子类的颜色是 black 173 Python 程序设计 8.5.4 提升训练 【训练内容】 利用继承知识,编写程序实现乐手弹奏乐器。乐手可以弹奏不同的乐器,从而发出不同的声音。 乐手可以弹奏的乐器包括二胡、钢琴和琵琶。 【参考代码】 class Instrument(): def makeSound(self): print(' 乐器发出美妙的声音 ') class Erhu(Instrument): def makeSound(self): print(' 二胡拉响人生 ') class Piano(Instrument): def makeSound(self): print(' 钢琴美妙无比 ') class Violin(Instrument): def makeSound(self): print(' 小提琴来啦 ') class Main(): def play(self,obj): #print(' 请问你要弹奏什么乐器 ') obj.makeSound() main = Main() v = Violin() p = Piano() erhu = Erhu() main.play(v) 8.6 自定义类使用举例 【例 8-14】利用面向对象的知识,完成一个简易的图书管理系统,包含查询图书、添加图书、 借阅图书、归还图书、退出 5 个功能。 class Book(object): def _ _init_ _(self, name, author, state, bookIndex): self.name = name self.author = author # 0:借出,1:未借出 self.state = state self.bookIndex = bookIndex def _ _str_ _(self): return " 书名 :《%s》 作者 :<%s> 状态 :<%s> 位置 :<%s>" % (self.name, self.author, self.state, self.bookIndex) class BookManage(object): books = [] 174 第 8 章 面向对象程序设计 def start(self): """ 图书管理初始化 """ b1 = Book('python', 'Guido', 1, "INS888") self.books.append(b1) self.books.append(Book('java', 'hello', 1, "IGS888")) self.books.append(Book('c', 'westos', 1, "INS880")) def menu(self): self.start() while True: print(""" 图书管理系统 1. 查询 2. 增加 3. 借阅 4. 归还 5. 退出 """) choice = input("Choice:") if choice == '1': name = input(" 书名:") self.checkBook(name) elif choice == '2': self.addBook() elif choice == '3': self.borrowBook() elif choice == '4': self.backBook() elif choice == '5': break else: print(" 请输入正确的选择 !") def addBook(self): name = input(" 书名:") self.books.append(Book(name, input(" 作者 :"), 1, input(" 书籍位置 :"))) print(" 添加图书 %s 成功 !" % (name)) def borrowBook(self): name = input(" 借阅书籍名称 :") ret = self.checkBook(name) if ret != None: if ret.state == 0: print(" 书籍《%s》已经借出 " % (name)) elif ret.state == 1: print(" 借阅《%s》成功 " % (name)) ret.state = 0 else: print(" 书籍《%s》不存在 !" % (name)) def backBook(self): name = input(" 归还书籍名称:") 175 Python 程序设计 book = self.checkBook(name) if book != None: print(" 书籍《%s》归还成功 "%name) book.state = 1 else: print(" 输入的书名有误! ") def checkBook(self, name): for book in self.books: if book.name == name: # 返回 book 对象 print(book) return book else: return None bookManage = BookManage() bookManage.menu() 习 题 一、选择题 1. 下列用于 Python 3 中初始化属性的方法是 。 (A)_ _add_ _() (C)_ _init_ _() (B)_ _del_ _() 2. Python 3 中,关于类和对象的关系,下列描述正确的是 (D) _ _name_ _() 。 (A)类是现实中事物的个体 (B)对象是根据类创建的,并且一个类只能对应一个对象 (C)对象描述的是现实的个体,它是类的实例 (D)对象用相同的属性描述,里面封装了相同的方法 3. Python 3 中,下列关于类的描述错误的是 。 (A)在类的内部,使用 def 关键字可以为类定义一个方法,类方法必须包含参数 self,且为 第一个参数 (B)Python 允许实例化的类访问私有数据 (C)如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法 (D)Python 使用了引用计数这一简单技术来跟踪和回收垃圾 4. Python 3 中,下列关于类的描述错误的是 。 (A)面向对象编程的一个重要特点就是数据封装 (B)self 代表类的实例,同时也是类 (C)子类可以继承父类的所有方法和属性,也可以重载父类的成员函数及属性 (D)定义在方法中的变量,只作用于当前实例的类 二、填空题 1. 类(class)由 2. 类是 176 的抽象, 、 和 是 3 个部分构成 的实例。 第 8 章 3. Python 3 中创建类,要使用关键字 面向对象程序设计 声明。 三、编程题 1. 创建一个 Person 类,在构造函数中初始化姓名 name 和年龄 age 属性,并且有 get_name 方 法获取人的姓名,有 get_age 方法获取人的年龄。 2. 创建 Student 类继承第 1 题创建的 Person 类的属性和方法,在构造函数中调用基类的构造 函数初始化共有的 name 和 age 属性,并将 Student 类独有的成绩属性 course(包括语文、数学、 英语 3 门成绩)进行初始化。创建有返回值的 get_MaxScore 方法用来返回 3 门科目中的最高分数, 创建有返回值的 get_AverageScore 方法用来返回 3 门科目中的平均分数。 177 附 录 全国高等学校计算机水平考试二级 ——Python 程序设计考试大纲及样题 一、考试目标与要求 考试目标: 测试考生系统掌握 Python 基本概念及其编程思想,了解 Python 计算生态,利用 Python 进行 数据处理及解决问题的能力。 考试要求: 1. 熟练掌握 Python 语言基础语法,掌握模块化程序设计思想,了解面向对象基本概念。 2. 熟练掌握 Python 常用标准库及第三方库的使用。 3. 能够阅读和分析 Python 程序,利用 Python 进行简单的数据处理及数据可视化。 二、考试内容 (一)基础知识 【考试要求】 掌握 Python 中的常量、变量、数据类型、运算符、表达式、内置函数和基本输入输出。 【主要考点】 1. 常量与变量。 2. 基本数据类型。 3. 运算符与表达式。 4. 数据类型转换。 5. 字符串类型及相关函数 / 方法。 6. 基本输入输出。 7. Python 常用内置函数。 8. 注释 (二)流程控制 【考试要求】 掌握 Python 程序格式框架及书写风格,掌握顺序、选择、循环结构在 Python 中的实现方法, 能灵活运用不同的控制结构解决实际问题。 【主要考点】 1. 顺序结构。 178 附录 全国高等学校计算机水平考试二级——Python 程序设计考试大纲及样题 2. 选择结构:单分支、双分支、多分支选择结构,选择结构的嵌套。 3. 循环结构:for 循环、while 循环、带有 else 子句的循环,break 与 continue 语句的作用。 4. 程序的异常处理:try...except。 (三)高级数据类型 【考试要求】 掌握 Python 中列表、元组、字典、集合的使用方法以及应用场合;了解不同数据类型的优缺 点和适用领域;学会综合应用多种数据类型解决实际问题。 【主要考点】 1. 列表:创建、删除、修改、切片、访问、排序;多维列表。 2. 元组:创建、删除、切片、访问。 3. 字典:创建、删除、添加、修改及访问,有序字典的使用。 4. 集合:创建、删除、访问,集合的并、交、差以及对称差等运算。 5. 列表、元组、集合的区别及相互转换。 (四)模块化程序设计 【考试要求】 了解模块化程序设计思想,掌握分而治之的结构化程序设计方法;掌握函数定义、参数传递 及高级函数的使用;了解变量作用域的含义;掌握匿名函数的定义及使用。 【主要考点】 1. 函数的定义和使用。 2. 函数参数:默认参数、可变参数、关键字参数。 3. 变量作用域:局部变量和全局变量。 4. 匿名函数的定义和使用。 5. 高阶函数(map、reduce、filter)的使用。 (五)面向对象程序设计 【考试要求】 了解 Python 中面向对象程序设计的基本方法,包括类的定义与使用、类的属性、类中的特殊 方法。 【主要考点】 1. 类的定义和使用。 2. 类成员访问控制。 3. 继承与多态。 4. 特殊方法与运算符重载。 (六)文件操作 【考试要求】 要求考生掌握文件的基本操作,掌握 CSV、Excel 文件的读取。 【主要考点】 1. 文件的使用:打开、读写、定位和关闭。 2. 一、二维数据组织:存储、处理及可视化。 3. 读写 CSV 格式数据文件。 179 Python 程序设计 4. 读取 Excel 格式数据文件(pandas 库)。 (七)模块与库 【考试要求】 了解 Python 生态系统,掌握 Python 常用标准库,运用第三方库解决实际问题 , 包括但不限于 网络爬虫、数据分析、文本处理、数据可视化、机器学习、Web 应用开发、网络编程等领域。 【主要考点】 1.Python 常用标准库:random、math、string、OS、time、datetime、urllib、request。 2. 第三方库:Matplotlib、pandas、jieba、wordcloud、requests、Beautiful Soup 4、XPath、NumPy、 Scrapy。 三、考试方式 机试。考试时间为 105 分钟,满分 100 分。 四、考试题目类型 考试分 3 种题型:单项选择题(15 题,30 分)、填空题(5 题,30 分)和操作题(程序代 码挖空题)(2 题,40 分)。 五、考试环境 Python 3.7 及以上版本,可选装 PyScripter、PyCharm、Anaconda、VSCode 等 IDE 编程环境。 六、考试样题 (一)单项选择题(15 题,30 分) 1. 下面 不是 Python 合法标识符。 (A)_ _name32 (B)name32 (C)_ _name32_ _ 2. 关于 Python 内存管理,下面说法错误的是 (D)32name 。 (A)当 Python 运行时垃圾回收也会启动 (B)Python 会自己管理内存,对于占用很大内存的对象,并不会马上释放 (C)Python 采用了类似 Windows 内核对象一样的方式来对内存进行管理 (D)Python 语言中,对象的类型和内存都是在运行时确定的 3、在 Python 3 中,下列不支持的数字类型是 。 (A)浮点型 (C)复数 (D)长整型 (C)元组 (D)数字 (B)分数 4. 下列不能作为字典的键是 (A)变量 。 (B)字符 5. 关于单引号、双引号、三引号的说法,错误的是 。 (A)单引号和双引号是等效的 (B)如果单引号、双引号、三引号要换行,都需要符号 (\),不可以直接换行 (C)三引号可以直接换行,并且可以包含注释 (D)三引号可以包含双引号,而不需要转义 6. 在 Python 3 中,下列 (A)m=(n=a+1) 语句是非法的。 (B)a,b=b,a 7. 在 Python 3 中,关于模块说法错误的是 180 (C)m,n='ab' 。 (D)m,n=3,4 附录 全国高等学校计算机水平考试二级——Python 程序设计考试大纲及样题 (A)模块是对象 (B)模块是一个 Python 文件 (C)模块能定义函数和变量,但不能定义类 (D)在不同的模块中可以存在相同名字的函数名和变量名 8. 关于变量的作用域说法错误的是 。 (A)如果一个局部变量和一个全局变量重名,则局部变量会覆盖全局变量 (B)Python 变量的作用域不由变量所在源代码中的位置决定 (C)全局变量如果是在函数内部赋值的话,必须经过声明 (D)全局变量在函数内部不经过声明也可以使用 9. 以下关于异常处理的描述,正确的是 。 (A)try 语句中由 except 子句,但也可以由 finally 子句 (B)Python 中不允许利用 raise 语句由程序主动引发异常 (C)引发一个不存在索引的列表元素会引发 NameError 错误 (D)Python 中,可以用异常处理捕获程序中的所有错误 10. 以下语句会无限循环下去的是 。 (A)for n in range(1000): (B)while 1<10: time.sleep(5) (C)while False: time.sleep(10) (D)while True: continue 11.Python 变量命名规则说法错误的是 break 。 (A)变量名不能包含空格,但可使用下划线来分隔其中的单词。 (B)不论是类成员变量还是全局变量,均不使用 m 或 g 前缀 (C)变量名只能包含字母、数字和下画线,以字母或数字开头 (D)变量名不应带有类型信息,因为 Python 是动态类型语言 12. 关于 Python 的 lambda 函数,以下选项中描述错误的是 。 (A)f = lambda x,y:x+y 执行后,f 的类型为数字类型 (B)lambda 函数是指一类无须定义标识符(函数名)的函数或子程序 (C)lambda 函数可以接收任意多个参数(包括可选参数),并且返回单个表达式的值 (D)lambda 的主体是一个表达式,而不是一个代码块 13. 以下选项中,不是 Python 语言保留字的是 (A)pass (B)continue 。 (C)except (D)next 14. 关于 Python 程序中与“缩进”有关的说法中,以下选项中不正确的是 。 (A)缩进统一为 4 个空格,且强制使用 (B)Python 中的每一条语句都有一个缩进级别,并且缩进级别会使用栈的数据结构进行存储 (C)Python 中的缩进是有语法含义的,它用来表示一个代码块 (D)一条比较长的语句也可以用缩进分成多行 15. 关于 Python 循环结构,以下选项中描述错误的是 。 (A)每个 continue 语句只能跳出当前层次的循环 (B)continue 语句被用来告诉 Python 跳过当前循环块中的剩余语句,然后继续进行下一轮循环 181 Python 程序设计 (C)break 跳出并结束当前整个循环,执行循环后的语句 (D)while 循环不会迭代 list 或 tuple 的元素,而是根据表达式判断循环是否结束 (二)填空题(5 题,30 分) 1. 表达式 [x for x in [1,2,3,4,5] if x<3] 的值为 ① 。 2. 设列表对象 aList 的值为 [3, 4, 5, 6, 7, 9, 11, 13, 15, 17],那么 aList [3:7] 得到的值是 ② 。 3. 从猫眼电影热门榜单上爬取电影信息(包括电影名、主演、上映时间、评分),结果 保 存 形 式 为 item= [(' 少 年 的 你 ',' 主 演: 周 冬 雨 ',' 上 映 时 间:06-27',9.5), ┈ ], 那 么 使 用 item. sort(key=lambda x:x[3],reverse=True) 语句的作用是对 行 ④ ③ (电影名 / 主演 / 上映时间 / 评分)进 (升序 / 降序)排列。 4. list1 中存放李小姐结婚请帖名单,她发现把“李泓”写成了“李弘”,少写“张瀚”, “刘 斯”名字有重复,得把第一个删除,请补全横线处的代码,请你帮她更正过来。 list1=['张杨华','韩冠浩','刘斯','李弘','关申海','李晓思','李思慧','刘潇斯','刘斯'] ⑤ (' 李泓 ') ⑥ (' 刘斯 ') ⑦ (' 张瀚 ') print(list1) 5. t_array 中存放 4×4 的二维数组,下列程序用于计算该二维数组各元素的和,请完善下列 代码。 t_array=[(4,5,6,7),(8,9,10,11),(12,13,14,15),(16,17,18,19)] sum= ⑧ for arr in t_array: for i in ⑨ : ⑩ print(sum) (三)操作题(程序代码填空题)(2 题,40 分) 1. 在考试操作试题文件夹里,打开“gdpython1.py”文件,按下面的要求完成操作:从键盘上 输入一段文字,调用下列函数,能统计出该段文字的中文个数、英文个数、数字个数和标点符号 的个数。请完善下列代码。 import string def str_count(str): count_en = count_dg = count_sp =count_zh = for s in str: # 统计英文字母个数 if s in string. ① : count_en += 1 # 统计数字个数 elif s. ② : count_dg += 1 # 统计中文个数 elif s. ③ : count_zh += 1 # 统计空格个数 elif s. ④ :count_sp += 1 # 统计标点符号个数 else: count_pu += 1 print(' 英文字符:', count_en) print(' 数字:', count_dg) 182 count_pu = 0 附录 全国高等学校计算机水平考试二级——Python 程序设计考试大纲及样题 print(' 空格:', count_sp) print(' 中文:', count_zh) print(' 标点符号:', count_pu) 2. 在考试操作试题文件夹里,打开“gdpython2.py”文件,按下面的要求完善列代码,生产随 机激活码。具体要求如下: (1)使用 random 库,采用 0x1011 作为随机种子。 (2)每次产生 20 条激活码,且首字不能一样。每条激活码单独一行。 (3)必须包含大写字母、小写字母和数字,必需包括“!@#S%&*-”8 个特殊符号中的某一个。 (4)每次激活码产生的长度为 12 位。 (5)不能重复。 (6)程序所产生的激活码保存在“激活码 .txt”文件中。 import random random. ① s="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ0123456789!@#$%&*-" ls=[] excludes="" while ② : pwd="" for i in ③ : ④ if pwd[0] in excludes: ⑤ else: ls. ⑥ excludes+= ⑦ with open(' 激活码 .txt','w') as f: f.write('\n'. ⑧ ) 183

相关文章