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。