备注:以下按照Python 2的规则编写代码。
在jupyter notebook中运行直接显示结果。
10 文件和异常
10.1 文件中读取数据
10.1.1 读取整个文件
"""file_reader.py"""
with open(‘chapter_10/pi_digits.txt‘) as file_object: # 关键字with在不再需要访问文件后将其关闭
contents = file_object.read()
print(contents)
3.1415926535
8979323846
2643383279
10.1.2
"""file_reader.py"""
with open(‘chapter_10/pi_digits.txt‘) as file_object: # 关键字with在不再需要访问文件后将其关闭
contents = file_object.read()
print(contents.rstrip()) # 使用rstrip()删除文件末尾多余的空行
3.1415926535
8979323846
2643383279
10.1.3 逐行读取
"""file_reader.py"""
filename = ‘chapter_10/pi_digits.txt‘
with open(filename) as f:
for line in f:
print line
with open(filename) as f:
for line in f:
print line.rstrip()
3.1415926535
8979323846
2643383279
3.1415926535
8979323846
2643383279
10.1.4 创建包含文件各行内容的列表
filename = ‘chapter_10/pi_digits.txt‘
with open(filename) as f:
lines = f.readlines()
print lines
for line in lines:
print line.rstrip()
[‘3.1415926535\n‘, ‘ 8979323846\n‘, ‘ 2643383279\n‘]
3.1415926535
8979323846
2643383279
10.1.5 使用文件中的内容
"""pi_string.py"""
filename = ‘chapter_10/pi_digits.txt‘
with open(filename) as f:
lines = f.readlines()
pi_string = ‘‘
for line in lines:
pi_string += line.rstrip()
print pi_string
print len(pi_string)
3.1415926535 8979323846 2643383279
36
"""pi_string.py"""
filename = ‘chapter_10/pi_digits.txt‘
with open(filename) as f:
lines = f.readlines()
pi_string = ‘‘
for line in lines:
pi_string += line.strip() # 删除前后的空格
print pi_string
print len(pi_string)
3.141592653589793238462643383279
32
注意:
读取文本文件时,Python将其中的所有文本都解读为字符串。如果读取的是数字,并要将其作为数值使用,就必须使用函数int()将其转换为整数,或使用函数float()将其转换为浮点数。
10.1.6 包含一百万位的大型文件
"""pi_string.py"""
filename = ‘chapter_10/pi_million_digits.txt‘
with open(filename) as f:
lines = f.readlines()
pi_string = ‘‘
for line in lines:
pi_string += line.strip()
print pi_string[:52] + "..." # 以打印小数点后50位为例
print len(pi_string)
3.14159265358979323846264338327950288419716939937510...
1000002
10.1.7 圆周率值中包含你的生日吗
"""任务:确定某个生日是否包含在圆周率值的前1 000 000位中。"""
filename = ‘chapter_10/pi_million_digits.txt‘
with open(filename) as f:
lines = f.readlines()
pi_string = ‘‘
for line in lines:
pi_string += line.rstrip()
birthday = raw_input("Enter your birthday, in the form mmddyy: ")
if birthday in pi_string:
print "Your birthday aperars in the first million digits of pi!"
else:
print "Your birthday doesn‘t appear in the first million digits of pi."
Enter your birthday, in the form mmddyy: 120372
Your birthday aperars in the first million digits of pi!
习题: 10-1 Python学习笔记
"""
在文本编辑器中新建一个文件,写几句话来总结一下你至此学到的 Python 知识,其中每一行都以“In Python you can”打头。
将这文件命名为learning_python.txt,并将其存储到为完成本章练习而编写的程序所在的目录中。编写一个程序,它读取这个
文件,并将你所写的内容打印三次:第一次打印时读取整个文件;第二次打印时遍历文件对象;第三次打印时将各行存储在一个列
表中,再在 with 代码块外打印它们。
"""
with open(‘chapter_10/learning_python.txt‘) as lp:
contents = lp.read()
print "1>>>"
print contents
print "\n2>>>"
for line in contents:
print line.strip()
print "\n3>>>"
with open(‘chapter_10/learning_python.txt‘) as lp:
lines = lp.readlines()
for line in lines:
print line.rstrip()
1>>>
In Python you can do whatever you want!
In Python you can read different tpyes of files.
In Python you can draw something.
In Python you can program interesting games.
2>>>
I
n
P
y
t
h
o
n
y
o
u
c
a
n
d
o
w
h
a
t
e
v
e
r
y
o
u
w
a
n
t
!
I
n
P
y
t
h
o
n
y
o
u
c
a
n
r
e
a
d
d
i
f
f
e
r
e
n
t
t
p
y
e
s
o
f
f
i
l
e
s
.
I
n
P
y
t
h
o
n
y
o
u
c
a
n
d
r
a
w
s
o
m
e
t
h
i
n
g
.
I
n
P
y
t
h
o
n
y
o
u
c
a
n
p
r
o
g
r
a
m
i
n
t
e
r
e
s
t
i
n
g
g
a
m
e
s
.
3>>>
In Python you can do whatever you want!
In Python you can read different tpyes of files.
In Python you can draw something.
In Python you can program interesting games.
练习:10-2 C语言学习笔记
"""
读取你刚创建的文件 learning_python.txt 中的每一行,使用方法replace()将其中的 Python 都替换为另一门语言的名称,
如 C。将修改后的各行都打印到屏幕上。
"""
message = "I really like dogs."
message.replace(‘dog‘, ‘cat‘)
with open(‘chapter_10/learning_python.txt‘) as f:
contents = f.read()
print contents
print "\nChange ‘Python‘ to ‘C‘."
print contents.replace("Python", "C") # replace()替换字符串中特定单词
In Python you can do whatever you want!
In Python you can read different tpyes of files.
In Python you can draw something.
In Python you can program interesting games.
Change ‘Python‘ to ‘C‘.
In C you can do whatever you want!
In C you can read different tpyes of files.
In C you can draw something.
In C you can program interesting games.
10.2 写入文件
10.2.1 写入空文件
"""write_message.py"""
filename = ‘chapter_10/programming1.txt‘
with open(filename, ‘w‘) as f:
f.write("I love programming.")
# 验证
with open(filename) as f:
print "The following are in the file:"
print f.read()
The following are in the file:
I love programming.
打开文件时,可指定读取模式(‘r‘)、写入模式(‘w‘)、附加模式(‘a‘)或让你能够读取和写入文件的模式(‘r+‘)。如果省略了模式实参,Python将以默认的只读模式打开文件。
如果要写入的文件不存在,函数open()将自动创建它。然而,以写入(‘w‘)模式打开文件时千万要小心,因为如果指定的文件已经存在,Python将在返回文件对象前清空该文件。
注意:
Python只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数str()将其转换为字符串格式。
10.2.2 写入多行
filename = ‘chapter_10/programming2.txt‘
with open(filename, ‘w‘) as f:
f.write("I love programming.\n") # 插入换行符,否则这两句会连在一起
f.write("I love creating new games.\n")
# 验证
with open(filename) as f:
print "The following are in the file:"
print f.read()
The following are in the file:
I love programming.
I love creating new games.
10.2.3 附加到文件
"""
如果要给文件添加内容,而不是覆盖原有的内容,可以附加模式打开文件,写入到文件的行都将添加到文件末尾。
如果指定的文件不存在,Python将创建一个空文件。
"""
filename = ‘chapter_10/programming3.txt‘
with open(filename, ‘a‘) as f:
f.write("I also love finding meaning in large dataset.\n")
f.write("I love creating apps that can run in browser.\n")
# 验证
with open(filename) as f:
print "The following are in the file:"
print f.read()
The following are in the file:
I love programming.
I love creating new games.
I also love finding meaning in large dataset.
I love creating apps that can run in browser.
习题:10-3 访客
"""
编写一个程序,提示用户输入其名字;用户作出响应后,将其名字写入到文件 guest.txt 中。
"""
filename = "chapter_10/guest.txt"
name = raw_input("Hello!\nWhat‘s your name?\n")
with open(filename, "w") as f:
f.write(name)
# 验证
with open(filename) as f:
print "The following are in the file:"
print f.read()
Hello!
What‘s your name?
Dan
The following are in the file:
Dan
习题:10-4 访客名单
"""
编写一个 while 循环,提示用户输入其名字。用户输入其名字后,在屏幕上打印一句问候语,并将一条访问记录添加到文件 guest_book.txt 中。确保这个文件中的每条记录都独占一行。
"""
filename = "chapter_10/guest_book.txt"
name = ""
with open(filename, "a") as f:
while name != "quit":
print "Enter ‘quit‘ to quit this program."
name = raw_input("Please write down your name: ")
if name == "quit":
break
line = "Hello! " + name + "\n"
f.write(line)
print line
Enter ‘quit‘ to quit this program.
Please write down your name: Ann
Hello! Ann
Enter ‘quit‘ to quit this program.
Please write down your name: Bob
Hello! Bob
Enter ‘quit‘ to quit this program.
Please write down your name: Candy
Hello! Candy
Enter ‘quit‘ to quit this program.
Please write down your name: Denny
Hello! Denny
Enter ‘quit‘ to quit this program.
Please write down your name: quit
10.3 异常
10.3.1 ZeroDivisionError 异常
"""division.py"""
print 5/0
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
in ()
2 """division.py"""
3
----> 4 print 5/0
ZeroDivisionError: integer division or modulo by zero
在上述traceback中,错误ZeroDivisionError是一个异常对象。Python无法按你的要求做时,就会创建这种对象。在这种情况下,Python将停止运行程序,并指出引发了哪种异常,而我们可根据这些信息对程序进行修改。
下面我们将告诉Python,发生这种错误时怎么办;这样,如果再次发生这样的错误,我们就有备无患了。
10.3.2 使用try-except代码块
"""处理ZeroDivisionError异常的try-except代码块类似于下面这样: """
try:
print 5/0
except ZeroDivisionError:
print "You can‘t divide by zero!"
You can‘t divide by zero!
在这个示例中,try代码块中的代码引发了ZeroDivisionError异常,因此Python指出了该如何解决问题的except代码块,并运行其中的代码。这样,用户看到的是一条友好的错误消息,而不是traceback。
10.3.3 使用异常避免崩溃
"""
发生错误时,如果程序还有工作没有完成,妥善地处理错误就尤其重要。这种情况经常会出现在要求用户提供输入的程序中;
如果程序能够妥善地处理无效输入,就能再提示用户提供有效输入,而不至于崩溃。
下面来创建一个只执行除法运算的简单计算器:
"""
"""division.py"""
print "Give me two numbers, and I‘ll divide them."
print "Enter ‘q‘ to quit."
while True:
first_number = raw_input("\nFirst number: ")
if first_number == ‘q‘:
break
second_number = raw_input("Second number: ")
if second_number == ‘q‘:
break
answer = float(first_number) / float(second_number)
print(answer)
Give me two numbers, and I‘ll divide them.
Enter ‘q‘ to quit.
First number: 5
Second number: 0
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
in ()
17 if second_number == ‘q‘:
18 break
---> 19 answer = float(first_number) / float(second_number)
20 print(answer)
ZeroDivisionError: float division by zero
程序崩溃可不好,但让用户看到traceback也不是好主意。不懂技术的用户会被它们搞糊涂,而且如果用户怀有恶意,他会通过traceback获悉你不希望他知道的信息。例如,他将知道你的程序文件的名称,还将看到部分不能正确运行的代码。有时候,训练有素的攻击者可根据这些信息判断出可对你的代码发起什么样的攻击。
10.3.4 else 代码块
"""
通过将可能引发错误的代码放在try-except代码块中,可提高这个程序抵御错误的能力。错误是执行除法运算的代码行导致的,
因此我们需要将它放到try-except代码块中。这个示例还包含一个else代码块;依赖于try代码块成功执行的代码都应放到
else代码块中:
"""
print "Give me two numbers, and I‘ll divide them."
print "Enter ‘q‘ to quit."
while True:
first_number = raw_input("\nFirst number: ")
if first_number == ‘q‘:
break
second_number = raw_input("Second number: ")
#if second_number == ‘q‘:
#break
try:
answer = float(first_number) / float(second_number)
except ZeroDivisionError:
print "You can‘t divide by 0!"
else:
print(answer)
Give me two numbers, and I‘ll divide them.
Enter ‘q‘ to quit.
First number: 5
Second number: 0
You can‘t divide by 0!
First number: 5
Second number: 2
2.5
First number: q
try-except-else代码块的工作原理大致如下:Python尝试执行try代码块中的代码;except代码块告诉Python,如果它尝试运行try代码块中的代码时引发了指定的异常,该怎么办。
通过预测可能发生错误的代码,可编写健壮的程序,它们即便面临无效数据或缺少资源,也能继续运行,从而能够抵御无意的用户错误和恶意的攻击。
10.3.5 处理 FileNotFoundError 异常
"""
使用文件时,一种常见的问题是找不到文件:你要查找的文件可能在其他地方、文件名可能不正确或者这个文件根本就不存在。
对于所有这些情形,都可使用try-except代码块以直观的方式进行处理。
我们来尝试读取一个不存在的文件。下面的程序尝试读取文件alice.txt的内容,但我没有将这个文件存储在alice.py
所在的目录中:
"""
"""alice.py"""
filename = ‘alice.txt‘
with open(filename) as f_obj:
contents = f_obj.read()
---------------------------------------------------------------------------
IOError Traceback (most recent call last)
in ()
10 filename = ‘alice.txt‘
11
---> 12 with open(filename) as f_obj:
13 contents = f_obj.read()
IOError: [Errno 2] No such file or directory: ‘alice.txt‘
在这个示例中,这个错误是函数open()导致的,因此要处理这个错误,必须将try语句放在包含open()的代码行之前:
filename = ‘alice.txt‘
try:
with open(filename) as f_obj:
contents = f_obj.read()
except IOError:
msg = "Sorry, the file ‘" + filename + "‘ does not exist."
print msg
Sorry, the file ‘alice.txt‘ does not exist.
10.3.6 分析文本
"""
下面来提取童话Alice in Wonderland的文本,并尝试计算它包含多少个单词。我们将使用方法split(),
它根据一个字符串创建一个单词列表。
"""
filename = ‘chapter_10/alice.txt‘
try:
with open(filename) as f:
contents = f.read()
except IOError:
msg = "Sorry, the file ‘" + filename + "‘ does not exist."
print msg
else:
# 计算文件大致包含多少个单词
words = contents.split()
num_words = len(words)
print "The file ‘" + filename + "‘ has about " + str(num_words) + " words."
The file ‘chapter_10/alice.txt‘ has about 29461 words.
10.3.7 使用多个文件
"""
下面多分析几本书。这样做之前,我们先将这个程序的大部分代码移到一个名为count_words()的函数中,
这样对多本书进行分析时将更容易:
"""
"""word_count.py """
def count_words(filename):
"""计算一个文件大致包含多少个单词"""
try:
with open(filename) as f_obj:
contents = f_obj.read()
except IOError:
msg = "Sorry, the file ‘" + filename + "‘ does not exist."
print(msg)
else:
# 计算文件大致包含多少个单词
words = contents.split()
num_words = len(words)
print "The file ‘" + filename + "‘ has about " + str(num_words) + " words."
filenames = [‘chapter_10/alice.txt‘, ‘chapter_10/siddhartha.txt‘, ‘chapter_10/moby_dick.txt‘, ‘chapter_10/little_women.txt‘]
for filename in filenames:
count_words(filename)
The file ‘chapter_10/alice.txt‘ has about 29461 words.
Sorry, the file ‘chapter_10/siddhartha.txt‘ does not exist.
The file ‘chapter_10/moby_dick.txt‘ has about 215136 words.
The file ‘chapter_10/little_women.txt‘ has about 189079 words.
文件siddhartha.txt不存在,但这丝毫不影响这个程序处理其他文件。
在这个示例中,使用try-except代码块提供了两个重要的优点:
避免让用户看到traceback;
让程序能够继续分析能够找到的其他文件。
如果不捕获因找不到siddhartha.txt而引发IOError异常,用户将看到完整的traceback,而程序将在尝试分析Siddhartha后停止运行——根本不分析Moby Dick和Little Women。
10.3.8 失败时一声不吭
"""
在前一个示例中,我们告诉用户有一个文件找不到。但并非每次捕获到异常时都需要告诉用户,有时候你希望程序在发生异常时
一声不吭,就像什么都没有发生一样继续运行。要让程序在失败时一声不吭,可像通常那样编写try代码块,但在except代码块
中明确地告诉Python什么都不要做。Python有一个pass语句,可在代码块中使用它来让Python什么都不要做:
"""
def count_words(filename):
"""计算一个文件大致包含多少个单词"""
try:
with open(filename) as f_obj:
contents = f_obj.read()
except IOError:
pass
else:
# 计算文件大致包含多少个单词
words = contents.split()
num_words = len(words)
print "The file ‘" + filename + "‘ has about " + str(num_words) + " words."
filenames = [‘chapter_10/alice.txt‘, ‘chapter_10/siddhartha.txt‘, ‘chapter_10/moby_dick.txt‘, ‘chapter_10/little_women.txt‘]
for filename in filenames:
count_words(filename)
The file ‘chapter_10/alice.txt‘ has about 29461 words.
The file ‘chapter_10/moby_dick.txt‘ has about 215136 words.
The file ‘chapter_10/little_women.txt‘ has about 189079 words.
pass语句还充当了占位符,它提醒你在程序的某个地方什么都没有做,并且以后也许要在这里做些什么。
例如,在这个程序中,我们可能决定将找不到的文件的名称写入到文件missing_files.txt中。用户看不到这个文件,但我们可以读取这个文件,进而处理所有文件找不到的问题。
"""
10-6 加法运算:提示用户提供数值输入时,常出现的一个问题是,用户提供的是文本而不是数字。在这种情况下,当你尝试
将输入转换为整数时,将引发 TypeError 异常。编写一个程序,提示用户输入两个数字,再将它们相加并打印结果。在用户
输入的任何一个值不是数字时都捕获 TypeError 异常,并打印一条友好的错误消息。对你编写的程序进行测试:先输入两个
数字,再输入一些文本而不是数字。
"""
print "Give me two numbers, and I‘ll add them."
num_1 = raw_input("First number: ")
num_2 = raw_input("Second number: ")
try:
sum = float(num_1) + float(num_2)
except ValueError:
print "\nYou didn‘t give me two NUMBERS!"
else:
print "\nThe answer is: " + str(sum)
Give me two numbers, and I‘ll add them.
First number: 1.2
Second number: 4.3
The answer is: 5.5
"""
10-7 加法计算器:将你为完成练习 10-6 而编写的代码放在一个 while 循环中,让用户犯错(输入的是文本而不是数字)
后能够继续输入数字。
"""
print "Give me two numbers, and I‘ll add them."
print "Enter ‘q‘ to quit."
check = True
while check:
num_1 = raw_input("\nFirst number: ")
if num_1 == "q":
break
num_2 = raw_input("Second number: ")
if num_2 == "q":
break
try:
sum = float(num_1) + float(num_2)
except ValueError:
print "\nYou didn‘t give me two NUMBERS!"
continue
else:
print "\nThe answer is: " + str(sum)
check = False
Give me two numbers, and I‘ll add them.
Enter ‘q‘ to quit.
First number: d
Second number: g
You didn‘t give me two NUMBERS!
First number: 3
Second number: o
You didn‘t give me two NUMBERS!
First number: 7
Second number: 6
The answer is: 13.0
"""
10-8 猫和狗:
创建两个文件 cats.txt 和 dogs.txt,在第一个文件中至少存储三只猫的名字,在第二个文件中至少存储三条狗的名字。
编写一个程序,尝试读取这些文件,并将其内容打印到屏幕上。将这些代码放在一个 try-except 代码块中,以便在文件不
存在时捕获 FileNotFound 错误,并打印一条友好的消息。将其中一个文件移到另一个地方,并确认 except 代码块中的
代码将正确地执行。
"""
catsname = "chapter_10/cats.txt"
dogsname = "chapter_10/dogs.txt"
def readfile(filename):
try:
with open(filename) as f:
print filename.upper()
print f.read()
except IOError:
print "The ‘" + filename + "‘ does not exist!"
readfile(catsname)
readfile(dogsname)
The ‘chapter_10/cats.txt‘ does not exist!
The ‘chapter_10/dogs.txt‘ does not exist!
"""
10-9 沉默的猫和狗:修改你在练习 10-8 中编写的 except 代码块,让程序在文件不存在时一言不发。
"""
catsname = "chapter_10/cats.txt"
dogsname = "chapter_10/dogs.txt"
def readfile(filename):
try:
with open(filename) as f:
print filename.upper()
print f.read()
except IOError:
pass
print "Complete."
readfile(catsname)
readfile(dogsname)
Complete.
Complete.
10.4 存储数据
模块json让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。你还可以使用json在Python程序之间分享数据。
更重要的是,JSON数据格式并非Python专用的,这让你能够将以JSON格式存储的数据与使用其他编程语言的人分享。这是一种轻便格式,很有用,也易于学习。
10.4.1 使用 json.dump()和 json.load()
函数json.dump()接受两个实参:要存储的数据以及可用于存储数据的文件对象。
"""number_write.py"""
import json # 导入json模块
numbers = [2, 3, 5, 7, 11, 13]
filename = ‘chapter_10/numbers.json‘
with open(filename, ‘w‘) as f:
json.dump(numbers, f)
"""number_reader.py"""
filename = ‘chapter_10/numbers.json‘
with open(filename) as f:
numbers = json.load(f)
print numbers
[2, 3, 5, 7, 11, 13]
10.4.2 保存和读取用户生成的数据
"""存储用户的名字"""
"""remember_me.py"""
import json
username = raw_input("What‘s your name? ")
filename = "chapter_10/username.json"
with open(filename, "w") as f:
json.dump(username, f)
print "We‘ll remember you when you come back, " + username + "!"
What‘s your name? Eric
We‘ll remember you when you come back, Eric!
"""再编写一个程序,向其名字被存储的用户发出问候: """
"""greet_user.py """
import json
filename = "chapter_10/username.json"
with open(filename) as f:
username = json.load(f)
print "Welcome back, " + username + "!"
Welcome back, Eric!
"""合并两个文件"""
"""remember_me.py"""
import json
# 如果以前存储了用户名,就加载它
# 否则,就提示用户输入用户名并存储它
filename = "chapter_10/username.json"
try:
with open(filename) as f:
username = json.load(f)
except IOError:
username = raw_input("What‘s your name? ")
with open(filename, "w") as f:
json.dump(username, f)
print "We‘ll remember you when you come back, " + username + "!"
else:
print "Welcome back, " + username + "!"
Welcome back, Eric!
10.4.3 重构
经常会遇到这样的情况:代码能够正确地运行,但可做进一步的改进——将代码划分为一系列完成具体工作的函数。这样的过程
被称为重构。重构让代码更清晰、更易于理解、更容易扩展。
要重构remember_me.py,可将其大部分逻辑放到一个或多个函数中。remember_me.py的重点是问候用户,因此我们将其所
有代码都放到一个名为greet_user()的函数中:
"""remrember_me.py"""
def greet_user():
"""问候用户,并指出其名字"""
filename = "chapter_10/username.json"
try:
with open(filename) as f:
username = json.load(f)
except IOError:
username = raw_input("What‘s your name? ")
with open(filename, "w") as f:
json.dump(username, f)
print "We‘ll remember you when you come back, " + username + "!"
else:
print "Welcome back, " + username + "!"
greet_user()
Welcome back, Eric!
"""
重构greet_user(),让它不执行这么多任务。先将获取存储的用户名的代码移到另一个函数中
"""
import json
def get_stored_username():
"""如果存储了用户名,就获取它"""
filename = "chapter_10/username.json"
try:
with open(filename) as f:
username = json.load(f)
except IOError:
return None
else:
return username
def greet_user():
username = get_stored_username()
if username:
print "Welcome back, " + username + "!"
else:
username = raw_input("What‘s your name? ")
with open(filename, "w") as f:
json.dump(username, f)
print "We‘ll remember you when you come back, " + username + "!"
greet_user()
Welcome back, Eric!
"""
将greet_user()中的另一个代码块提取出来:将没有存储用户名时提示用户输入的代码放在一个独立的函数中:
"""
import json
def get_stored_username():
try:
with open(filename) as f:
username = json.load(f)
except IOError:
return None
else:
return username
def get_new_username():
"""提示用户输入用户名"""
username = raw_input("What is your name? ")
filename = "chapter_10/username.json"
with open(filename, ‘w‘) as f:
json.dump(username, f)
return username
def greet_user():
username = get_stored_username()
if username:
print "Welcome back, " + username + "!"
else:
username = get_new_username()
print "We‘ll remember you when you come back, " + username + "!"
greet_user()
Welcome back, Eric!
在remember_me.py的这个最终版本中,每个函数都执行单一而清晰的任务。
我们调用greet_user(),它打印一条合适的消息:要么欢迎老用户回来,要么问候新用户。
为此,它首先调用get_stored_username(),这个函数只负责获取存储的用户名(如果存储了的话),再在必要时调用get_new_username(),这个函数只负责获取并存储新用户的用户名。要编写出清晰而易于维护和扩展的代码,这种划分工作必不可少。
10-11 喜欢的数字: 编写一个程序,提示用户输入他喜欢的数字,并使用json.dump()将这个数字存储到文件中。
再编写一个程序,从文件中读取这个值,并打印消息“I know your favorite number! It’s _____.”。
import json
filename = "chapter_10/favorite_number.json"
num = raw_input("What‘s your favorite number? ")
with open(filename, "w") as f:
json.dump(num, f)
with open(filename) as f:
answer = json.load(f)
print "I know your favorite number! It‘s " + str(answer) + "."
What‘s your favorite number? 20
I know your favorite number! It‘s 20.
10-12 记住喜欢的数字:将练习 10-11 中的两个程序合而为一。如果存储了用户喜欢的数字,就向用户显示它,
否则提示用户输入他喜欢的数字并将其存储到文件中。运行这个程序两次,看看它是否像预期的那样工作。
import json
filename = "chapter_10/favorite_number.json"
with open(filename) as f:
try:
answer = json.load(f)
print "I know your favorite number! It‘s " + str(answer) + "."
except ValueError:
print "Oh, I don‘t know your favorite number."
num = raw_input("So what‘s your favorite number? ")
with open(filename, "w") as f:
json.dump(num, f)
I know your favorite number! It‘s 20.
10-13 验证用户:最后一个 remember_me.py 版本假设用户要么已输入其用户名,要么是首次运行该程序。我们应修改
这个程序,以应对这样的情形:当前和最后一次运行该程序的用户并非同一个人。
为此,在 greet_user()中打印欢迎用户回来的消息前,先询问他用户名是否是对的。
如果不对,就调用 get_new_username()让用户输入正确的用户名。
import json
def get_stored_username():
filename = "chapter_10/username.json"
try:
with open(filename) as f:
username = json.load(f)
except IOError:
return None
else:
return username
def get_new_username():
"""提示用户输入用户名"""
username = raw_input("What is your name ? ")
filename = "chapter_10/username.json"
with open(filename, ‘w‘) as f:
json.dump(username, f)
return username
def greet_user():
username = get_stored_username()
if username:
check = raw_input("Are you " + username + " ? y/s ")
if check == "y":
print "Welcome back, " + username + "!"
if check == "n":
print "I‘s sorry."
username = get_new_username()
print "We‘ll remember you when you come back, " + username + "!"
else:
username = get_new_username()
print "We‘ll remember you when you come back, " + username + "!"
greet_user()
Are you Ada ? y/s n
I‘m sorry.
What is your name ? Eric
We‘ll remember you when you come back, Eric!