错误处理
try
def foo(): #先定义两个函数
r = some_function()
if r == (-1):
return (-1)
return r
def bar():
r = foo()
if r == (-1):
print('Error')
else:
pass
try的机制:
try:
print('try…')
r = 10 / 'a'
print('result:', r)
except ValueError as e: #错误类型有很多,如果有不同的类型错误,应该由不同的except语句块处理
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e) #所有错误的类型都继承自BaseException,不但捕获该类型的错误,还把其子类一网打尽
else:
print('no Error!') #
finally:
print('finally…') #可以没有finally语句
print('END')
try还可以跨越多层调用:
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
print('Error', e)
finally:
print('finally...')
调用堆栈
编写一个错误范例:
#err.py
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
bar('0')
main()
执行结果:
Traceback (most recent call last): #tell us this is 错误的跟踪信息
File "err.py", line 11, in <module> #调用main()出错了,在第11行代码,但是真正的原因是在第9行
main()
File "err.py", line 9, in main #调用的bar(‘0’)出错了,但是原因在第6行
bar('0')
File "err.py", line 6, in bar #return foo(s) * 2这个语句出错了,但是原因在第3行
return foo(s) * 2
File "err.py", line 3, in foo #原来是return 10 / int(s)出错了,下方打印的是错误产生的源头
return 10 / int(s)
ZeroDivisionError: division by zero #根据错误类型,我们判断int(s)本身没有出错,但是返回0,在计算10 / 0时出错
记录错误
logging可以记录错误,并且可以让程序打印完错误信息后继续运行:
#error_logging
import logging #导入logging模块
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
logging.except(e)
main()
print('END')
运行结果:
ERROR:root:division by zero
Traceback (most recent call last): #打印错误信息
File "/Users/Mojian/Desktop/learnpy/error_logging.py", line 12, in main
bar('0')
File "/Users/Mojian/Desktop/learnpy/error_logging.py", line 8, in bar
return foo(s) * 2
File "/Users/Mojian/Desktop/learnpy/error_logging.py", line 5, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
END #程序继续运行,并正常退出
抛出错误
用raise语句跑出一个错误的实例:
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n == 0:
raise FooError('invalid value: %s' % s) #尽量使用Python内置的错误类型,如ValueError等
return 10 / n
foo('0')
执行后,可以跟踪我们定义的错误:
Traceback (most recent call last):
File "/Users/Mojian/Desktop/learnpy/error_raise.py", line 11, in <module>
foo('0')
File "/Users/Mojian/Desktop/learnpy/error_raise.py", line 8, in foo
raise FooError('invalid value: %s' % s)
FooError: invalid value: 0
还有另外一种错误处理方式:
def foo(s):
n = int(s)
if n == 0:
raise ValueError('invalid value: %s' % s)
return 10 / n
def bar():
try:
foo('0')
except ValueError as e:
print('ValueError:', e)
raise #raise如果不加参数,会把错误原样抛出
bar()
#返回:
ValueError: invalid value: 0
File "<input>", line 13, in <module>
File "<input>", line 9, in bar
File "<input>", line 4, in foo
ValueError: invalid value: 0
运行过程:
1.bar()调用,函数开始执行,执行try—>foo('0') —>调用foo函数,参数’0’
2.foo('0')开始执行,抛出错误raise ValueError('invalid value: %s' % 0)
3.foo('0')此时抛出的错误不会在窗口中打印,抛到它的上级调用者----bar()中的try中
4.try…except捕获到抛出的错误,except对错误类型进行对比—>找到此类型的错误处理信息—>错误进行处理—>错误再次抛出—>抛出到上级
5.上级无try...except捕获并处理错误的语句,错误信息在窗口中打印出来
在except中raise一个Error,还可以把一种类型的错误转化成另一种,但,决不能把一个IOError 转换成 不相干的ValueError
try:
10 / 0
except ZeroDivisionError as e:
raise ValueError('input error!')
调试
print
调试第一种方法是直接打印出来调试信息:
def foo(s):
n = int(s)
print('>>> n = %s' % n)
return 10 / n
def main():
foo('0')
main()
执行后在输出中查找打印的变量值:
>>> n = 0
Traceback (most recent call last):
File "/Users/Mojian/Documents/调试1.py", line 9, in <module>
main()
File "/Users/Mojian/Documents/调试1.py", line 7, in main
foo('0')
File "/Users/Mojian/Documents/调试1.py", line 4, in foo
return 10 / n
ZeroDivisionError: division by zero
坏处是还需删除print()
断言
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'#判断n 是否为0,为0,抛出AssertionError
return 10 / n #assert为Ture,则 继续进行
def main(): #调用foo()
foo('0')
logging
import logging
logging.basicConfig(level = logging.INFO)
#指定记录信息的级别(debug,info,warning,error等),当指定level=warning时,logging.debug,info就不起作用
s = '0'
n = int(s)
logging.info('n is %d' %n)
print(10 / n)
单元测试
编写一个Dict类,这个类的行为和dict一致,但是可以通过属性访问,mydict.py:
class Dict(dict):
def __init__(self, **kw): #__init__是构造函数(附带任何可选参数)
super().__init__(**kw) #super继承父类,调用父类的__init__()
def __getattr__(self, key): #__getattr__获得属性(getattr()内置方法)
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict has no attribute '%s'' % key)
def __setattr__(self, key, value):
self[key] = value
#__setattr__设置属性;setattr赋给self对象的key属性 值 value
编写mydict_test:
import unittest#需引入Python自带的unitest模块
from mydict import Dict
class TestDict(unitest.TestCase):#编写一个单元测试,需编写一个测试类,需从unittest.TestCase继承
def setUp(self):#可以在单元test中增加两个特殊方法,这两方法会分别在每调用一个测试方法的前后分别被执行
print('setUp…')
def tearDown(self):#可以在setUp()中连接数据库,在tearDown中关闭数据库,不必在每个测试中重复代码
print('tearDown…')
def test_init(self): #以test开头的方法是测试方法,才会在单元测试中被测试
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’)#最常用的断言assertEqual(),断言两个值相等
def test_attr(self):
d = Dict()
d.key = 'value'
self.assertTrue('key' in d) #断言d中含有'key'属性
self.assertEqual(d['key'], value)
def test_keyerror(self):
d = Dict()
with self.assertRaises(Keyerror):#另一种断言,期待抛出指定类型的Error
value = d['empty’]
def test_attrerror(self):
d = Dict()
with self.assertRaises(AttributeError):#如果value=d.empty,抛出的错误为AttributeError,则断言成功
value = d.empty
if __name__ == '__main__':
unittest.main() #所有的模块都有一个内置属性 __name__
#当我们在命令行运行该模块文件时,Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该模块时,if判断将失败,因此,这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。
文档测试
# -*- coding: utf-8 -*-
def fact(n):
'''
>>> fact(3)
6
>>> fact(1)
1
>>> fact(0)
Traceback(most recent call last):
… #"…"表示中间烦人的输出
ValueError
'''
if n < 1:
raise ValueError
if n == 1:
return 1
return n * fact(n - 1)
if __name__ == '__main__'
import doctest #Python内置的doctest模块可以直接提取注释中的代码并执行测试
doctest.testmod() #运行后什么输出也没有
#如果出现错误,程序就会报错,如:
**********************************************************************
File "mydict3.py", line 9, in __main__.fact
Failed example:
fact(0)
Exception raised:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/doctest.py", line 1321, in __run
compileflags, 1), test.globs)
File "<doctest __main__.fact[2]>", line 1, in <module>
fact(0)
File "mydict3.py", line 15, in fact
raise ValueError
ValueError
**********************************************************************
1 items had failures: #表示测试执行失败数,无失败时无输出
1 of 3 in __main__.fact
***Test Failed*** 1 failures.