错误
语法错误(syntax errors)
>>> for i in range(10) File "<stdin>", line 1 for i in range(10) ^ SyntaxError: invalid syntax
上面那句话因为缺少冒号:
,导致解释器无法解释,于是报错。这个报错行为是由python的语法分析器完成的,并且检测到了错误所在文件和行号(File "<stdin>", line 1
),还以向上箭头^
标识错误位置(后面缺少:
),最后显示错误类型。
逻辑错误
逻辑错误可能会由于不完整或者不合法的输入导致,也可能是无法生成、计算等,或者是其它逻辑问题。
当python检测到一个错误时,解释器就无法继续执行下去,于是抛出异常。
异常
>>> 1/0 Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: integer division or modulo by zero
当python抛出异常的时候,首先有“跟踪记录(Traceback)”,还可以给它取一个更优雅的名字“回溯”。后面显示异常的详细信息。异常所在位置(文件、行、在某个模块)。
NameError
python中变量需要初始化,即要赋值。虽然不需要像某些语言那样声明,但是要赋值先。因为变量相当于一个标签,要把它贴到对象上才有意义。
ZeroDivisionError
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
貌似这样简单的错误时不会出现的,但在实际情境中,可能没有这么容易识别,所以,依然要小心为妙。
SyntaxError
>>> for i in range(10)
File "<stdin>", line 1
for i in range(10)
^
SyntaxError: invalid syntax
这种错误发生在python代码编译的时候,当编译到这一句时,解释器不能讲代码转化为python字节码,就报错。只有改正才能继续。所以,它是在程序运行之前就会出现的(如果有错)。现在有不少编辑器都有语法校验功能,在你写代码的时候就能显示出语法的正误,这多少会对编程者有帮助。
IndexError
>>> a = [1,2,3]
>>> a[4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> d = {"python":"itdiffer.com"}
>>> d["java"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: ‘java‘
这两个都属于“鸡蛋里面挑骨头”类型,一定得报错了。不过在编程实践中,特别是循环的时候,常常由于循环条件设置不合理出现这种类型的错误。
IOError
>>> f = open("foo")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: ‘foo‘
如果你确认有文件,就一定要把路径写正确,因为你并没有告诉python对你的computer进行全身搜索,所以,python会按照你指定位置去找,找不到就异常。
AttributeError
>>> class A(object): pass
...
>>> a = A()
>>> a.foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: ‘A‘ object has no attribute ‘foo‘
属性不存在。这种错误前面多次见到。
其实,python内建的异常也不仅仅上面几个,上面只是列出常见的异常中的几个。比如还有:
>>> range("aaa")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: range() integer end argument expected, got str.
总之,如果读者在调试程序的时候遇到了异常,不要慌张,这是好事情,是python在帮助你修改错误。只要认真阅读异常信息,再用dir()
,help()
或者官方网站文档、google等来协助,一定能解决问题。
处理异常
在一段程序中,为了能够让程序健壮,必须要处理异常。
处理异常的方式之一,使用try...except...
。
只看try和except部分,如果没有异常发生,except子句在try语句执行之后被忽略;如果try子句中有异常可,该部分的其它语句被忽略,直接跳到except部分,执行其后面指定的异常类型及其子句。
except后面也可以没有任何异常类型,即无异常参数。如果这样,不论try部分发生什么异常,都会执行except。
处理多个异常
处理多个异常,并不是因为同时报出多个异常。程序在运行中,只要遇到一个异常就会有反应,所以,每次捕获到的异常一定是一个。所谓处理多个异常的意思是可以容许捕获不同的异常,有不同的except子句处理。
#!/usr/bin/env python # coding=utf-8 while 1: print "this is a division program." c = raw_input("input ‘c‘ continue, otherwise logout:") if c == ‘c‘: a = raw_input("first number:") b = raw_input("second number:") try: print float(a)/float(b) print "*************************" except ZeroDivisionError: print "The second number can‘t be zero!" print "*************************" except ValueError: print "please input number." print "************************" else: break
将上节的一个程序进行修改,增加了一个except子句,目的是如果用户输入的不是数字时,捕获并处理这个异常。测试如下:
$ python 21701.py
this is a division program.
input ‘c‘ continue, otherwise logout:c
first number:3
second number:"hello" #输入了一个不是数字的东西
please input number. #对照上面的程序,捕获并处理了这个异常
************************
this is a division program.
input ‘c‘ continue, otherwise logout:c
first number:4
second number:0
The second number can‘t be zero!
*************************
this is a division program.
input ‘c‘ continue, otherwise logout:4
$
如果有多个except,在try里面如果有一个异常,就转到相应的except子句,其它的忽略。如果except没有相应的异常,该异常也会抛出,不过这是程序就要中止了,因为异常“浮出”程序顶部。
除了用多个except之外,还可以在一个except后面放多个异常参数,比如上面的程序,可以将except部分修改为:
except (ZeroDivisionError, ValueError):
print "please input rightly."
print "********************"
运行的结果就是:
$ python 21701.py
this is a division program.
input ‘c‘ continue, otherwise logout:c
first number:2
second number:0 #捕获异常
please input rightly.
********************
this is a division program.
input ‘c‘ continue, otherwise logout:c
first number:3
second number:a #异常
please input rightly.
********************
this is a division program.
input ‘c‘ continue, otherwise logout:d
$
需要注意的是,except后面如果是多个参数,一定要用圆括号包裹起来。否则,后果自负。
突然有一种想法,在对异常的处理中,前面都是自己写一个提示语,发现自己写的不如内置的异常错误提示更好。希望把它打印出来。但是程序还能不能中断。python提供了一种方式,将上面代码修改如下:
while 1:
print "this is a division program."
c = raw_input("input ‘c‘ continue, otherwise logout:")
if c == ‘c‘:
a = raw_input("first number:")
b = raw_input("second number:")
try:
print float(a)/float(b)
print "*************************"
except (ZeroDivisionError, ValueError), e:
print e
print "********************"
else:
break
运行一下,看看提示信息。
$ python 21702.py
this is a division program.
input ‘c‘ continue, otherwise logout:c
first number:2
second number:a #异常
could not convert string to float: a
********************
this is a division program.
input ‘c‘ continue, otherwise logout:c
first number:2
second number:0 #异常
float division by zero
********************
this is a division program.
input ‘c‘ continue, otherwise logout:d
$
在python3.x中,常常这样写:
except (ZeroDivisionError, ValueError) as e:
以上程序中,之处理了两个异常,还可能有更多的异常呢?如果要处理,怎么办?可以这样:execpt:
或者except Exception, e
,后面什么参数也不写就好了。
else子句
有了try...except...
,在一般情况下是够用的,但总有不一般的时候出现,所以,就增加了一个else子句。其实,人类的自然语言何尝不是如此呢?总要根据需要添加不少东西。
>>> try:
... print "I am try"
... except:
... print "I am except"
... else:
... print "I am else"
...
I am try
I am else
这段演示,能够帮助读者理解else的执行特点。如果执行了try,则except被忽略,但是else被执行。
>>> try:
... print 1/0
... except:
... print "I am except"
... else:
... print "I am else"
...
I am except
这时候else就不被执行了。
#!/usr/bin/env python # coding=utf-8 while 1: try: x = raw_input("the first number:") y = raw_input("the second number:") r = float(x)/float(y) print r except Exception, e: print e print "try again." else: break
先看运行结果:
$ python 21703.py the first number:2 the second number:0 #异常,执行except float division by zero try again. #循环 the first number:2 the second number:a #异常 could not convert string to float: a try again. the first number:4 the second number:2 #正常,执行try 2.0 #然后else:break,退出程序 $
需要对程序中的except简单说明,这次没有像前面那样写,而是except Exception, e
,意思是不管什么异常,这里都会捕获,并且传给变量e,然后用print e
把异常信息打印出来。
finally
finally子句,一听这个名字,就感觉它是做善后工作的。的确如此,如果有了finally,不管前面执行的是try,还是except,它都要执行。因此一种说法是用finally用来在可能的异常后进行清理。比如:
>>> x = 10 >>> try: ... x = 1/0 ... except Exception, e: ... print e ... finally: ... print "del x" ... del x ... integer division or modulo by zero del x
看一看x是否被删除?
>>> x Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name ‘x‘ is not defined
assert
assert,翻译过来是“断言”之意。assert是一句等价于布尔真的判定,发生异常就意味着表达式为假。
assert的应用情景就有点像汉语的意思一样,当程序运行到某个节点的时候,就断定某个变量的值必然是什么,或者对象必然拥有某个属性等,简单说就是断定什么东西必然是什么,如果不是,就抛出错误。
!/usr/bin/env python # coding=utf-8 class Account(object): def __init__(self, number): self.number = number self.balance = 0 def deposit(self, amount): assert amount > 0 self.balance += balance def withdraw(self, amount): assert amount > 0 if amount <= self.balance: self.balance -= amount else: print "balance is not enough."
上面的程序中,deposit()和withdraw()方法的参数amount值必须是大于零的,这里就用断言,如果不满足条件就会报错。比如这样来运行:
if __name__ == "__main__": a = Account(1000) a.deposit(-10)
出现的结果是:
$ python 21801.py Traceback (most recent call last): File "21801.py", line 22, in <module> a.deposit(-10) File "21801.py", line 10, in deposit assert amount > 0 AssertionError
这就是断言assert的引用。什么是使用断言的最佳时机?有文章做了总结:
如果没有特别的目的,断言应该用于如下情况:
- 防御性的编程
- 运行时对程序逻辑的检测
- 合约性检查(比如前置条件,后置条件)
- 程序中的常量
- 检查文档