1. Python 中的错误
在 Python 开发过程中,可能会遇到 3 种错误:
-
语法错误 (Syntax Error) :代码不符合 Python 语法规则。
- PyCharm 会给出红色的波浪线提示,运行会出现
SyntaxError。
python# 计算 a 跟 b 的和 a = 10 b = 5 print(a - b) # 5 - PyCharm 会给出红色的波浪线提示,运行会出现
-
逻辑错误 (Logic Error、Bug) :代码不符合业务逻辑。
- PyCharm 不会给出任何提示信息。
-
异常 (Exception) :操作不合理、Python 无法正常处理你的代码。
- Python 会抛出异常信息(比如
XxxError)。
pythoni = int('小码哥') print(i) print(10 / 0) - Python 会抛出异常信息(比如
2. 异常的特点
- 会直接导致程序终止运行。
python
print(1)
print(10 / 0)
print(2)
def test():
print(2)
print(10 / 0)
print(3)
print(1)
test()
print(4)
# 运行结果:
# 1
# ZeroDivisionError: division by zero
# 1
# 2
# ZeroDivisionError: division by zero
- 有时候来得猝不及防,把握不住。
python
age = int(input('请输入你的年龄:'))
print(age)
3. 异常的处理
在绝大部分情况下:
- 我们都不希望程序因为异常而终止运行。
- 我们更希望的是:
- 能够"把握住"异常
- 能够拦截(捕获)异常
- 根据不同的异常信息作出不同的处理
- 让程序保持正常运行
3.1 捕获异常的基本结构
try、except、else、finally 配合使用:
- try :包含可能会产生异常的代码。
- 一旦出现异常,会直接跳过
try中剩余未执行的代码。
- 一旦出现异常,会直接跳过
- except :当出现异常时,会执行
except中的代码。- 可以省略。
- else :当没有出现异常时,会执行
else中的代码。- 可以省略。只能在有 except 时使用。
- finally :不管是否出现异常,最终都会执行
finally中的代码。- 可以省略。
python
try:
代码1
except:
代码2
else:
代码3
finally:
代码4
综合示例:
python
try:
a = int(input('请输入第1个整数:'))
print(1)
b = int(input('请输入第2个整数:'))
print(2)
print(f'a除以b等于{a / b}')
print(3)
except ValueError:
print('输入的数据无法转成整数')
except ZeroDivisionError:
print('0不能作为被除数')
else:
print('很好,一切正常')
finally:
print('操作处理完毕')
print(4)
3.2 常见的内置异常类型
-
ZeroDivisionError :0 作为除数
pythonprint(10 / 0) # ZeroDivisionError: division by zero -
IndexError :索引超出正常范围
pythons = [11, 22, 33] print(s[5]) # IndexError: list index out of range -
NameError :无法找到名称(标识符)
pythonprint(age) # NameError: name 'age' is not defined -
KeyError :无法找到字典的 key
pythond = {'age': 18} print(d['name']) # KeyError: 'name' -
TypeError :使用了不恰当类型的对象
pythonprint('age is ' + 18) # TypeError: can only concatenate str (not "int") to str -
ValueError :虽然类型正确,但值不正确
pythonn = int('娃哈哈') # ValueError: invalid literal for int() with base 10: '娃哈哈' -
AttributeError :操作(访问、赋值等)属性失败
pythonclass Person: pass p = Person() print(p.age) # AttributeError: 'Person' object has no attribute 'age'
3.3 except 的高级用法
- 省略异常类型 :
except后面可以不写异常类型,表示支持所有的异常类型。
python
try:
a = int(input('请输入第1个整数:'))
b = int(input('请输入第2个整数:'))
print(f'a除以b等于{a / b}')
print([][10])
except ValueError:
print('输入的数据无法转成整数')
except ZeroDivisionError:
print('0不能作为被除数')
except:
print('其他异常')
- 捕获多个异常类型 :可以用元组同时表示多个异常类型。
python
try:
a = int(input('请输入第1个整数:'))
b = int(input('请输入第2个整数:'))
print(f'a除以b等于{a / b}')
except (ValueError, ZeroDivisionError):
print('出现异常')
- 接收异常对象 :可以用变量来接收异常对象(使用关键字
as)。
python
try:
n = int(input('请输入第1个整数:'))
print(n)
except BaseException as e:
print('出现异常', e)
try:
a = int(input('请输入第1个整数:'))
b = int(input('请输入第2个整数:'))
print(f'a除以b等于{a / b}')
except (ValueError, ZeroDivisionError) as e:
print('出现异常', e)
4. 异常的传播
- 一旦函数/方法中的代码出现了异常:
- 会直接跳过剩余的代码,函数/方法停止运行。
- 函数/方法中产生的异常,会向外传播到当初调用函数/方法的地方。
- 从异常的打印信息(
Traceback),可以很清晰地看出异常的传播轨迹。 - 异常会由内往外 传播出去,直到被拦截捕获或传播到全局作用域为止。
- 可以想象成是在寻求帮助,看看哪位大神能够收了(处理)这个异常。
- 当异常传播到全局作用域时,还没有被拦截捕获,就会导致程序终止运行,并将异常信息打印出来。
5. 抛出异常 (raise)
如果函数/方法的参数有问题,我们可以在函数/方法内部:
- 将不合理的值过滤掉。
- 抛出异常,告诉传递参数的人:你传递的参数有严重问题。
可以通过 raise 主动抛出异常:
raise后面跟上的必须是异常类型(BaseException类型或其子类类型)。
python
class Person:
def __init__(self):
self.__age = 1
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if age < 1: # 主动抛出异常
raise ValueError('age不能小于1')
if age > 120: # 主动抛出异常
raise ValueError('age不能大于120')
self.__age = age
try:
p = Person()
p.age = -10
print(p.age)
except ValueError as e:
print('出现了异常', e.args[0])
6. 自定义异常
为了让异常的类型、描述信息更加精准,可以考虑自定义异常。
- 一般建议 :自定义的异常类型,最终要继承自
Exception,而并不是BaseException。
python
class AgeError(Exception):
"""自定义的异常类型,表示age有问题"""
def __str__(self):
return f'{self.args[0]}:{self.args[1]}值不合理'
class Person:
def __init__(self):
self.__age = 1
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if age < 1: # 主动抛出异常
raise AgeError('age不能小于1', age)
if age > 120: # 主动抛出异常
raise AgeError('age不能大于120', age)
self.__age = age
try:
p = Person()
p.age = 300
print(p.age)
except AgeError as e:
print('出现了异常', e)