学习处理文件——让程序能快速地分析大量数据;
学习错误处理——避免在程序在面对意外情形时崩溃;
学习异常——Python创建的特殊对象,用于管理程序运行时的错误;
学习模块json——能让你能够保存用户数据,以免程序停止运行后丢失。
本章内容可提高程序的适用性、可用性和稳定性
3.1415926535
8979323846
2643383279
file_reader.py
with open('pi_digits.txt') as file_object:
contents = file_object.read()
print(contents)
运行结果:
3.1415926535
8979323846
2643383279
例子如上,现在来详细分析一下吧:
file_path = r'D:\vscode\PYTHON\demo\pi_digits.txt'
with open(file_path) as file_object:
通过绝对路径,可读取系统任何地方的文件.就目前而言,最简单的做法是,要么将数据文件存储在程序文件所在的目录,要么将其存储在吃呢工序所在目录下的一个文件夹中.
注意:windows系统有时候能够正确地读取文件路径中的斜杆.如果使用的是Windows系统,且结果不符合预期,请确保在文件路径中使用的是反斜杠.另外,由于反斜杠在Python中被视为转义标记,为在Windows中确保万无一失,应以原始字符串的方式指定路径,即在开头的单引号加上r
with open('pi_digits.txt') as file_object:
for line in file_object:
print(line)
运行结果:
3.1415926535
8979323846
2643383279
我们打印每一行时,发现空白行更多了.为何会出现这些空白行?因为在这个文件中,每行的末尾都有一个看不见的换行符,而print语句也会加上一个换行符,因此每行有两个换行符:一个来自文件,一个来自print语句.要消除这些多余的空白,使用rstrip():
file_name = 'pi_digits.txt'
with open(file_name) as file_object:
for line in file_object:
print(line.rstrip())
运行结果:
3.1415926535
8979323846
2643383279
现在.输出又跟文件内容相同了
file_name = 'pi_digits.txt'
with open(file_name) as file_object:
lines = file_object.readlines()
for line in lines:
print(line.rstrip())
运行结果:
3.1415926535
8979323846
2643383279
方法readlines()从文件中读取每一行,并将其存储在一个列表中;接下来,该列表被存储到变量中;在with代码块外,我们依然可以使用这个变量.下面的for循环的输出结果则印证了这一点.
file_name = 'pi_digits.txt'
with open(file_name) as file_object:
lines = file_object.readlines()
pi_string = ''
for line in lines:
pi_string += line.strip()
print(pi_string)
print(len(pi_string))
运行结果:
3.141592653589793238462643383279
32
为什么这个例子使用的是strip()方法而不是rstrip方法呢?这是有原因的,rstrip()方法仅能删除后面的空白,但数字右边也存在空白.因此,要用strip()方法.
这样,我们就获得了一个这样的字符串:它包含精确到30位小数的圆周率值
注意:读取文本文件时,Python将其中的所有文本都解读为字符串.如果你读取的是数字,并要将其作为数值使用,就必须使用函数int()将其转换成整数,或使用函数float()将其转换成浮点数.
保存数据的最简单的方式之一是将其写入到文件中.通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出也依然存在:你可以在程序结束运行后查看这些输出,可与别人分享输出文件,还可以编写程序来将这些输出读入到内存中并进行处理.
file_name = 'programming.txt'
with open(file_name,'w') as file_object:
file_object.read("I love programming.")
运行结果:
programming.txt
I love programming.
注意:Python只能将字符串写入文本文件.要将数值数据存储到文本文件,必须先使用str()将其转换为字符串格式.
file_name = 'programming.txt'
with open(file_name,'w') as file_object:
file_object.write("I love programming.\n")
file_object.write("I love creating new games.\n")
运行结果:
programming.txt
I love programming.
I love creating new games.
像显示到终端的输出一样,还可以使用空格、制表符和空行来设置输出的格式。
programming.txt
file_name = 'programming.txt'
with open(file_name,'a') as file_object:
file_object.write("I also love finding meanig in large datasets.\n")
file_object.write("I love creating apps that can run in a browser.\n")
运行结果:
programming.txt
I love programming.
I love creating new games.
I also love finding meanig in large datasets.
I love creating apps that can run in a browser.
在打开方式处,我们选择了附加模式(即实参’a’),以便将内容附加到文件末尾,而不是覆盖文件原来的内容.最终的结果是:文件原来的内容还在,它们后面是我们刚添加的内容.
Python使用被称为异常的特殊对象来管理程序执行期间发生的错误.每当发生让python不知所措的错误时,它都会创建一个异常对象.如果你编写了处理该异常的代码,程序将继续运行;如果你未对异常进行处理,程序将停止,并显示一个traceback,其中包含有关异常的报告.
异常是使用try-except代码块处理的.try-except代码块让Python执行指定的操作,同时告诉Python发生异常时怎么办.使用了try-except代码块时,即便出现了异常,程序也将继续运行:显示你编写的友好的错误信息,而不是令用户迷惑的traceback.
print(5/0)
显然,会导致异常:
Traceback (most recent call last):
File "", line 1, in
print(5/0)
ZeroDivisionError: division by zero
在traceback中,错误ZeroDivisionError是一个异常对象.python无法按要求做时,就会创建这种对象.在这种情况下,python将停止运行程序,并指出发生了哪种对象,而我们可根据这些消息对程序进行修改.下面我们将告诉python,发生这种错误时怎么办.
处理ZeroDivisionError异常的try-except代码块类似下面这样:
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero.")
运行结果:
You can’t divide by zero.
将引发错误的代码行放在try代码块中.如果try代码块中的代码运行起来没有问题.Python将跳过except代码块;如果try代码块中的代码导致了错误,python将查找这样的代码块,并运行其中的代码,即其中指定的错误与引发的错误相同.
如果try-except代码块后面还有其他代码,程序将继续运行,因为已经告诉python如果处理这种错误.
print("Give me two numbers,and I will divide them.")
print("Input 'q' to quit.")
while True:
first_num = input("\nfirst number:")
if first_num == 'q':
break
second_num = input("second number:")
try:
answer = int(first_num)/int(second_num)
except ZeroDivisionError:
print("You can't divide by 0!")
else:
print(answer)
我们让Python尝试执行try代码块里的除法运算,这个代码块只包含可能导致错误的代码.依赖于try代码块成功执行的代码都放在else代码块中,如果除法运算成功,我们就使用else代码块打印结果
try-except-else代码块的工作原理大致如下:python尝试try代码块中的代码;只有可能引发异常得到代码才需要放在try代码块中.有时候,有一些仅在try代码块运行成功时才需要运行的代码;这些代码应放在else代码块中.except代码块告诉python,如果它尝试运行try代码块中的代码时引发了指定的异常,该怎么办.
倘若不使用try-except代码块会发生什么呢?将会发生下面的错误:
Traceback (most recent call last):
File "d:\vscode\test.py", line 1, in
print(5/0)
ZeroDivisionError: division by zero
程序崩溃可不好,但让用户看到traceback也不是好主意.不懂技术的用户会让它们搞糊涂,而且如果用户有恶意,他会通过traceback获悉你不希望他知道的信息.有时候,训练有素的攻击者可根据这些信息判断出可对你的代码发出什么样的攻击.
通过预测可能可能发生错误的代码,可编写健壮的程序,它们即便面临无效数据或缺少资源,也能继续运行,从而能够抵挡无意的用户错误和恶意的攻击.
def count_words(filename):
"""计算一个文件大概包含多少单词"""
try:
with open(filename) as f_obj:
contents = f_obj.read()
except FileNotFound_Error:
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.")
#分析单个文本
filename = "alice.txt'
count_word(filename)
#分析多个文件
filenames = ['alice.txt','siddhartha.txt','little_women.txt','moby_dick.txt']
for filename in filenames:
count_word(filename)
运行结果:
The file alice.txt has about 29461 words.
The file alice.txt has about 29461 words.
The file siddhartha.txt has about 42172 words.
Sorry,the file little_women.txt does not exist.
The file moby_dict.txt has about 215136 words.
让我们来分析以上代码!
except FileNotFound_Error:
pass
很多程序都要求用户输入某种信息,如让用户存储游戏首选项或提供可视化的数据。不管专注的是什么,程序都要将程序提供的信息存储在列表和字典等数据结构。用户关闭程序时,你几乎总是要保存他们提供的信息;一种简单的方式是使用模块json来存储数据。
模块json让你能够将简单的Python数据结构转储在文件中,并在程序再次运行时加载该文件中的数据.你还可以使用json在python程序之间分享数据.更重要的是,JSON数据格式并非python专用的,这让你能够将以Json格式存储的数据与使用其他编程语言的人分享.这是一种轻便格式,很有用,也易于学习.
注意:JSON(JavaScript Object Notation)格式最初是为JavaScript开发的,但随后成为了一种常见格式,被包括Python在内的众多语言采用.
import json
filename = 'username.json'
try:
#如果以前存储了用户名,就加载它
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
#否则,就提示用户输入用户名并存储它
username = input("What is your name?")
with open(filename,'w') as f_obj:
json.dump(username,f_obj)
print("We'll remenber you when you come back," + usename + "!")
else:
print("Welcome back," + username + "!")
让我们分析下这个例子:
Welcome back,Eric!
What is your name?Eric
We'll remenber you when you come back,Eric!
import json
def get_stored_username():
"""如果存储了用户名,就获取它"""
filename = "username.json"
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
return None
else:
return username
def get_new_username():
"""提示用户输入用户名"""
username = input("What is your name?")
filename = "username.json"
with open(filename) as f_obj:
json.dump(usename,f_obj)
return username
def greet_user():
"""问候用户,并指出其名字"""
usename = get_stored_username()
if username:
print("Welcome back," + username +"!")
else:
username = get_new_username()
print("We'll remenber you when you come back." + username + "!")
greet_user()
重构之后,每个函数都执行单一而清晰的任务.我们调用greet_user(),它打印一条合适的消息:要么欢迎老用户回来,要么问候新用户.为此,它先调用get_stored_username(),这个函数只负责获取存储的用户名(如果存储了的话),再在必要时调用get_new_username(),这个函数只负责获取并存储新用户.要编写出清晰而易于维护和拓展的代码,这种工作必不可少.
个人从书本学到的知识,作为自己学习笔记的同时,与各位朋友分享,如有不足,请多多指教
参考文献:《Python编程从入门到实践》【美】Eric Matthes 著 袁国忠 译