Python-4 异常、调试、测试、IO
异常
- 抛出、捕获异常 try... except... finally...:
try:
    x = 1
    raise TypeError('类型错误!')
except TypeError as e: # 使用as的方式,错误类型会包括子类错误
    print('类型错误:', e.value)
else: # 成功运行,没有except的else,可选
    print('成功运行了')
finally:
    print('finally...')
print('END')
- 一般尽量使用 Python 内置的错误类型。
- 在 except里,可以使用不带参数的raise,这将继续将所捕获的错误继续向上抛出。
- logging模块
 在- except中,使用- logging.exception(e)可以记录下错误,但还可以使程序继续运行。
- 也可以将错误信息输出到文件中。
logging.basicConfig(level=logging.INFO)
logging.info('n = %d' % n)
print(10 / n)
- 还有:debug,info,warning,error。
- with关键词
 - with是一种简化的- try... except... finally...。
with open('x.txt') as fp:  
    print fp.read()
- 注意:with后面处理的对象必须有__enter__()和__exit__()这两个方法。
- with在执行前先执行- __enter__(),然后在- finally
 执行- __exit__()。- 确保了执行 finally,做好一般的善后和异常处理,一般在资源文件里使用较多。
 
- 确保了执行 
调试
- 使用断言
# n应该不为0
 assert n != 0, 'n is zero!'
使用断言,如果不符合条件(n为0),则会打印后面的信息并抛出 AssertionError。
- python -O err.py:不运行- assert。相当于- pass。
- 调试器 pdb,单步执行
- 设置断点:pdb.set_trace()
单元测试
编写一些测试条件,测试所有可能出现的代表情况,通过测试说明代码基本可行。
- mydict.py代码如下:
class Dict(dict):
    def __init__(self, **kw):
        super().__init__(**kw)
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
    def __setattr__(self, key, value):
        self[key] = value
- 单元测试
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):
    def test_init(self):
        d = Dict(a=1, b='test')
        self.assertEqual(d.a, 1)
        self.assertEqual(d.b, 'test')
        self.assertTrue(isinstance(d, dict))
    def test_key(self):
        d = Dict()
        d['key'] = 'value'
        self.assertEqual(d.key, 'value')
    def test_attr(self):
        d = Dict()
        d.key = 'value'
        self.assertTrue('key' in d)
        self.assertEqual(d['key'], 'value')
    def test_keyerror(self):
        d = Dict()
        with self.assertRaises(KeyError):
            value = d['empty']
    def test_attrerror(self):
        d = Dict()
        with self.assertRaises(AttributeError):
            value = d.empty
if __name__ == '__main__': # 设置这个语句,即当成普通的py脚本运行测试
    unittest.main()
- 继承自 unittest.TestCase。
- 以 test开头的函数才会被视为测试方法执行。
- 最常用 assertEqual()判断结果是否符合预期。
- 测试能否抛出正确的异常:
# 访问d['empty']抛出错误
with self.assertRaises(KeyError):
    value = d['empty']
- 另一种运行测试文件的方法:python -m unittest mydict_test
- setUp与- tearDown:这两个特殊的函数会在运行每个测试方法前后被调用,可以用来连接数据库等。
文档测试
在类的文档中,Python 的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。
比如重写上述的模块:
class Dict(dict):
    '''
    Simple dict but also support access as x.y style.
    >>> d1 = Dict()
    >>> d1['x'] = 100
    >>> d1.x
    100
    >>> d1.y = 200
    >>> d1['y']
    200
    >>> d2 = Dict(a=1, b=2, c='3')
    >>> d2.c
    '3'
    >>> d2['empty']
    Traceback (most recent call last):
        ...
    KeyError: 'empty'
    >>> d2.empty
    Traceback (most recent call last):
        ...
    AttributeError: 'Dict' object has no attribute 'empty'
    '''
# ...
那么在调用该文件时,如果没有显示错误,说明文档测试通过。
- 文档测试只会在命令行直接运行的时候才会被调用,平时导入模块里不会被调用。
IO 文件操作
- 外发数据 Ouput,外部发送数据来 Input。
- 同步 IO:会堵塞等待;异步 IO:继续执行,跳过等待结果,有回调模式,轮询模式(轮询是主线程主动多次去查询结果,回调是 IO 完子线程会执行的)。
在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。
- 读完文件都需要关闭文件,避免占用系统资源,需要 finally/with。
file object  = open(file_name [, access_mode][, buffering])
- file_name:目标文件,可以包括文件的路径。
- access_mode:打开文件的模式,默认只读。
- buffering:缓冲。- 0 关闭(仅二进制模式)。1 打开行缓冲(仅文本模式)。大于 1 的表示设置缓冲区大小。负数表示使用系统默认缓冲区大小。
 
- 相当于字符流,字节流。
有关模式:
| Character | Meaning | 
|---|---|
| 'r' | 只读模式 (默认 rt)。 | 
| 'w' | 写入模式,文件存在则清空重新写,不存在会自动创建。 | 
| 'x' | 独占写模式,新建一个文件,如果该文件已存在则会报错。 | 
| 'a' | 追加写入模式,文件存在追加写入,不存在会自动创建。 | 
| 'b' | 二进制模式。 | 
| 't' | 文本模式(默认 rt)。 | 
| '+' | 打开一个文件进行更新(可读可写)。 | 
os 模块
- os.name:返回当前系统的名称。- posix:- Linux、- Unix或- Mac OS X。
- nt:- Windows。
 
- os.rename(current_file_name, new_file_name):重命名文件。
- os.remove(file_name):删除文件。
- os.mkdir("newdir"):创建目录。
- os.chdir("newdir"):改变当前目录。
- os.getcwd():返回当前所在目录。
- os.rmdir('dirname'):删除目录,该目录下的文件需要先删干净。
- 操作一个路径的字符串:
- os.path.join():拼接路径,可以自动识别路径分隔符。
- os.path.split():拆分出路径里末尾的文件/目录,返回两个元素的列表。可以自动识别路径分隔符。
- os.path.splitext():拆分出路径里文件的扩展名。
 
- shutil模块提供了- copyfile()函数,相当于- os模块的补充。
# 当前目录下的所有目录
L = [x for x in os.listdir('.') if os.path.isdir(x)]
# 当前目录下的所有.py文件
L2 = [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1] == '.py']
序列化
将储存在内存中的变量等变成可储存或可传输的样式。
在 Python 中叫 pickling,在其他语言中也被称之为 serialization,marshalling,flattening 等。反之叫做反序列化,unpickling。
- pickle.dumps():将任意对象序列化成二进制- bytes,可以将二进制写到文件中。
- pickle.dump():将对象序列化写入到一个- file-like Object(有- read()方法)中。
import pickle
d = dict(name='Bob', age=20, score=88)
dbytes = pickle.dumps(d) # 是一个二进制数据
f = open('dump.txt', 'wb')
pickle.dump(d, f) # 将对象d转储到f中。
f.close()
dump 英 [dʌmp] 美 [dʌmp]
v. 倾倒;丢下;与(某人)结束恋爱关系;卸出(数据);(内存信息)转储
n. 垃圾场;废渣堆;军需品临时存放处;转储
理解:将对象从内存中倒出来,变成可储存的文件。
- pickle.loads():将二进制- bytes反序列化为对象。可以先将文件读取到二进制,然后使用该函数。
- pickle.load():将- file-like Object直接反序列化出对象。
f = open('dump.txt', 'rb')
d = pickle.load(f) # 从f中反序列化,产生一个相同内容的对象!
f.close()
print(d)
# {'age': 20, 'score': 88, 'name': 'Bob'}
JSON
- json.dump():直接把 JSON 写入一个- file-like Object。
- json.dumps():将对象转成 JSON 的字符串:
import json
d = dict(name='Bob', age=20, score=88)
print(json.dumps(d)) # 返回字符串
# {"age": 20, "score": 88, "name": "Bob"}
- json.loads():将 JSON 的字符串反序列化。- json.loads(json_str)
- json.load():从- file-like Object中读取字符串并反序列化。
- 总结:有 s表示与与文件无关,无s的表示直接与文件流相关。
序列化一个类
在类中定义方法返回字典,使用 json.dumps(s, default=student2dict)) 即可将类实例 s 转为 JSON 字符串。
# 类转为字典
def student2dict(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }
# 字典转为类
def dict2student(d):
    return Student(d['name'], d['age'], d['score'])
通常 class 的实例都有一个 __dict__ 属性,它就是一个 dict,用来存储实例变量。也有少数例外,比如定义了 __slots__ 的class。
 
    
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号