本文含 5048 字,5 图表截屏
建议阅读 23 分钟
在公众号对话框回复 ERR
获取完整 Jupyter Notebook
0
引言
用 Python 写代码的流程(flow)可类比成三种演绎故事的形式,按顺序写、按条件写(if)、重复写(while, for),这些都叫做流程控制(control flow)。
按顺序:一句一句写
按条件:用 if 语句
按重复:
用 for 循环 - 当循环次数事先知道
用 while 循环 - 当循环次数事先不知道
前提是代码运行不出错。如果出错了需要异常处理(exception handling)。处理异常或错误我们首先要知道它们的类型,才能有的放矢。这就是本贴的内容
本帖的内容如下:
从高层面了解三大错误类型
从具体层面了解常见错误类型
用面向对象编程的期权定价例子来说明
1
三类错误
程序中的错误(error)又称 bug,可以分为三大类:
语法错误(syntax error)
运行错误(runtime error)
逻辑错误(logical error)
先看下图的总结。
语法错误
语法错误顾名思义就是没有遵循 Python 语言的语法,在程序编译(compliation)时报错,而且容易识别。语法错误的常见例子有:
关键词拼错:print 打成 primt
函数最后没带冒号:def fun()
字符串最后没带引号:'error
括号不匹配:print(
缩进位置错误
等等。
分析上图具体的中文和 Python 代码的语法错误例子:
# 中文
昨天,送给我几本书。
# Python 代码
print('I love you)
那句中文缺少主语。
那句代码字符串没有引号。
运行错误
运行错误顾名思义是在程序运行时报错,不太容易识别。因为有些错误真的是你不运行根本不知道这里会出错,除非你过去有丰富的经验。运行错误的常见例子有:
分母为零
整数加字符串
使用没有定义的变量名
调用没有定义的函数
读取不存在的文件
等等。
分析上图具体的中文和 Python 代码的运行错误例子:
# 中文
我飞到月球去看风景。
# Python 代码
a = 10
b = 0
c = a/b
那句中文中的“我飞到月球去看风景”这句话没有语法错误,但是实操时根本不可行。
那句代码在做除法时中分母为零。
逻辑错误
逻辑错误在程序运行时根本不报错,只是产出不是想要的结果,因此很难识别。逻辑错误的常见例子有:
用错变量名
缩进错了整块代码
用错操作符优先级
用错布尔表达式
等等。
分析上图具体的中文和 Python 代码的逻辑错误例子:
# 中文
因为我满仓入市,所以股市一定涨。
# Python 代码
(a, b) = (10, 8)
print('The mean of a and b is', a+b/2)
那句中文虽然没任何语法错误,也可能发生(运气好赌对了),但是毫无逻辑。
那句代码想求 a 和 b 的均值,应该写成 (a+b)/2 但是写成 a+b/2。
2
具体错误
由下图所示,程序开始时有两种可能,1. 没报错,2. 报错。没报错时程序按照顺序、按照条件语句(if),按照循环语句(for, while)跑,如果报错了 Python 会给你很详细的信息。
下表是最齐全的错误层级表,每个错误类型都是 BaseException 的子类,而我们一般讲的异常处理是 Exception 下面的子类。
接下来我们就来看看常见的错误类型(下表粉色)。
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
由于代码太过于简单,几乎不需要额外解释。
SyntaxError
SyntaxError
print('字符串最后没有单引号)
File "", line 1
print('函数最后没有小括号'
^
SyntaxError: unexpected EOF while parsing
print('函数最后没有小括号'
File "", line 1
print('函数最后没有小括号'
^
SyntaxError: unexpected EOF while parsing
IndentationError
作为 SyntaxError 的子类,当缩进有问题时报错。
if True:
print('缩进有问题')
File "", line 2
print('缩进有问题')
^
IndentationError: expected an indented block
NameError
NameError
not_defined
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in
----> 1 not_defined
NameError: name 'not_defined' is not defined
UnboundLocalError
作为 SyntaxError 的子类,当局部变量被引用前没有赋值时报错。
def fun():
a = a + 1
fun()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
in
2 a = a + 1
3
----> 4 fun()
in fun()
1 def fun():
----> 2 a = a + 1
3
4 fun()
UnboundLocalError: local variable 'a' referenced before assignment
LookupError
IndexError
作为 LookupError 的子类,当列表的索引超出范围时报错(对元组也适用)。
l = [1, 2, 3]
l[1031]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
in
1 l = [1, 2, 3]
----> 2 l[1031]
IndexError: list index out of range
KeyError
作为 LookupError 的子类,当字典的键没有定义时报错。
d = {'code':'JD', 'price':42}
d['CEO']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
in
1 d = {'code':'JD', 'price':42}
----> 2 d['CEO']
KeyError: 'CEO'
ArithmeticError
ZeroDivisionError
作为 ArithmeticError 的子类,当做除法分母为零时报错。
1/0
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
in
----> 1 1/0
ZeroDivisionError: division by zero
OverflowError
作为 ArithmeticError 的子类,当运算结果太大而不能被表示时报错。
import math
math.exp(1031)
---------------------------------------------------------------------------
OverflowError Traceback (most recent call last)
in
1 import math
----> 2 math.exp(1031)
OverflowError: math range error
FloatingPointError
作为 ArithmeticError 的子类,以 numpy 里的操作为例,只有当把 seterr 中的参数 invalid 设置 'raise' 时,当给负数开根号时报错。
import numpy
numpy.seterr(invalid='raise')
numpy.sqrt(-1)
---------------------------------------------------------------------------
FloatingPointError Traceback (most recent call last)
in
1 import numpy
2 numpy.seterr(invalid='raise')
----> 3 numpy.sqrt(-1)
FloatingPointError: invalid value encountered in sqrt
TypeError
当某种操作不兼容两种不同类型的变量时报错(比如整数加字符串)。
1031 + '1031'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in
----> 1 1031 + '1031'
TypeError: unsupported operand type(s) for +: 'int' and 'str'
ValueError
当对函数传入无效的参数时报错。
int('@')
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
in
----> 1 int('@')
ValueError: invalid literal for int() with base 10: '@'
AttributeError
当对象没有某种属性而被调用时报错(比如整型对象没有 append 方法,这是列表对象里的方法)。
a = 1
a.append(2)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in
1 a = 1
----> 2 a.append(2)
AttributeError: 'int' object has no attribute 'append'
OSError
操作系统错误的类别。
FileNotFoundError
作为 OSError 的子类,当要打开的文件不存在时报错。
f = open('No Such File.txt')
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
in
----> 1 f = open('No Such File.txt')
FileNotFoundError: [Errno 2] No such file or directory: 'No Such File.txt'
ImportError
当导入模块下的函数失败时报错,比如 numpy 中 没有 sqr 方法,只有 sqrt 方法。
from numpy import sqr
---------------------------------------------------------------------------
ImportError Traceback (most recent call last)
in
----> 1 from numpy import sqr
ImportError: cannot import name 'sqr' from 'numpy' (C:\Users\oawsy\AppData\Local\Continuum\anaconda3\lib\site-packages\numpy\__init__.py)
ModuleNotFoundError
当导入模块不存在时报错,比如将 numpy 误打成 numqy。
import numqy
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
in
----> 1 import numqy
ModuleNotFoundError: No module named 'numqy'
StopIteration
当在迭代器中使用 next() 函数超出其所含元素个数的范围时报错。
i = iter([1,2,3])
i
print(next(i))
print(next(i))
print(next(i))
print(next(i))
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
in
2 print(next(i))
3 print(next(i))
----> 4 print(next(i))
StopIteration:
3
具体实例
以用 OOP 来编写 Black-Scholes 解析解为例。首先引用 numpy 和 scipy 包下的函数。
from numpy import log, sqrt, exp
from scipy import stats
编写以下 BSM 类来计算期权价格。
创建一个看涨期权对象。
opt = BSM('Call', 100, 105, 0.05, 0, 1, 0.2)
type(opt)
__main__.BSM
计算其期权价值,没有报错。
opt.value()
8.021352235143176
但是如果不小心的话,可能会犯以下错误。
比如忘记打反括号 - SyntaxError
opt.value(
File "", line 1
opt.value(
^
SyntaxError: unexpected EOF while parsing
比如把 value() 记成了 price() - AttributeError
opt.price()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in
----> 1 opt.price()
AttributeError: 'BSM' object has no attribute 'price'
比如把 opt 打成了 opt1 - NameError
opt1.value()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
in
----> 1 opt1.value()
NameError: name 'opt1' is not defined
比如将 S0 的数值 100 输成了 '100' - TypeError
opt1 = BSM('Call', '100', 105, 0.05, 0, 1, 0.2)
opt1.value()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in
1 opt1 = BSM('Call', '100', 105, 0.05, 0, 1, 0.2)
----> 2 opt1.value()
in value(self)
18
19 def value(self):
---> 20 return self.omega * (self.S0*exp(-self.q*self.T)*stats.norm.cdf(self.omega*self.d1()) \
21 - self.K*exp(-self.r*self.T)*stats.norm.cdf(self.omega*self.d2()))
TypeError: can't multiply sequence by non-int of type 'numpy.float64'
虽然以上的错误看起来有些弱智,但是用户在输入时指不定出什么幺蛾子,各种情况都要考虑进来保证程序不会崩。
4
总结
三大错误类型:语法错误(容易识别),运行错误(较易识别)和逻辑错误(不易识别)。
最常见错误类型:
SyntaxError(语法报错)
IndexError(序列中没有该索引)
KeyError(字典中没有该键)
ValueError(传入无效参数)
NameError(未声明/初始化对象 )
TypeError(对不同类型操作无效)
AttributeError(调用对象中不存在的属性)
本帖将用 Python 编程的错误做了个系统的梳理,then so what?有错就要改不是吗?这就是下帖的内容,异常处理(exception handling)。
Stay Tuned!