05.文件和异常

1、读取文件

读取文本文件时,Python将其中的所有文本都解读为字符串。如果你读取的是数字,并要将其作为数值使用,就必须使用函数int()将其转换为整数,或使用函数float()将其转换为浮点数。

3.1415926535
8979323846
2643383279
with open('pi_digits.txt') as file_object:
	contents = file_object.read()
	print(contents)
  • 函数open():接受一个参数:要打开的文件的名称。返回一个表示文件的对象。
  • 关键字with:在不再需要访问文件后将其关闭。只管打开文件,并在需要时使用它,Python自会在合适的时候自动将其关闭。
  • 可以调用open()和close()来打开和关闭文件,但这样做时,如果程序存在bug,导致close()语句未执行,文件将不会关闭。未妥善地关闭文件可能会导致数据丢失或受损。如果在程序中过早地调用close(),你会发现需要使用文件时它已关闭(无法访问),这会导致更多的错误。

相对路径:如果文件夹text_files位于文件夹python_work中,可使用相对文件路径来打开该文件夹中的文件。

# Linux系统和OS X系统
with open('text_files/filename.txt') as file_object:

# Windows系统中使用 反斜杠
with open('text_files\filename.txt') as file_object:

绝对路径:绝对路径通常比相对路径更长,因此将其存储在一个变量中,再将该变量传递给open()会有所帮助。

# Linux系统和OS X系统
file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:
    
# Windows系统
file_path = 'C:\Users\ehmatthes\other_files\text_files\filename.txt'
with open(file_path) as file_object
  • 由于反斜杠在Python中被视为转义标记,为在Windows中确保万无一失,应以原始字符串的方式指定路径,即在开头的单引号前加上r

逐行读取:读取文件时,常常需要检查其中的每一行:可能要在文件中查找特定的信息,或者要以某种方式修改文件中的文本。

filename = 'pi_digits.txt'

with open(filename) as file_object:
	for line in file_object:
		print(line.rstrip())
  • rstrip():消除这些多余的空白行,在print语句中使用

包含文件各行内容列表:使用关键字with时,open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件的内容,可在with代码块内将文件的各行存储在一个列表中,并在with代码块外使用该列表:你可以立即处理文件的各个部分,也可推迟到程序后面再处理。

filename = 'pi_digits.txt'

with open(filename) as file_object:
	lines = file_object.readlines()
	
for line in lines:
	print(line.rstrip())
  • readlines():从文件中读取每一行,并将其存储在一个列表中

使用文件中的内容:将文件读取到内存中后,就可以以任何方式使用这些数据了。

filename = 'pi_digits.txt'

with open(filename) as file_object:
	lines = file_object.readlines()
	
pi_string = ''
for line in lines:
	pi_string += line.strip()

print(pi_string)
print(len(pi_string))
  • strip():删除每行左右两边的空格

----------------------------------------------------

2、写入文件

写入空文件:打开文件时,可指定读取模式('r')、写入模式('w')、附加模式('a')或让你能够读取和写入文件的模式('r+')。如果省略了模式实参,Python将以默认的只读模式打开文件。

filename = 'programming.txt'

with open(filename, 'w') as file_object:
	file_object.write("I love programming.")
  • 如果要写入的文件不存在,函数open()将自动创建它。
  • 以写入('w')模式打开文件,如果指定的文件已经存在,Python将在返回文件对象前清空该文件。
  • Python只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数str()将其转换为字符串格式。

写入多行:函数write()不会在你写入的文本末尾添加换行符,要让每个字符串都单独占一行,需要在write()

语句中包含换行符:

filename = 'programming.txt'

with open(filename, 'w') as file_object:
    file_object.write("I love programming.\n")
    file_object.write("You love programming too.\n")

追加到文件:如果你要给文件添加内容,而不是覆盖原有的内容,可以附加模式打开文件。写入到文件的行都将添加到文件末尾。

filename = 'programming.txt'

with open(filename, 'a') as file_object:
    file_object.write("I also love finding meaning in large datasets.\n")
    file_object.write("I love creating apps that can run in a brower.\n")

-------------------------------------------------------

3、存储数据

模块json能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。还可以使用json在Python程序之间分享数据。

json.dump():使用json.dump()来存储这组数字。

import json

numbers = [2, 3, 5, 7, 11, 13]

filename = 'numbers.json'
with open(filename, 'w') as file_object:
	json.dump(numbers, file_object)

json.load():使用json.load()来读取文件中的内容。

import json

filename = 'numbers.json'
with open(filename) as file_object:
	numbers1 = json.load(file_object)
print(numbers)

保存和读取用户输入:对于用户生成的数据,使用json保存它们大有裨益,因为如果不以某种方式进行存储,等程序停止运行时用户的信息将丢失。

import json

# 如果以前存储了用户名,就加载它
# 否则,就提示用户输入用户名并存储它
filename = 'username.json'
try:
	with open(filename) as file_obj:
		username = json.load(file_obj)
except FileNotFoundError:
	username = input("What's your name?")
	with open(filename, 'w') as file_obj:
		json.dump(username, file_obj)
		print("We'll remember you when you come back, " + username + "!")
else:
	print("Welcome back, " + username + "!")

重构:将代码划分为一系列完成具体工作的函数。、重构让代码更清晰、更易于理解、更容易扩展。

  • First rebuild:将其所有代码都放到一个名为greet_user()的函数中:
import json

def greet_user():   
    """问候用户,并指出其名字""" 
    filename = 'username.json'
    try:        
        with open(filename) as f_obj:           
			username = json.load(f_obj)   
    except FileNotFoundError:        
        username = input("Whatis your name? ")        
        with open(filename, 'w') as f_obj:           
            json.dump(username, f_obj)           
 			print("We'll remember you when you come back, " + username + "!")    
    else:        
        print("Welcome back, " + username + "!")
        
greet_user()
  • Second rebuild:greet_user(),让它不执行这么多任务。将获取存储的用户名的代码移到另一个函数中:
import json

def get_stored_username():
	"""如果存储了用户名,就获取它"""
	filename = 'username.json'
	try:
		with open(filename) as file_obj:
			username = json.load(file_obj)
	except FileNotFoundError:
		return None
	else:
		return username


def greet_user():
	"""问候用户,并指出其名字"""
	username = get_stored_username()
	if username:
		print("Welcome back, " + username + "!")
	else:
		uusername = input("What's your name?")
		filename = 'username.json'
		with open(filename, 'w') as file_obj:
			json.dump(username, file_obj)
			print("We'll remember you when you come back, " + 
					username + "!")
					
greet_user()
  • Third rebuild:最终版本中,每个函数都执行单一而清晰的任务。
import json

def get_stored_username():
	"""如果存储了用户名,就获取它"""
	filename = 'username.json'
	try:
		with open(filename) as file_obj:
			username = json.load(file_obj)
	except FileNotFoundError:
		return None
	else:
		return username

def get_new_username():
	"""提示用户输入用户名"""
	username = input("What's your name?")
	filename = 'username.json'
	with open(filename, 'w') as file_obj:
		json.dump(filename, file_obj)
	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()

----------------------------------------------------

4、异常

异常是使用try-except代码块处理的。使用了try-except代码块时,即便出现异常,程序也将继续运行:显示你编写的友好的错误消息,而不是令用户迷惑的traceback。

处理ZeroDivisionError异常:不能将一个数字除以0

try:
	print(5 / 0)
except ZeroDivisionError:
	print("You can't divide by zero!\n")
	
print("It's going on...")

try-except-else代码块:依赖于try代码块成功执行的代码都应放到else代码块中:

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("\nSecond number:")
	if second_number == 'q':
		break
		
	try:
		answer = int(first_number) / int(second_number)
	except ZeroDivisionError:
		print("You cant't divided by 0")
	else:
		print(answer)

处理FileNotFoundError异常:使用文件时,一种常见的问题是找不到文件:你要查找的文件可能在其他地方、文件名可能不正确或者这个文件根本就不存在。

filename = 'alice.txt'

try:
	with open(filename) as file_object:
		contents = file_object.read()
except FileNotFoundError:
	msg = "Sorry, the file " + filename + " does not exist."
	print(msg)

分析文本:方法split(),它根据一个字符串创建一个单词列表

filename = 'alice.txt'

try:
	with open(filename) as file_object:
		contents = file_object.read()
except FileNotFoundError:
	print("Sorry, the file " + filename + " does not exist.")
else:
	# 计算文件大致包含多少个单词
	words = contents.split()
	num_words = len(words)
	print("The file " + filename + 
			" has about " + str(num_words) + " words.")

使用多个文件:

def count_words(filename):
	"""计算一个文件大致包含多少个单词"""
	try:
		with open(filename) as file_object:
			contents = file_object.read()
	except FileNotFoundError:
		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) + " wrods.")
			  

filenames = ['alice.txt', 'siddhartha.txt', 
				'moby_dick.txt', 'little_woman.txt']
for filename in filenames:
	count_words(filename)

发生异常时什么都不做:在except代码块中使用pass语句。

def count_words(filename):
	"""计算一个文件大致包含多少个单词"""
	try:
		"""--snip--"""
	except FileNotFoundError:
		pass
	else:
		"""--snip--"""
			  

filenames = ['alice.txt', 'siddhartha.txt', 
				'moby_dick.txt', 'little_woman.txt']
for filename in filenames:
	count_words(filename)
5、测试函数

Python标准库中的模块unittest提供了代码测试工具。单元测试用于核实函数的某个方面没有问题;测试用例是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。良好的测试用例考虑到了函数可能收到的各种输入,包含针对所有这些情形的测试。全覆盖式测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。对于大型项目,要实现全覆盖可能很难。通常,最初只要针对代码的重要行为编写测试即可,等项目被广泛使用时再考虑全覆盖。

要为函数编写测试用例,可先导入模块unittest以及要测试的函数,再创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试。

可通过的测试:

import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
	"""测试name_fucntion.py"""
	def test_first_last_name(self):
		"""能够正确地处理像Janis Joplin这样的名字吗?"""
		formatted_name = get_formatted_name('janis', 'joplin')
		self.assertEqual(formatted_name, 'Janis Joplin')

unittest.main()
  • 首先,导入了模块unittest和要测试的函数get_formatted_name()。
  • 创建了一个名为NamesTestCase的类,用于包含一系列针对get_formatted_name()的单元测试。这个类必须继承unittest.TestCase类,这样Python才知道如何运行你编写的测试。
  • 运行testname_function.py时,所有以test打头的方法都将自动运行。在这个方法中,我们调用了要测试的函数,并存储了要测试的返回值。
  • assertEqual():断言方法用来核实得到的结果是否与期望的结果一致。
  • unittest.main():让Python运行这个文件中的测试。

不可通过的测试:

测试未通过时,不要修改测试,而应修复导致测试不能通过的代码:检查刚对函数所做的修改,找出导致函数行为不符合预期的修改。

新增的中间名参数是必不可少的,这导致get_formatted_name()的行为不符合预期。就这里而言,最佳的选择是让中间名变为可选的。

def get_formatted_name(first, last, middle=''):
	"""Generate a neatly formatted full name."""
	if middle:
		full_name = first + ' ' + middle + ' ' + last
		return full_name.title()
	else:
		full_name = first + ' ' + last
		return full_name.title()

添加新的测试:

import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
	"""测试name_fucntion.py"""
	def test_first_last_name(self):
		"""能够正确地处理像Janis Joplin这样的名字吗?"""
		formatted_name = get_formatted_name('janis', 'joplin')
		self.assertEqual(formatted_name, 'Janis Joplin')
	
	def test_first_last_middle_name(self):
		"""能够正确地处理像Wolfgang Amadeus Mozart这样的姓名吗?"""
		formatted_name = get_formatted_name(
							'wolfgang', 'mozart', 'amadeus')
		self.assertEqual(formatted_name, 'Wolfgang Amadeus Mozart')


unittest.main()
6、测试类

Python在unittest.TestCase类中提供了很多断言方法:

方法

用途

assertEqual(a, b)

核实 a == b

assertNotEqual(a, b)

核实 a != b

assertTrue(x)

核实 x 为 True

assertFalse(x)

核实 x 为 False

assertIn(item, list)

核实 item 在 list 中

assertNotIn(item, list)

核实 item 不在在 list 中

基础类:这个类首先存储了一个你指定的调查问题,并创建了一个空列表,用于存储答案:

class AnonymousSurvey():
	"""收集匿名调查问卷的答案"""
	
	def __init__(self, question):
		"""存储一个问题,并为存储答案做准备"""
		self.question = question
		self.responses = []
		
	def show_question(self):
		"""显示调查问卷"""
		print(self.question)
	
	def store_response(self, new_response):
		"""存储单份调查答卷"""
		self.responses.append(new_response)
		
	def show_results(self):
		"""显示收集到的所有答卷"""
		print("Survey results:")
		for response in self.responses:
			print('-' + response)

功能类:调用show_question()来显示问题,并提示用户输入答案。收到每个答案的同时将其存储起来。用户输入所有答案(输入q要求退出)后,调用show_results()来打印调查结果:

from survey import AnonymousSurvey

# 定义一个问题,并创建一个表示调查的AnonymousSurvey对象
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)

# 显示问题并存储答案
my_survey.show_question()
print("Enter 'q' at any time to quit.\n")
while True:
	response = input("Language: ")
	if response == 'q':
		break
	my_survey.store_response(response)
	
# 显示调查结果
print("\nThank you to everyone who participated in the survey!")
my_survey.show_results()

测试类:对AnonymousSurvey类的行为的一个方面进行验证:如果用户面对调查问题时只提供了一个答案,这个答案也能被妥善地存储。为此,我们将在这个答案被存储后,使用方法assertIn()来核实它包含在答案列表中:

import unittest
from survey import AnonymousSurvey


class TestAnonymousSurvey(unittest.TestCase):
	"""针对AnnoymousSurvey类的测试"""
	
	def test_store_single_response(self):
		"""测试单个答案会被妥善地存储"""
		question = "What language did you first learn to speak?"
		my_survey = AnonymousSurvey(question)
		my_survey.store_response('English')
		
		self.assertIn('English', my_survey.responses)

    def test_store_three_responses(self):
    		"""测试三个答案会被妥善地存储"""
    		question = "What language did you first learn to speak?"
    		my_survey = AnonymousSurvey(question)
    		responses = ['English', 'Spanish', 'Mandarin']
    		for response in responses:
    			my_survey.store_response(response)
    		
    		for response in responses:
    			self.assertIn(response, my_survey.responses)


unittest.main()

setUp()方法:unittest.TestCase类包含方法setUp(),让我们只需创建这些对象一次。

如果在TestCase类中包含了方法setUp(),Python将先运行它,再运行各个以test_打头的方法。这样,在编写的每个测试方法中都可使用在方法setUp()中创建的对象了。

import unittest
from survey import AnonymousSurvey


class TestAnonymousSurvey(unittest.TestCase):
	"""针对AnnoymousSurvey类的测试"""
	
	def setUp(self):
		"""
		创建一个调查对象和一组答案,供使用的测试方法使用
		"""
		question = "What language did you first learn to speak?"
		self.my_survey = AnonymousSurvey(question)
		self.responses = ['English', 'Spanish', 'Mandarin']
		
	def test_store_single_response(self):
		"""测试单个答案会被妥善地存储"""
		self.my_survey.store_response(self.responses[0])
		self.assertIn(self.responses[0], self.my_survey.responses)
	
	def test_store_three_responses(self):
		"""测试三个答案会被妥善存储"""
		for response in self.responses:
			self.my_survey.store_response(response)
		for response in self.responses:
			self.assertIn(response, self.my_survey.responses)

unittest.main()
  • 方法setUp()做了两件事情:创建一个调查对象;创建一个答案列表。存储这两样东西的变量名包含前缀self(即存储在属性中),因此可在这个类的任何地方使用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值