九、文件和异常
1、从文件中读取数据
要使用文本文件中的信息,首先需要将信息读取到内存中,可以一次性读取文件的全部内容,也可以每次一行的方式逐步读取。
①读取整个文件
#在当前目录下创建一个文本为 Nine-test.txt,然后使用代码读取这个文本
with open('Nine-test') as file_object:
contents = file_object.read()
print(contents)
函数 open() :要以任何方式使用文件,哪怕是仅仅打印其内容,都要先打开文件才能访问它,函数 open() 接受一个参数,为要打开文件的名称,Python 在当前执行的文件所在的目录中查找指定的文件,函数 open() 返回一个表示文件的对象,返回的是文本,Python 将这个对象存储在我们将在后面使用的变量中。
关键字 with 在不需要访问文件后将其关闭。
read() 到达文件末尾时返回一个空字符串,而将这个空字符串显示出来时就是一个空行,要删除多余的空行,可在 print 语句中使用 rstrip()
②文件路径
要让Python打开不与程序文件位于同一个目录中的文件,需要提供文件路径。
#在 Linux 和 OS X 系统中:( / ) with open('text_files/filename.txt') as file_object: #在windows 系统中:( ) with open('text_filesfilename.txt') as file_object:
可将计算机中准确的位置告诉Python,这称为绝对路径:
file_path = 'C:/Users/rx801/PycharmProjects/study/Nine_test.txt' with open(file_path) as file_object: content = file_object.read() print(content)
③逐行读取
读取文件时,常常需要检查其中的每一行,可能需要在文件中查找特定的信息,或者要以某种方式修改文件中的文本
例如:遍历一个包含天气的文件,并使用天气描述中包含字样 sunny 的行,或在新闻报道中查找包含标签<headline>的行。
要以每次一行的方式检查文件,可对文件对象使用 for 循环。
file_path = 'Nine-test' #将要读取的文件名称存储在变量 filename 中,这是使用文件时一种常见的做法 with open(file_path) as file_object: #调用 open() 后,将一个表示文件及其内容的对象存储到了变量 file_object 中。 for line in file_object: print(line.rstrip()) #使用 rstrip 消除每次输出后面的换行符。否则每行末尾都有两个换行符,一个来自文件,另一个来自print语句
④创建一个包含文件各行内容的列表
使用关键字 with 时,open() 返回的文件对象只在 with 代码块内可用,如果要在 with 代码块外访问文件的内容,可在 with代码块内将文件的各行存储在一个列表中,并在 with 代码块外使用该列表;你可以立即处理文件的各个部分,也可推迟到程序后面再处理。
file_path = 'C:/Users/rx801/new_test.txt' with open(file_path) as file_object: line = file_object.readlines() for item in line: print(item.rstrip())
readlines() 方法从文件中读取每一行,并将其存储在一个列表中,该列表存储在变量 line 中。
⑤使用文件的内容
将文件读取到内存中后,就可以以任何方式使用这些数据了
#圆周率值,创建一个字符串,包含文件中的所有数字,且没有任何空格 file_path = 'C:/Users/rx801/new_test.txt' with open(file_path) as file_object: line = file_object.readlines() pi_string = '' for item in line: pi_string += item.strip() #拼接字符串 print(pi_string) print(len(pi_string))
#读取一百万位的文件,显示前50位,示例 file_path = 'C:/Users/rx801/new_test.txt' with open(file_path) as file_object: line = file_object.readlines() pi_string = '' for item in line: pi_string += item.strip() print(pi_string[:52] + "...")
对于可处理的数据量,Python 没有任何限制;只要系统的内存足够多,想处理多少数据都可以。
#检查生日是否包含在圆周率中 file_path = 'C:/Users/rx801/new_test.txt' with open(file_path) as file_object: line = file_object.readlines() pi_string = '' for item in line: pi_string += item.strip() birthday = input("Please input your Birthday: ") if birthday in pi_string: print("Yes,exist!") else: print("No,Not Exist!")
2、替换文件内容 -- replace()
方法 replace() 将字符串中的特定单词替换为另一个单词。
message = "I Like Python" message.replace('Python','Linux') #只是临时修改,永久修改需要将修改后的重新定义到变量中,message = message.replace('Python','Linux')
3、写入文件
保存数据最简单的方式之一是将其写入到文件中,通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出依然存在,可以在程序结束运行后查看这些输出,可与别人分享输出文件,还可编写程序来将这些输出读取到内存中并进行处理。
①写入空文件
要将文本写入文件,在调用 open() 时需要提供另一个实参,告诉Python要写入打开的文件
file_path = 'C:/Users/rx801/new_test.txt' with open(file_path,'w') as file_object: file_object.write("I Like Shell") with open(file_path) as file_a: print(file_a.read())
在这个示例中,Python 调用了两个实参,第一个实参是要打开的文件的名称,第二个实参(‘w’)告诉Python要以写入模式打开这个文件,打开文件时,可指定读取模式(’r‘),写入模式('w'),附加模式('a'),能够读取和写入文件的模式('r+'),如果省略了默认实参,Python将以默认只读的模式打开文件。
如果写入的文件不存在,函数open() 会创建它,使用('w')模式打开文件时,如果指定的文件已经存在,Python 将在返回文件对象前清空该文件。
使用文件对象的方法 write() 将一个字符串写入文件。
※Python只能将字符串写入文本文件,要将数值数据存储到文本文件中,必须使用函数 str() 将其转换为字符串格式。
②写入多行
函数 write() 不会在写入文本末尾添加换行符,因此在写入多行时需要指定换行符
file_path = 'C:/Users/rx801/new_test.txt' with open(file_path,'r+') as file_object: file_object.write("I Like Python.") file_object.write("I Like Linux.")
#加入换行符 file_path = 'C:/Users/rx801/new_test.txt' with open(file_path,'r+') as file_object: file_object.write("I Like Python. ") file_object.write("I Like Linux. ")
③附加文件(追加文件)
要给文件添加内容,而不是覆盖原有的内容,以附加模式打开文件,写入到文件的行都将添加到文件末尾,如果指定的文件不存在,Python将创建一个空文件。
file_path = 'C:/Users/rx801/new_test.txt' with open(file_path,'a') as file_object: file_object.write("I Like Python. ") file_object.write("Python is very good!")
#编写一个while 循环,提示用户输入其名字。用户输入其名字后,在屏幕上打印一句问候语,并将一条访问记录添加到文件guest.txt中。确保这 个文件中的每条记录都独占一行 file_path = 'C:/Users/rx801/guest.txt' while True: name = input("Please input your name: ") if name == 'quit': break else: message = "Hello,"+name+". " print(message) with open(file_path,'a') as file_name: file_name.write(message)
4、异常
Python使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果编写了处理该异常的代码,程序将继续执行,如果未对异常进行处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。
异常是使用 try-except 代码块处理的,try-except 代码块让 Python 执行指定的操作,同时告诉 Python 发生异常时怎么办,使用了 try_except 代码块时,即使出现异常,程序也将继续运行:显示你编写好的友好的错误信息,而不是令用户迷惑的 traceback。
①处理 ZeroDivisionError 异常
print(5/0) --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) <ipython-input-6-fad870a50e27> in <module> ----> 1 print(5/0) ZeroDivisionError: division by zero
Python 无法按照你的要求做时,就会创建这种对象,Python 将停止运行程序,并指出引发了哪种异常。
②使用 try-except 代码块
当你认为可能发生错误时,可编写一个 try-except 代码块来处理可能引发的异常。
#处理 ZeroDivisionError 异常的 try-except 代码块类似这样: try: print(5/0) except ZeroDivisionError: print("You can't divide by zero!")
将导致错误的代码行放在 try 代码块中,如果 try 代码块中的代码运行起来没有问题,Python将跳过except代码块;如果 try 代码块中的代码导致了错误,Python 将查找这样的except代码块,并运行其中的代码,即其中的错误与引发的错误相同。
如果 try-except 后面还有其他代码,程序将接着运行。
③else 代码块
while True: first_number = input("Please input first number: ") if first_number == 'quit': break second_number = input("Please input second number: ") if second_number == 'quit': break try: answer = int(first_number) / int(second_number) except: print("Error,Please input right number !") else: print(answer)
※ try-except-else 代码块的工作原理:Python 尝试执行 try 代码块中的代码,只有可能引发异常的代码才需要放在 try 语句中,仅在 try 代码块成功执行时才需要运行的代码放在 else 代码块中,except 代码块告诉 Python,尝试运行 try 代码块中的代码时引发了指定的异常怎么处理。
④处理 FileNotFoundError 异常
使用文件时,一种常见的问题是找不到文件,要查找的文件可能在其他地方、文件名可能不正确或者这个文件根本不存在。
file_path = 'C:/Users/rx801/Error.txt' with open(file_path) as file: print(file.read()) --------------------------------------------------------------------------- FileNotFoundError Traceback (most recent call last) <ipython-input-14-a6e913997e4e> in <module> 1 file_path = 'C:/Users/rx801/Error.txt' ----> 2 with open(file_path) as file: 3 file.read() FileNotFoundError: [Errno 2] No such file or directory: 'C:/Users/rx801/Error.txt'
使用 try-except 代码块
file_path = 'C:/Users/rx801/Error.txt' try: with open(file_path) as file: content = file.read() except: print("Not Find This File!") else: print(content)
⑤分析文本
方法 split() 以空格为分隔符将字符串分拆成多个部分,并将这些部分存储到一个列表中。
title = 'C:/Users/rx801/new_test.txt' try: with open(title) as file: contents = file.read() except: print("file does not exist!") else:
#计算文件包含多少单词 words = contents.split() num_words = len(words) print("The title has "+str(num_words)+" words!")
使用 for 循环查看多个文件
def count_words(title): try: with open(title) as file: contents = file.read() except: print("file does not exist!") else: words = contents.split() num_words = len(words) print("The title has "+str(num_words)+" words!") title = ['C:/Users/rx801/guest.txt','C:/Users/rx801/new_test.txt','C:/Users/rx801/abcd.txt'] for item in title: count_words(item)
⑥失败时一声不吭
在前一个示例中,有一个文件找不到,但并不是每次有异常都需要告诉用户,希望程序在发生异常时一声不吭继续运行,只需在except代码块告诉Python什么都不要做。
使用 pass 语句可让发生异常时什么都不做。
def count_words(title): try: with open(title) as file: contents = file.read() except: pass else: words = contents.split() num_words = len(words) print("The title has "+str(num_words)+" words!") title = ['C:/Users/rx801/guest.txt','C:/Users/rx801/new_test.txt','C:/Users/rx801/abcd.txt'] for item in title: count_words(item)
count() 方法可以确定特定的单词或短语在字符串中出现了多少次。
pets = "dog,cat,fish,Cat,pig,CAT" pets.lower().count('cat')
使用 lower() 将字符串变为小写,再查找相应的字符串。
5、存储数据
模块 json 能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件的数据,还可以使用 json 在 Python 程序之间分享数据。
①使用 json.dump() 和 json.load()
函数 json.dump() 接受两个实参:要存储的数据以及可用于存储数据的文件对象。
import json numbers = [2,3,4,5,6,7] filename = 'C:/Users/rx801/numbers.txt' with open(filename,'w') as f_obj: json.dump(numbers,f_obj)
使用函数 json.load() 将这个列表读取到内存中
import json filename = 'C:/Users/rx801/numbers.txt' with open(filename) as f_obj: numbers = json.load(f_obj) print(numbers)
读取前面写入的文件,使用函数 json.load() 加载存储在 numbers.json 中的信息,并将其存储到变量 numbers 中,最后打印数字列表。
6、保存和读取用户生成的数据
对于用户生成的数据,如果不以某种方式进行存储,等程序停止运行时用户的信息将丢失。
#保存用户生成的数据 import json filename = 'C:/Users/rx801/numbers.txt' username = input("What's your name? ") with open(filename,'w') as file_object: json.dump(username,file_object) #读取用户保存的数据 import json filename = 'C:/Users/rx801/numbers.txt' try: with open(filename) as file_object: username = json.load(file_object) except FileNotFoundError: username = input("What's your name? ") with open(filename,'w') as f_obj: json.dump(filename,f_obj) print("welcome back: "+username) else: print(username)
十、测试代码
编写函数或类时,可为其编写测试。通过测试,可确定代码面对各种输入都能够按要求的那样工作,在程序中添加新代码时,也可以对其进行测试,确认它们不会破坏程序既有的行为,经常测试代码,在用户发现问题前找出它们。
1、单元测试和测试用例
Python标准库中的模块 unittest 提供了代码测试工具。
单元测试用于核实函数的某个方面有没有问题。
测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。良好的测试用例考虑到了函数可能接收到的各种输入。
全覆盖式测试:用例包含一整套单元测试,涵盖了各种可能的函数使用方式。
#main_user.py def greet_user(first_name,last_name): full_name = first_name+' '+last_name return full_name #Users.py import unittest from main_user import greet_user class NameTestCase(unittest.TestCase): #创建的类名随便起,但是最好和测试相关并包含Test,这个类必须继承unittest.TestCase类,这样Python才知道如何运行测试。 '''测试 main_user.py''' def test(self): full_name = greet_user('Steven','Curry') '''使用unittest类的断言方法''' self.assertEqual(full_name,'Steven Curry') #如果full_name的值与字符串'Steven Curry'相等,就OK,不相等就Fail unittest.main()
assertEqual 方法是 unittest 类最有用的功能之一:一个断言方法,断言方法用来核实得到的结果是否与期望的结果一致。
2、测试类
①各种断言方法
Python 在unittest.TestCase类中提供了很多断言方法。断言方法检查你认为应该满足的条件是否确实满足。
②unittest Module中的断言方法: