Python学习笔记(三)

一、文件和异常

  • 从文件中读取数据
    • 读取整个文件
with open('10.txt') as file_object:         # 函数open()接受一个参数,即要打开的文件的名称;with在不再需要访问文件后将其关闭
    contents = file_object.read()           # 方法read()读取这个文件的全部内容,在到达文件末尾时返回一个空字符串(打印出来是个空行)
    print(contents)
    • 文件路径
with open(text_files\filename.txt) as file_object:       # 相对文件路径(打开的文件位于py文件文件夹的子文件夹中)

file_path = 'C:\Users\May\Python\text_files\filename.txt'
with open(file_path) as file_object                      # 绝对文件路径(无需关心文件位于哪里)
    • 逐行读取
filename = 'digits.txt'

with open(filename) as file_object:
    for line in file_object:                         # file_object文件中每行末尾会有一个看不见的换行符
        print(line)
    • 创建一个包含文件各行内容的列表
filename = 'digits.txt'

with open(filename) as file_object:
    lines = file_object.readlines()       # 方法readlines()从文件中读取每一行

for line in lines:
    print(line.rstrip())
    • 使用文件中的内容(若将字符串转换为数值使用,函数int()转换为整数,函数float()转换为浮点数)
  • 写入文件
    • 写入空文件
      • 写入模式'w'
      • 读取模式'r'
      • 附加模式'a'
      • 读取和写入模式'r+'
filename = 'programming.txt'

with open(filename, 'w') as f_obj:         # 第二个实参'w'告诉Python以写入模式打开文件,若文件不存在,则创建一个新文件;若存在,则覆盖原有内容
    f_obj.write("I love programming")
    • 附加到文件('a',将内容添加到文件末尾)
  • 异常(使用try-except代码块处理)
    • 处理ZeroDivisionError异常
>>> print(5/0)
Traceback (most recent call last):
  File "division.py", line 1, in <module>
    print(5/0)
ZeroDivisionError: division by zero
    • 使用try-except代码块(返回一条友好信息而不是traceback)
try:
    print(5/0)                            # 将导致错误的代码行放在一个try代码块中
except ZeroDivisionError:                 # 若try代码块没问题,将跳过except代码块
    print("You can't divide by zero!")
    • 使用异常避免崩溃
    • 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("Second number: ")
    if second_number == 'q':
        break
    try:
        answer = int(first_number) / int(second_number)
    except ZeroDivisionError:
        print("You can't divide by zero!")
    else:
        print(answer)
    • 处理FileNotFoundError异常
filename = 'alice.txt'

with open(filename) as f_obj:
    contents = f_obj.read()

Traceback (most recent call last):
  File "alice.py", line 3, in <module>
    with open(filename) as f_obj:
FileNotFoundError: [Error 2] No such file or directory: 'alice.txt'
filename = 'alice.txt'

try:
    with open(filename) as f_obj:
        contents = f_obj.read()
except FileNotFoundError:
    msg = "Sorry, the file " + filename + " does not exist."
    print(msg)
    • 分析文本(项目Gutenberg提供了一系列不受版权限制的文学作品)
>>> title = "Alice in Wonderland"
>>> title.split()            # 方法split()根据一个字符串创建一个单词列表,以空格为分隔符将字符串分拆
['Alice', 'in', 'Wonderland']
    • 使用多个文件
      • try-except代码块提供了两个优点:避免让用户看到traceback;让程序能够继续分析能够找到的其他文件
    • 失败时一声不吭
try:
    --snip--
except FileNotFoundError:
    pass                                # pass语句充当占位符
else:
    --snip--
  • 存储数据(使用json模块)
    • JSON(JavaScript Object Notation)
    • 使用json.dump(),接受两个实参:要存储的是数据以及可用于存储数据的文件对象
import json

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

filename = 'numbers.json'              # 文件扩展名.json表示文件存储的数据为JSON格式
with open(filename, 'w') as f_obj:
    json.dump(numbers, f_obj)          # 这个程序生成文件numbers.json,或覆盖文件原有内容,其数据的存储格式与Python一样
    • 使用json.load():将文件数据读取到内存中
import json

filename = 'numbers.json'
with open(filename) as f_obj:
    numbers = json.load(f_obj)

print(numbers)
    • 保存和读取用户生成的数据
# remember_me.py

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 remember you when you come bacck, " + username + "!")
else:
    print("Welcome back, " + username + "!")
  • 重构:将代码划分为一系列完成具体工作的函数

 

# 重构成一个函数
import json

def greet_user():
    """问候用户,并指出其名字"""
    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 remember you when you come back, " + username + "!")
    else:
        print("Welcome back, " + username + "!")

greet_user()
# 重构成两个函数
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 greet_user():
    """问候用户,并指出其名字"""
    username = get_stored_username()
    if username:
        print("Welcome back, " + username + "!")
    else:
        username = input("What is your name? ")
        filename = 'username.json'
        with open(filename, 'w') as f_obj:
            json.dump(username, f_obj)
            print("We'll remember you when you come back, " +username + "!")

greet_user()
# 重构成三个函数
import json

def get_stored_username():
    --snip--

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

二、测试代码

  • 测试函数:Python标准库中的模块unittest提供了代码测试工具
    1. 单元测试:用于核实函数的某个方面没有问题
    2. 测试用例:一组单元测试(全覆盖式测试用例)
# name_function.py测试代码
def get_formatted_name(first, last):
    """Generate a neatly formatted full name."""
    full_name = first + ' ' + last
    return full_name.title()
    • 可通过的测试
# test_name_function.py
import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):                     # 该类用于包含一系列针对函数的单元测试,类名最好包含Test字样
    """测试name_function.py"""
    
    def test_first_last_name(self):                         # 运行py文件时,以test_开头的方法都将自动运行
        """能够正确处理像May Zheng这样的姓名吗?"""
        formatted_name = get_formatted_name('may', 'zheng')
        self.assertEqual(formatted_name, 'May Zheng')       # 一个断言方法

if __name__ == '__main__':
    unittest.main()

# 输出
.                                                     # 句点表明有一个测试通过了
-----------------------------------------
Ran 1 test in 0.000s                                  # 运行了一个测试,消耗的时间不到0.001s

OK                                                    # 测试用例中的所有单元测试都通过了
    • 不能通过的测试
# 修改name_function.py如下
def get_formatted_name(first, middle, last):
    """生成整洁的名字"""
    full_name = first + ' ' + middle + ' ' + last
    return full_name.title()
# test_name_function.py
import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):                     # 该类用于包含一系列针对函数的单元测试,类名最好包含Test字样
    """测试name_function.py"""
    
    def test_first_last_name(self):                         # 运行py文件时,以test_开头的方法都将自动运行
        """能够正确处理像May Zheng这样的姓名吗?"""
        formatted_name = get_formatted_name('may', 'zheng')
        self.assertEqual(formatted_name, 'May Zheng')       # 一个断言方法

if __name__ == '__main__':
    unittest.main()

# 输出
E                           # 指出测试用例中有一个单元测试导致了错误
==================================================================================
ERROR: test_first_last_name (__main__,NamesTestCase)
----------------------------------------------------------------------------------
Traceback (most recent call last):
  File "test_name_function.py", line 8, in test_first_last_name
    formatted_name = get_formatted_name('may', 'zheng')
TypeError: get_formatted_name() missing 1 required positional argument: 'last'
----------------------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)
    • 测试未通过时:应修改导致测试不能通过的代码,而不是修改测试
# 修改name_function.py如下
def get_formatted_name(first, last, middle=''):
    """生成整洁的名字"""
    if middle:
        full_name = first + ' ' + middle + ' ' + last
    else:
        full_name = first + ' ' + last
    return full_name.title()
    • 添加新测试(用于测试包含中间名的姓名)
# test_name_function.py
import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):                     # 该类用于包含一系列针对函数的单元测试,类名最好包含Test字样
    """测试name_function.py"""
    
    def test_first_last_name(self):                         # 运行py文件时,以test_开头的方法都将自动运行
        """能够正确处理像May Zheng这样的姓名吗?"""
        formatted_name = get_formatted_name('may', 'zheng')
        self.assertEqual(formatted_name, 'May Zheng')       # 一个断言方法

    def test_first_last_middle_name(self):
        """能够正确处理像May Yi Zheng这样的姓名吗?"""
        formatted_name = get_formatted_name(
                'may', 'zheng', 'yi')
        self.assertEqual(formatted_name, 'May Yi Zheng')

if __name__ == '__main__':
    unittest.main() 
# 输出
..
-----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK   
  •  测试类
    • 各种断言方法(Python在unittest.TestCase类中提供了很多断言方法,断言方法检查你认为应该满足的条件是否满足)
unittest Module中的断言方法
方法 用途
assertEqual(a,b) 核实a == b
assertNotEqual(a,b) 核实a != b
assertTrue(x) 核实x为True
assertFalse(x) 核实x为False
assertIn(item, list) 核实itemlist
assertNotIn(item, list) 核实item不在list
    • 要测试类的行为,需要创建其实例
# 一个要测试的类,survey.py
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)
# 测试AnonynousSurvey类,test_survey.py
import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
    """针对AnonymousSurvey类的测试"""

    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)

if __name__ == '__main__':
    unittest.main()
    • 方法setUp():先运行setUp()方法,再运行以test_开头的方法(上述测试类额代码可以不用在每个方法中创建实例)
import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
    """针对AnonymousSurvey类的测试"""

    def setUp(self):                 # 在setUp()方法中创建一系列实例并设置它们的属性,再在测试方法中直接使用这些实例
        """
        创建一个调查对象和一组答案,供使用的测试方法使用
        """
        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)

if __name__ == '__main__':
    unittest.main()
    • 每完成一个单元测试,Python都打印一个字符:
      • 测试通过时:打印一个句点
      • 测试引发错误时:打印一个E
      • 测试导致断言失败时:打印一个F

 

posted on 2019-01-24 22:30  沐沐°  阅读(205)  评论(0编辑  收藏  举报