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(即存储在属性中),因此可在这个类的任何地方使用。