示例文件可以手动创建,或者通过以下两个网站之一下载源代码压缩包,并从中提取得到
1. 图灵社区官网的随书下载:
https://www.ituring.com.cn/book/3038
2. 英文版官网的首页:
https://ehmatthes.github.io/pcc_3e/
内存:计算机中一个读写速度很快的存储区域,程序运行时所需要的数据一般都存放在其中(文件则存储在硬盘上)。
例如,变量中的的数据(在这里是文本文件中的全部内容)便是存放在内存中的。
(如果你想要学习具体的概念,请阅读计算机组成原理相关书籍)
简写取前三个字母的命名方式,在 Python 中很常见
比如 string 使用 str 表示,integer 使用 int 表示等等,这类简写一般不容易有歧义,且能简化我们的输入,使代码短小精悍
银行:Bank
公园:Park
银行:Bank
公园:Park
在显示文件路径时,Windows 系统使用反斜杠(\)而不是斜杠(/)。但为了避免问题,在你还没有弄明白一切之前,请在代码中始终使用斜杠,即便在 Windows 系统中也是如此。对于复制而来的路径,需要你手动更改其中的字符。
另外,有时候你还会在路径中看到点号:
一个点号 . 表示当前路径,写和不写没有太大区别,但却能使得路径的表达更加清晰。
两个点号 .. 表示上一级路径,比如 /right 的上一级是起点,即 /right/.. 就相当于 /
读取文本文件和读取用户输入时类似,Python 会将其中的所有文本都解释为字符串。
如果要将读取的数作为数值使用,就必须使用 int() 函数将其转换为整数,或者使用 float() 函数将其转换为浮点数。
注意:要运行这个程序(以及后面的众多示例),需要从本书主页下载相关的资源。
示例文件可以通过以下两个网站之一下载源代码压缩包,并从中提取得到
1. 图灵社区官网的随书下载:
https://www.ituring.com.cn/book/3038
2. 英文版官网的首页:
https://ehmatthes.github.io/pcc_3e/
可以回忆一下字符串的定义:字符串是一串字符,因此我们可以把它当作一个列表,并可以使用列表相关的方法,来操作其中的每一个字符。
读取文件的内容后,就能以任意 Python 支持的方式,对文本进行分析了。
Python 只能将字符串写入文本文件。如果要将数值数据存储到文本文件中,必须先使用函数 str() 将其转换为字符串格式。
注:路径中的目录部分如果不存在,是不会自动创建的,并且会给出错误。
后面将介绍如何使用 pathlib 检查指定的文件是否存在。
traceback:回溯,(堆栈的)跟踪信息,包含执行到的异常代码、代码路径、调用过程和异常描述等信息
由于“回溯”两个字容易混淆,所以还是偏向用使用英文原文,这里记住这个单词和大概的含义即可。
当然了,如果轮胎已经无法修补了,那就只好更换了:我们可以根据 try 中给出的异常,对应进行针对性处理,如果无法处理,也可 以终止程序的运行。
其中 ZeroDivisionError 是个异常对象。Python 在无法按你的要求做时,就会创建这种对象。在这种情况下,Python 将停止运行程序,并指出引发了哪种异常,而我们可根据这些信息对程序进行修改来修正代码。
except ZeroDivisionError 只能匹配处理 ZeroDivisionError 异常
匹配异常的子类
程序崩溃可不好,让用户看到 traceback 也不是个好主意。
不懂技术的用户会感到糊涂,怀有恶意的用户还能通过 traceback 获悉你不想让他们知道的信息。
例如,他们将知道你的程序文件的名称,还将看到部分不能正确运行的代码。
有时候,训练有素的攻击者可根据这些信息判断出可对你的代码发起什么样的攻击。
try-except-else
通过预测可能发生错误的代码,可编写稳健的程序。它们即便面临无效数据或缺少资源,也能继续运行,不受无意的用户错误和恶意攻击的影响。
【拓展】这里使用 read_text() 的方式与前面稍有不同,括号中多了 encoding='utf-8',这指明了读取时所用的文件编码。
如果系统的默认编码与要读取的文件的编码不一致,参数 encoding 必不可少,通过它来指定文件编码可以避免读取时发生错误。
如果要读取的文件不是在你的电脑中创建的(不同电脑的默认编码可能会有所不同),读取时会更容易发生错误。
注:这个知识点放的略有突兀,保留是考虑配套讲义应尽量与原书一致,实际讲解的时可考虑拓展、移后或删除,为了系统性地学习编码的相关知识,个人推荐可以阅读《流畅的 Python》的第四章部分。
这里的 traceback 比前面的那些都长,对于这样复杂的 traceback,通常建议先看 traceback 的最后一行。
往下的其余部分列出了一些代码,它们来自打开和读取文件涉及的库。通常,不需要详细阅读和理解 traceback 中的这些内容。
这样,当找不到文件时,Python 将运行 except 代码块中的代码,从而显示一条友好的错误消息,而不是 traceback。
最后,打印一条消息,指出文件包含多少个单词。
将文件放到哪里?请回忆路径相关知识,要补充的一点是,相对路径是以程序运行的所在目录为起点的。
注:实际上,输出的数会略微偏大,因为这里使用的文本文件包含出版商提供的额外信息,但它与童话爱丽丝漫游奇境记(Alice in Wonderland)的长度基本一致。
这些代码大多与原来一样,只是被移到了函数 count_words()中,并且增加了缩进量。
在修改程序的同时更新注释是个不错的习惯,因此我们将注释改成了文档字符串,并稍微调整了一下措辞
在这个示例中,使用 try-except 代码块有两个重要的优点:
一是避免用户看到 traceback,二是让程序可以继续分析能够找到的其他文件。如果不捕获因找不到 siddhartha.txt 而引发的 FileNotFoundError 异常,用户将看到让一般人困惑的完整 traceback,并且程序也将在尝试分析 Siddhartha 后停止运行——根本不会继续分析 Moby Dick 和 Little Women。
要注意的是,如果你不知道代码写成这样是否合适,那最好不要这样做,静默失败容易导致发生“看不见”的问题,当程序执行后的预期结果不一致时,会使得你难以排查问题的原因。pass 关键字很有用,我们以后还会遇到它。
编写得很好且经过恰当测试的代码不容易出现内部错误,如语法错误和逻辑错误,但只要程序依赖于外部因素,如用户输入、是否存在指定的文件、是否有网络连接,就有可能出现异常。我们凭借经验判断该在程序的什么地方包含异常处理块,以及出现错误时该向用户提供多少相关的信息。
JSON(JavaScript Object Notation)格式最初是为JavaScript 开发的,但随后成了一种通用的格式,被包括Python 在内的众多语言采用。
我们读取数据文件的内容,并使用 json.loads() 将恢复的数据赋给变量 username。有了已恢复的用户名,就可以使用个性化的问候语欢迎用户回来了:
这里原本可以编写一个 try-except 代码块,以便在文件 username.json 不存在时采取合适的措施,但我们没有这样做,而是使用 了pathlib 模块提供的一个便利方法 path.exists()。如果文件 username.json 不存在,就提示用户输入用户名,并存储用户输入的值。此外,还会打印一条消息,指出当用户再回来时我们还会记得他。
无论执行的是哪个代码块,都将显示用户名和合适的问候语。
如果文件 username.json 不存在,就提示用户输入用户名,并存储用户输入的值。此外,还会打印一条消息,指出当用户再回来时我们还会记得他。
无论执行的是哪个代码块,都将显示用户名和合适的问候语。
```python
from pathlib import Path
import json
def greet_user():
"""问候用户,并指出其名字"""
path = Path('username.json')
if path.exists():
contents = path.read_text()
username = json.loads(contents)
print(f"Welcome back, {username}!")
else:
username = input("What is your name? ")
contents = json.dumps(username)
path.write_text(contents)
print(f"We'll remember you when you come back, {username}!")
greet_user()
```
事实上来说,这不算多的,不过这里的主要目的,是为了简单演示重构的思路。
```python
from pathlib import Pathimport jsondef get_stored_username(path): """如果存储了用户名,就获取它""" if path.exists(): contents = path.read_text() username = json.loads(contents) return username else: return Nonedef greet_user(): """问候用户,并指出其名字""" path = Path('username.json') username = get_stored_username(path) if username: print(f"Welcome back, {username}!") else: username = input("What is your name? ") contents = json.dumps(username) path.write_text(contents) print(f"We'll remember you when you come back, {username}!")greet_user()
```
```python
from pathlib import Pathimport jsondef get_stored_username(path): """如果存储了用户名,就获取它""" if path.exists(): contents = path.read_text() username = json.loads(contents) return username else: return Nonedef get_new_username(path): """提示用户输入用户名""" username = input("What is your name? ") contents = json.dumps(username) path.write_text(contents) return usernamedef greet_user(): """问候用户,并指出其名字""" path = Path('username.json') username = get_stored_username(path) if username: print(f"Welcome back, {username}!") else: username = get_new_username(path) print(f"We'll remember you when you come back, {username}!")greet_user()
```
进阶补充:如果我们编写的代码将涉及更深的嵌套,那么还能通过使用“卫语句”进一步简化。
greet_user 函数没有那么复杂,但同样能进行简化。