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

Python-4 异常、调试、测试、IO
二次蓝 Lv4

异常

  1. 抛出、捕获异常 try... except... finally...

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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,这将继续将所捕获的错误继续向上抛出。
  2. logging 模块
    except 中,使用 logging.exception(e) 可以记录下错误,但还可以使程序继续运行。

    • 也可以将错误信息输出到文件中。
    1
    2
    3
    logging.basicConfig(level=logging.INFO)
    logging.info('n = %d' % n)
    print(10 / n)
    • 还有:debuginfowarningerror
  3. with 关键词
    with 是一种简化的 try... except... finally...

    1
    2
    with open('x.txt') as fp:  
    print fp.read()
    • 注意with 后面处理的对象必须有 __enter__()__exit__() 这两个方法。
    • with 在执行前先执行 __enter__(),然后在 finally
      执行 __exit__()
    • 确保了执行 finally,做好一般的善后和异常处理,一般在资源文件里使用较多。

调试

  1. 使用断言

    1
    2
    # n应该不为0
    assert n != 0, 'n is zero!'

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

    • python -O err.py:不运行 assert。相当于 pass
  2. 调试器 pdb,单步执行

  3. 设置断点:pdb.set_trace()

单元测试

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

  1. mydict.py代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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
  2. 单元测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    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() 判断结果是否符合预期。
    • 测试能否抛出正确的异常:
    1
    2
    3
    # 访问d['empty']抛出错误
    with self.assertRaises(KeyError):
    value = d['empty']
    • 另一种运行测试文件的方法:python -m unittest mydict_test
    • setUptearDown:这两个特殊的函数会在运行每个测试方法前后被调用,可以用来连接数据库等。

文档测试

在类的文档中,Python 的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。
比如重写上述的模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
1
file object  = open(file_name [, access_mode][, buffering])
  • file_name:目标文件,可以包括文件的路径。
  • access_mode:打开文件的模式,默认只读。
  • buffering:缓冲。
    • 0 关闭(仅二进制模式)。1 打开行缓冲(仅文本模式)。大于 1 的表示设置缓冲区大小。负数表示使用系统默认缓冲区大小。
  • 相当于字符流,字节流。

有关模式

CharacterMeaning
'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 模块的补充。

1
2
3
4
# 当前目录下的所有目录
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() 方法)中。
1
2
3
4
5
6
7
8
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 直接反序列化出对象。
1
2
3
4
5
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 的字符串:
1
2
3
4
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 字符串。

1
2
3
4
5
6
7
8
9
10
# 类转为字典
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。

  • 标题: Python-4 异常、调试、测试、IO
  • 作者: 二次蓝
  • 创建于 : 2020-12-04 21:38:29
  • 更新于 : 2020-12-04 21:39:00
  • 链接: https://blog.ercilan.cn/2020/12/04/Python-4-异常、调试、测试、IO/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论