目录
1、读取文件
1.1 读取文件的全部内容
1.2 相对路径和绝对路径
1.3 访问文件中的各行
1.4 使用文件中的内容
1.5 包含100万位的大型文件
1.6 圆周率中的生日
2、写入文件
2.1 写入一行
2.2 写入多行
3、异常
3.1 处理ZeroDivisionError 异常
3.2 使用try-except代码块
3.3 使用异常避免崩溃
3.4 else代码块
3.5 处理FileNotFoundError异常
3.6 分析文本
3.7 使用多个文件
3.8 静默失败
3.9 决定报告哪些错误
3.10 count()求特定单词
4、存储系统
4.1 使用json.dumps() 和 json.loads()
4.2 保存和读取用户生成的数据
4.3 重构
要使⽤⽂本⽂件中的信息,⾸先需要将信息读取到内存中。既可以⼀次性 读取⽂件的全部内容,也可以逐⾏读取。
要读取⽂件,需要⼀个包含若⼲⾏⽂本的⽂件。下⾯来创建⼀个⽂件,它 包含精确到⼩数点后 30 位的圆周率值,且在⼩数点后每 10 位处换⾏:
3.1415926535
8979323846
2643383279
下⾯的程序打开并读取这个⽂件,再将其内容显⽰到屏幕上:
from pathlib import Path
❶ path = Path('pi_digits.txt')
❷ contents = path.read_text()
print(contents)
要使⽤⽂件的内容,需要将其路径告知 Python。路径(path)指的是⽂件或
⽂件夹在系统中的准确位置。Python 提供了 pathlib 模块,让你能够更轻
松地在各种操作系统中处理⽂件和⽬录。提供特定功能的模块通常称为库 (library)。这就是这个模块被命名为 pathlib 的原因所在。
这⾥⾸先从 pathlib 模块导⼊ Path 类。Path 对象指向⼀个⽂件,可⽤ 来做很多事情。例如,让你在使⽤⽂件前核实它是否存在,读取⽂件的内 容,以及将新数据写⼊⽂件。这⾥创建了⼀个表⽰⽂件 pi_digits.txt 的 Path 对象,并将其赋给了变量 path(⻅❶)。由于这个⽂件与当前编写 的 .py ⽂件位于同⼀个⽬录中,因此 Path 只需要知道其⽂件名就能访问 它。
创建表⽰⽂件 pi_digits.txt 的 Path 对象后,使⽤ read_text() ⽅法来读 取这个⽂件的全部内容(⻅❷)。read_text() 将该⽂件的全部内容作为 ⼀个字符串返回,⽽我们将这个字符串赋给了变量 contents。在打印 contents 的值时,将显⽰这个⽂本⽂件的全部内容:
3.1415926535
8979323846
2643383279
相⽐于原始⽂件,该输出唯⼀不同的地⽅是末尾多了⼀个空⾏。为何会多 出这个空⾏呢?因为 read_text() 在到达⽂件末尾时会返回⼀个空字符 串,⽽这个空字符串会被显⽰为⼀个空⾏。 要删除这个多出来的空⾏,可对字符串变量 contents 调⽤ rstrip():
from pathlib import Path
path = Path('pi_digits.txt')
contents = path.read_text()
contents = contents.rstrip()
print(contents)
Python ⽅法 rstrip() 能删除字符串末尾的空⽩。现在,
输出与原始⽂件的内容完全⼀致了:
3.1415926535
8979323846
2643383279
要在读取⽂件内容时删除末尾的换⾏符,可在调⽤ read_text() 后直接
调⽤⽅法 rstrip():
contents = path.read_text().rstrip()
当将类似于 pi_digits.txt 这样的简单⽂件名传递给 Path 时,Python 将在当 前执⾏的⽂件(即 .py 程序⽂件)所在的⽬录中查找。
根据你组织⽂件的⽅式,有时可能要打开不在程序⽂件所属⽬录中的⽂ 件。例如,你可能将程序⽂件存储在了⽂件夹 python_work 中,并且在⽂件 夹 python_work 中创建了⼀个名为 text_files 的⽂件夹,⽤于存储程序⽂件 要操作的⽂本⽂件。虽然⽂件夹 text_files 在⽂件夹 python_work 中,但仅 向 Path 传递⽂件夹 text_files 中的⽂件的名称也是不可⾏的,因为 Python 只在⽂件夹 python_work 中查找,⽽不会在其⼦⽂件夹 text_files 中查找。 要让 Python 打开不与程序⽂件位于同⼀个⽬录中的⽂件,需要提供正确的 路径。
在编程中,指定路径的⽅式有两种。⾸先,相对⽂件路径让 Python 到相对 于当前运⾏的程序所在⽬录的指定位置去查找。由于⽂件夹 text_files 位于 ⽂件夹 python_work 中,因此需要创建⼀个以 text_files 打头并以⽂件名结 尾的路径,如下所⽰:
path = Path('text_files/filename.txt')
其次,可以将⽂件在计算机中的准确位置告诉 Python,这样就不⽤管当前 运⾏的程序存储在什么地⽅了。这称为绝对⽂件路径。在相对路径⾏不通 时,可使⽤绝对路径。假如 text_files 并不在⽂件夹 python_work 中,则仅 向 Path 传递路径 'text_files/filename.txt' 是⾏不通的,因为 Python 只在⽂件夹 python_work 中查找该位置。为了明确地指出希望 Python 到哪⾥去查找,需要提供绝对路径。
绝对路径通常⽐相对路径⻓,因为它们以系统的根⽂件夹为起点:
path = Path('/home/eric/data_files/text_files/filename.txt')
使⽤绝对路径,可读取系统中任何地⽅的⽂件。就⽬前⽽⾔,最简单的做 法是,要么将数据⽂件存储在程序⽂件所在的⽬录中,要么将其存储在程 序⽂件所在⽬录下的⼀个⽂件夹(如 text_files)中。
注意:在显⽰⽂件路径时,Windows 系统使⽤反斜杠(\)⽽不是斜杠 (/)。但是你在代码中应该始终使⽤斜杠,即便在 Windows 系统中
也是如此。在与你或其他⽤户的系统交互时,pathlib 库会⾃动使⽤
正确的路径表⽰⽅法。
你可以使⽤ splitlines()
⽅法将冗⻓的字符串转换为⼀系列⾏,再使⽤ for
循环以每次⼀⾏的⽅式检查⽂件中的各⾏:
from pathlib import Path
path = Path('pi_digits.txt')
❶ contents = path.read_text()
❷ lines = contents.splitlines()
for line in lines:
print(line)
将⽂件的内容读取到内存中后,就能以任意⽅式使⽤这些数据了。
from pathlib import Path
path = Path('pi_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
pi_string = ''
❶ for line in lines:
pi_string += line
print(pi_string)
print(len(pi_string))
变量 pi_string
存储的字符串包含原来位于每⾏左端的空格。要删除这 些空格,可对每⾏调⽤ lstrip():
--snip--
for line in lines:
pi_string += line.lstrip()
print(pi_string)
print(len(pi_string))
注意:在读取⽂本⽂件时,Python 将其中的所有⽂本都解释为字符 串。如果读取的是数,并且要将其作为数值使⽤,就必须使⽤ int() 函数将其转换为整数,或者使⽤ float() 函数将其转换为浮点数。
如果⼀个⽂本⽂件包含精确到⼩数点后 1 000 000 位⽽不是 30 位的圆周率值,也可以创建⼀个包含所有这些数字的字符串。⽆须对前⾯的程序做任何修改,只需将这个⽂件传递给它即可。在这⾥, 只打印到⼩数点后 50 位,以免终端花太多时间滚动显⽰全部的 1 000 000 位数字:
from pathlib import Path
path = Path('pi_million_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
pi_string = ''
for line in lines:
pi_string += line.lstrip()
print(f"{pi_string[:52]}...")
print(len(pi_string))
输出表明,创建的字符串确实包含精确到⼩数点后 1 000 000 位的圆周率
值:
3.14159265358979323846264338327950288419716939937510...
1000002
在可处理的数据量⽅⾯,Python 没有任何限制。只要系统的内存⾜够⼤, 你想处理多少数据就可以处理多少数据。
我⼀直想知道⾃⼰的⽣⽇是否包含在圆周率值中。下⾯来扩展刚才编写的 程序,以确定某个⼈的⽣⽇是否包含在圆周率值的前 1 000 000 位中。。为此,可先将⽣⽇表⽰为⼀个由数字组成的字符串,再检查这个字符串是否 在 pi_string 中:
--snip--
for line in lines:
pi_string += line.strip()
birthday = input("Enter your birthday, in the form mmddyy: ")
if birthday in pi_string:
print("Your birthday appears in the first million digits of pi!")
else:
print("Your birthday does not appear in the first million digits of
pi.")
⾸先提⽰⽤户输⼊其⽣⽇,再检查这个字符串是否在 pi_string 中。运 ⾏这个程序:
Enter your birthdate, in the form mmddyy: 120372
Your birthday appears in the first million digits of pi!
我的⽣⽇确实出现在了圆周率值中!读取⽂件的内 容后,就能以任意⽅式
对其进⾏分析了
保存数据的最简单的⽅式之⼀是将其写⼊⽂件。
定义⼀个⽂件的路径后,就可使⽤ write_text() 将数据写⼊该⽂件了。
from pathlib import Path
path = Path('programming.txt')
path.write_text("I love programming.")
write_text() ⽅法接受单个实参,即要写⼊⽂件的字符串。
注意:Python 只能将字符串写⼊⽂本⽂件。如果要将数值数据存储到
⽂本⽂件中,必须先使⽤函数 str() 将其转换为字符串格式。
write_text() ⽅法会在幕后完成⼏项⼯作。⾸先,如果 path 变量对应 的路径指向的⽂件不存在,就创建它。其次,将字符串写⼊⽂件后,它会 确保⽂件得以妥善地关闭。
要将多⾏写⼊⽂件,需要先创建⼀个字符串(其中包含要写⼊⽂件的全部 内容),再调⽤ write_text() 并将这个字符串传递给它。
下⾯将多⾏内 容写⼊⽂件 programming.txt:
from pathlib import Path
contents = "I love programming.\n"
contents += "I love creating new games.\n"
contents += "I also love working with data.\n"
path = Path('programming.txt')
path.write_text(contents)
⾸先定义变量 contents,⽤于存储要写⼊⽂件的所有内容。
注意:在对 path 对象调⽤ write_text() ⽅法时,务必谨慎。如果 指定的⽂件已存在, write_text() 将删除其内容,并将指定的内容写⼊其中。
Python 使⽤称为异常(exception)的特殊对象来管理程序执⾏期间发⽣的 错误。每当发⽣让 Python 不知所措的错误时,它都会创建⼀个异常对象。
异常是使⽤ try-except 代码块处理的。
下⾯来看⼀种导致 Python 引发异常的简单错误。
print(5/0)
Python ⽆法这样做,因此你将看到⼀个 traceback:
Traceback (most recent call last):
File "division_calculator.py", line 1, in
print(5/0)
~^~
❶ ZeroDivisionError: division by zero
在上述 traceback 中,错误 ZeroDivisionError 是个异常对象(⻅ ❶)。Python 在⽆法按你的要求做时,就会创建这种对象。在这种情况 下,Python 将停⽌运⾏程序,并指出引发了哪种异常,⽽我们可根据这些 信息对程序进⾏修改。
try-except
代码块当你认为可能发⽣错误时,可编写⼀个 try-except 代码块来处理可能引 发的异常。
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
如果在错误发⽣时,程序还有⼯作没有完成,妥善地处理错误就显得尤其 重要。
print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
first_number = input("\nFirst number: ")
if first_number == 'q':
break
second_number = input("Second number: ")
if second_number == 'q':
break
answer = int(first_number) / int(second_number)
print(answer)
这个 程序没有采取任何处理错误的措施,因此在执⾏除数为 0 的除法运算时,
它将崩溃:
Give me two numbers, and I'll divide them.
Enter 'q' to quit.
First number: 5
Second number: 0
Traceback (most recent call last):
File "division_calculator.py", line 11, in
answer = int(first_number) / int(second_number)
~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
ZeroDivisionError: division by zero
程序崩溃可不好,让⽤户看到 traceback 也不是个好主意。不懂技术的⽤户 会感到糊涂,怀有恶意的⽤户还能通过 traceback 获悉你不想让他们知道的 信息。
通过将可能引发错误的代码放在 try-except 代码块中,可提⾼程序抵御 错误的能⼒。这个⽰例还包含⼀个 else 代码块,只有 try 代码块成功执⾏才需要继续执⾏的代码,都应放到 else 代码块中:
--snip--
while True:
--snip--
if second_number == 'q':
break
❶try:
answer = int(first_number) / int(second_number)
❷except ZeroDivisionError:
print("You can't divide by 0!")
❸else:
print(answer)
在使⽤⽂件时,⼀种常⻅的问题是找不到⽂件:要查找的⽂件可能在其他 地⽅,⽂件名可能不正确,或者这个⽂件根本就不存在。
我们来尝试读取⼀个不存在的⽂件。下⾯的程序尝试读取⽂件 alice.txt 的内
容,但这个⽂件并没有被存储在 alice.py 所在的⽬录中:
from pathlib import Path
path = Path('alice.txt')
contents = path.read_text(encoding='utf-8')
请注意,这⾥使⽤ read_text() 的⽅式与前⾯稍有不同。如果系统的默 认编码与要读取的⽂件的编码不⼀致,参数 encoding 必不可少。如果要 读取的⽂件不是在你的系统中创建的,这种情况更容易发⽣。
Python ⽆法读取不存在的⽂件,因此引发了⼀个异常:
Traceback (most recent call last):
❶ File "alice.py", line 4, in
❷ contents = path.read_text(encoding='utf-8')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/.../pathlib.py", line 1056, in read_text
with self.open(mode='r', encoding=encoding, errors=errors) as f:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/.../pathlib.py", line 1042, in open
return io.open(self, mode, buffering, encoding, errors, newline) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
❸ FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'
这⾥的 traceback ⽐前⾯的那些都⻓,因此下⾯介绍如何看懂复杂的 traceback。
通常最好从 traceback 的末尾着⼿。
从最后⼀⾏可知,引发了异 常 FileNotFoundError(⻅❸)。这⼀点很重要,它让我们知道应该在 要编写的 except 代码块中使⽤哪种异常。
回头看看 traceback 开头附近(⻅❶),从这⾥可知,错误发⽣在⽂件 alice.py 的第四⾏。接下来的⼀⾏列出了导致错误的代码⾏(⻅❷)。 traceback 的其余部分列出了⼀些代码,它们来⾃打开和读取⽂件涉及的 库。通常,不需要详细阅读和理解 traceback 中的这些内容。
为了处理这个异常,应将 traceback 指出的存在问题的代码⾏放到 try 代码
块中。这⾥,存在问题的是包含 read_text() 的代码⾏:
from pathlib import Path
path = Path('alice.txt')
try:
contents = path.read_text(encoding='utf-8')
❶ except FileNotFoundError:
print(f"Sorry, the file {path} does not exist.")
这样,当找不 到⽂件时,Python 将运⾏ except 代码块中的代码,从⽽显⽰⼀条友好的
错误消息,⽽不是 traceback:
Sorry, the file alice.txt does not exist.
你可以分析包含整本书的⽂本⽂件。
下⾯来提取童话 Alice in Wonderland(《爱丽丝漫游奇境记》)的⽂本,并 尝试计算它包含多少个单词。我们将使⽤ split() ⽅法,它默认以空⽩为 分隔符将字符串分拆成多个部分:
from pathlib import Path
path = Path('alice.txt')
try:
contents = path.read_text(encoding='utf-8')
except FileNotFoundError:
print(f"Sorry, the file {path} does not exist.")
else:
#计算⽂件⼤致包含多少个单词
❶ words = contents.split()
❷ num_words = len(words)
print(f"The file {path} has about {num_words} words.")
我将⽂件 alice.txt 移到了正确的⽬录中,让 try 代码块能够成功地执⾏。 对变量 contents(它现在是⼀个⻓⻓的字符串,包含童话 Alice in Wonderland 的全部⽂本)调⽤ split() ⽅法,⽣成⼀个列表,其中包含这 部童话中的所有单词(⻅❶)。
下⾯多分析⼏本书。先将这个程序的⼤部分代码移到⼀个名为 count_words() 的函数中,这样对多本书进⾏分析会更容易:
from pathlib import Path
def count_words(path):
❶ """计算⼀个⽂件⼤致包含多少个单词"""
try:
contents = path.read_text(encoding='utf-8')
except FileNotFoundError:
print(f"Sorry, the file {path} does not exist.")
else:
# 计算⽂件⼤致包含多少个单词
words = contents.split()
num_words = len(words)
print(f"The file {path} has about {num_words} words.")
path = Path('alice.txt')
count_words(path)
这些代码⼤多与原来⼀样,只是被移到了函数 count_words() 中,并且 增加了缩进量。
为此,我们把要分析的⽂件的名称存储在⼀个列表中,然后对列表中 的每个⽂件都调⽤ count_words()。
from pathlib import Path
def count_words(filename):
--snip--
filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt',
'little_women.txt']
for filename in filenames:
❶ path = Path(filename)
count_words(path)
我们告诉⽤户有⼀个⽂件找不到。但并⾮每次捕获异常 都需要告诉⽤户,你有时候希望程序在发⽣异常时保持静默,就像什么都 没有发⽣⼀样继续运⾏。要让程序静默失败,可像通常那样编写 try 代码 块,但在 except 代码块中明确地告诉 Python 什么都不要做。Python 有⼀ 个 pass 语句,可在代码块中使⽤它来让 Python 什么都不做:
def count_words(path):
"""计算⼀个⽂件⼤致包含多少个单词"""
try:
--snip--
except FileNotFoundError:
pass
else:
--snip--
现在,当出现 FileNotFoundError 异常时,虽然仍将执⾏except 代码块中的代码,但什么都不会发⽣。
如果⽤ 户知道要分析哪些⽂件,他们可能希望在有⽂件未被分析时出现⼀条消息
来告知原因。
如果⽤户只想看到结果,并不知道要分析哪些⽂件,可能就 ⽆须在有些⽂件不存在时告知他们。
可以使⽤⽅法 count() 来确定特定的单词或短语在字符串中出现了多
少次。例如,下⾯的代码计算 'row' 在⼀个字符串中出现了多少次:
>>> line = "Row, row, row your boat"
>>> line.count('row')
2
>>> line.lower().count('row')
3
请注意,通过使⽤ lower() 将字符串转换为全⼩写的,可捕捉要查找 的单词的各种格式,⽽不管其⼤⼩写如何。
很多程序要求⽤户输⼊某种信息,⽐如让⽤户存储游戏⾸选项或提供要可 视化的数据。当⽤户关闭程序时,⼏乎总是要保存他们提供的信 息。⼀种简单的⽅式是使⽤模块 json 来存储数据。
模块 json 让你能够将简单的 Python 数据结构转换为 JSON 格式的字符 串,并在程序再次运⾏时从⽂件中加载数据。你还可以使⽤ json 在 Python 程序之间共享数据。
注意:JSON(JavaScript Object Notation)格式最初是为 JavaScript 开发 的,但随后成了⼀种通⽤的格式,被包括 Python 在内的众多语⾔采 ⽤。
下⾯先编写⼀个存储⼀组数的简短程序,再编写⼀个将这些数读取到内存 中的程序。第⼀个程序将使⽤ json.dumps() 来存储这组数,⽽第⼆个程 序将使⽤ json.loads() 来读取它们。
json.dumps() 函数接受⼀个实参,即要转换为 JSON 格式的数据。这个 函数返回⼀个字符串,这样你就可将其写⼊数据⽂件了:
from pathlib import Path
import json
numbers = [2, 3, 5, 7, 11, 13]
❶ path = Path('numbers.json')
❷ contents = json.dumps(numbers)
path.write_text(contents)
下⾯再编写⼀个程序,使⽤ json.loads() 将这个列表读取到内存中:
from pathlib import Path
import json
❶ path = Path('numbers.json')
❷ contents = path.read_text()
❸ numbers = json.loads(contents)
print(numbers)
在❶处,确保读取的是前⾯写⼊的⽂件。这个数据⽂件是使⽤特殊格式的 ⽂本⽂件,因此可使⽤ read_text() ⽅法来读取它(⻅❷)。然后将这 个⽂件的内容传递给 json.loads()(⻅❸)。这个函数将⼀个 JSON 格 式的字符串作为参数,并返回⼀个 Python 对象(这⾥是⼀个列表),⽽我 们将这个对象赋给了变量 numbers。最后,打印恢复的数值列表,看看是 否与 number_writer.py 中创建的数值列表相同:
[2, 3, 5, 7, 11, 13]
这是⼀种在程序之间共享数据的简单⽅式。
使⽤ json 保存⽤户⽣成的数据很有必要,因为如果不以某种⽅式进⾏存 储,⽤户的信息就会在程序停⽌运⾏时丢失。
下⾯来看⼀个这样的例⼦:
提⽰⽤户在⾸次运⾏程序时输⼊⾃⼰的名字,并且在他再次运⾏程序时仍
然记得他。
先来存储⽤户的名字:
from pathlib import Path
import json
❶ username = input("What is your name? ")
❷ path = Path('username.json')
contents = json.dumps(username)
path.write_text(contents)
❸ print(f"We'll remember you when you come back, {username}!")
⾸先,提⽰⽤户输⼊名字(⻅❶)。接下来,将收集到的数据写⼊⽂件 username.json(⻅❷)。然后,打印⼀条消息,指出存储了⽤户输⼊的信息 (⻅❸):
What is your name? Eric
We'll remember you when you come back, Eric!
现在再编写⼀个程序,向名字已被存储的⽤户发出问候:
greet_user.py
from pathlib import Path
import json
❶ path = Path('username.json')
contents = path.read_text()
❷ username = json.loads(contents)
print(f"Welcome back, {username}!")
我们读取数据⽂件的内容(⻅❶),并使⽤json.loads() 将恢复的数据 赋给变量 username(⻅❷)。有了已恢复的⽤户名,就可以使⽤个性化 的问候语欢迎⽤户回来了:
Welcome back, Eric!
需要将这两个程序合并到⼀个程序(remember_me.py)中。在这个程序运 ⾏时,将尝试从内存中获取⽤户的⽤户名。如果没有找到,就提⽰⽤户输 ⼊⽤户名,并将其存储到⽂件 username.json 中,以供下次使⽤。
from pathlib import Path
import json
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}!")
你经常会遇到这样的情况:虽然代码能够正确地运⾏,但还可以将其划分 为⼀系列完成具体⼯作的函数来进⾏改进。这样的过程称为重构。
要重构 remember_me.py,可将其⼤部分逻辑放到⼀个或多个函数中。
remember_me.py 的重点是问候⽤户,因此将其所有代码都放到⼀个名为
greet_user() 的函数中:
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()
下⾯重构 greet_user(),不让它执⾏这么多任务。⾸先将获取已存储⽤
户名的代码移到另⼀个函数中:
from pathlib import Path
import json
def get_stored_username(path):
❶"""如果存储了⽤户名,就获取它"""
if path.exists():
contents = path.read_text()
username = json.loads(contents)
return username
else:
❷return None
def 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()
新增的 get_stored_username() 函数⽬标明确,⽂档字符串(⻅❶) 指出了这⼀点。
还需要将 greet_user() 中的另⼀个代码块提取出来,将在没有存储⽤户名时提⽰⽤户输⼊的代码放在⼀个独⽴的函数中:
from pathlib import Path
import json
def get_stored_username(path):
"""如果存储了⽤户名,就获取它"""
--snip--
def get_new_username(path):
"""提⽰⽤户输⼊⽤户名"""
username = input("What is your name? ")
contents = json.dumps(username)
path.write_text(contents)
return username
def 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()
在 remember_me.py 的这个最终版本中,每个函数都执⾏单⼀⽽清晰的任 务。我们调⽤ greet_user(),它打印⼀条合适的消息:要么欢迎⽼⽤户 回来,要么问候新⽤户。