夜阑卧听风吹雨

铁马冰河入梦来

Loading

Python-4 异常、调试、测试、IO

异常

  1. 抛出、捕获异常 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,这将继续将所捕获的错误继续向上抛出。
  1. logging 模块
    except 中,使用 logging.exception(e) 可以记录下错误,但还可以使程序继续运行。
  • 也可以将错误信息输出到文件中。
logging.basicConfig(level=logging.INFO)
logging.info('n = %d' % n)
print(10 / n)
  • 还有:debuginfowarningerror
  1. with 关键词
    with 是一种简化的 try... except... finally...
with open('x.txt') as fp:  
    print fp.read()
  • 注意with 后面处理的对象必须有 __enter__()__exit__() 这两个方法。
  • with 在执行前先执行 __enter__(),然后在 finally
    执行 __exit__()
    • 确保了执行 finally,做好一般的善后和异常处理,一般在资源文件里使用较多。

调试

  1. 使用断言
# n应该不为0
 assert n != 0, 'n is zero!'

使用断言,如果不符合条件(n为0),则会打印后面的信息并抛出 AssertionError

  • python -O err.py:不运行 assert。相当于 pass
  1. 调试器 pdb,单步执行
  2. 设置断点:pdb.set_trace()

单元测试

编写一些测试条件,测试所有可能出现的代表情况,通过测试说明代码基本可行。

  1. 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
  1. 单元测试
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
  • setUptearDown:这两个特殊的函数会在运行每个测试方法前后被调用,可以用来连接数据库等。

文档测试

在类的文档中,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:返回当前系统的名称。
    • posixLinuxUnixMac OS X
    • ntWindows
  • 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。

posted @ 2020-12-04 21:39  二次蓝  阅读(104)  评论(0编辑  收藏  举报