盘一盘 Python 系列特别篇 - 错误类型


本文含 5048 字,5 图表截屏

建议阅读 23 分钟

在公众号对话框回复 ERR

获取完整 Jupyter Notebook

0

引言

用 Python 写代码的流程(flow)可类比成三种演绎故事的形式,按顺序写、按条件写(if)、重复写(while, for),这些都叫做流程控制(control flow)。

  • 按顺序:一句一句写

  • 按条件:用 if 语句

  • 按重复:

    • 用 for 循环 - 当循环次数事先知道

    • 用 while 循环 - 当循环次数事先不知道

前提是代码运行不出错。如果出错了需要异常处理(exception handling)。处理异常或错误我们首先要知道它们的类型,才能有的放矢。这就是本贴的内容

本帖的内容如下:

  1. 从高层面了解三大错误类型

  2. 从具体层面了解常见错误类型

  3. 用面向对象编程的期权定价例子来说明

1

三类错误

程序中的错误(error)又称 bug,可以分为三大类:

  1. 语法错误(syntax error)

  2. 运行错误(runtime error)

  3. 逻辑错误(logical error)

先看下图的总结。

盘一盘 Python 系列特别篇 - 错误类型_第1张图片

语法错误

语法错误顾名思义就是没有遵循 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 会给你很详细的信息。

盘一盘 Python 系列特别篇 - 错误类型_第2张图片

下表是最齐全的错误层级表,每个错误类型都是 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 类来计算期权价格。

盘一盘 Python 系列特别篇 - 错误类型_第3张图片

创建一个看涨期权对象。

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!

你可能感兴趣的:(盘一盘 Python 系列特别篇 - 错误类型)