错误和异常
基本概念
错误
从软件方面来说,错误是语法或是逻辑上的
- 语法错误指示软件的结构上有错误,导致不能被解释器解释或编译器无法编译,这些错误必须在程序执行前纠正
- 逻辑错误可能是由于不完整或是不合法的输入所致,还可能十逻辑无法生成、计算,或是输出结果需要的无法执行
异常
当python检测到一个错误时,解释器就会指出当前流已经无法继续执行下去,这时候就出现了异常
异常是因为程序出现了错误而在正常控制流以外采取的行为
这个行为又分为两个阶段:
- 首先是引起异常发生的错误
- 然后是检测(和采取可能的措施)阶段
python中的异常
当程序运行时,因为遇到未解的错误而导致终止运行,便会出现tarceback消息,打印异常
异常 | 描述 |
NameError | 未声明/初始化对象 |
IndexError | 序列中没有此索引 |
SyntaxError | 语法错误 |
KeyboardInterrupt | 用户中断执行 |
EOFError | 没有内建输入,到达EOF标记 |
IOError | 输入/输出操作失败 |
检测和处理异常
try.except语句
定义了进行异常监控的一段代码,并且提供了处理异常的机制
try:
try_suite #监控这里的异常
except Exception[,reason]
except_sutie # 异常处理代码
>>> try:
... f = open('foo.txt')
... except IOError:
... print "No such file!"
...
No such file!
#!/usr/bin/env pytyon
import time
for i in range(1,11):
print i
try:
time.sleep(1)
except KeyboardInterrupt:
break/pass
print 'done'
带有多个except的try语句
可以把多个except语句连接在一起,处理一个try块中可能发生的多种异常
>>> try:
... data = int(raw_input('input a number: '))
... except KeyboardInterrupt:
... print 'user cancelled'
... except ValueError:
... print 'you must input a number!'
...
input a number: hello
you must input a number!
习题训练:简化除法判断
编写除法程序
1、提示用户输入一个数字作为除数
2、如果用户按下Ctrl+C或Ctrl+D则退出程序
3、如果用户输入非数字字符,提示用户应该输入数字
4、如果用户输入0,提示用户0不能作为除数
#!/usr/bin/env python
try:
num = int(raw_input('number: '))
result = 100 / num
except (KeyboardInterrupt,EOFError):
print "User cancelled"
except (ValueError,ZeroDivisionError):
print "Invalib input!"
捕获所有异常
如果出现的异常没有出现在指定要捕获的异常列表中,程序仍然会中断,可以使用
在异常继承的树结构中,BaseException实在最顶层的,所以使用它可以捕获任意类型的异常
>>> try:
... data = int(raw_input("input a number: "))
... except BaseException:
... print "\nsome error"
...
input a number: ^C
some error
异常参数
异常也可以有参数,异常引发后它会被传递给异常处理器
当异常被引发后参数是你为附加帮助信息传递给异常处理器的
>>> try:
... data = 10 / 0
... except ZeroDivisionError,e:
#异常名字(如果有多个用()括起来,用逗号隔开),第二个参数是存储异常原因的变量
... print "Error",e
...
Error integer division or modulo by zero
else子句
在try范围中没有异常被检测到时,执行else子句
在else范围中的任何代码运行前,try范围中的所有代码必须完全成功
>>> try:
... res = 10 / int(raw_input("Input a number: "))
... except BaseException,e:
... print "Error:",e
... else:
... print res
...
Input a number: 5
2
finally子句
finally子句是无论异常是否发生,是否捕捉都会执行的一段代码
如果打开文件后,因为发生异常导致文件没有关闭,可能会发生数据损坏。使用finally可以保证文件总是能正常关闭
>>> try:
... try:
... ccfile = open('carddata.txt','r')
... txns = ccfile.readlines()
... except IOError:
... log.write("no txns this month\n")
... finally:
... if ccfile:
... ccfile.close()
...
f = open('abc.txt','r')
try:
data = f.read()
finally:
f.close()
with子句
with语句是用来简化代码的
在将打开文件的操作放在with语句中,代码块结束后,文件将自动关闭
>>> with open("get-pip.py") as f:
... data = f.readlines()
...
>>> f.closed
True
触发异常
raise语句
要想引发异常,最简单的形式就是输入关键字raise,后面跟要引发的异常的名称
执行raise语句时,Python会创建指定的异常类的一个对象
raise语句还可指定对异常对象进行初始化的参数
>>> number = 3
>>> try:
... if number < 10:
... raise ValueError,'Number is too smalle.' #(必须是系统存在的异常,可自定义异常产生原因)
... except ValueError,e:
... print 'Error:',e
...
Error: Number is too smalle.
断言
断言是一句必须等价于布尔值为真的判定
此外,发生异常也意味着表达式为假
>>> number = 3
>>> try:
... assert number > 10,'number is too small'
... except AssertionError,e:
... print 'Error:',e
...
Error: number is too small
函数基础
创建函数
def语句
函数用def语句创建,语法如下:
def function_name(arguments):
"function_documentation_string"
function_body_sutie
标题行由def关键字,函数的名字,以及参数的集合(如果有的话)组成
def子句的剩余部分包括了一个虽然可选但是强烈推荐的文档字串,和必须的函数体
前向引用
函数不允许在函数未声明之前对其引用或者调用
def foo():
print "in foo"
bar()
foo() #报错,因为bar没有定义
---------------------------------------------
def foo():
print “in foo”
bar()
def bar():
print "in bar"
foo() # 正常执行,虽然bar的定义在foo定义后面
函数属性
函数属性是python另外一个使用了句点属性标识并拥有名字空间的领域
>>> def foo():
... 'foo() -- properly create doc string'
...
>>> foo.__doc__
'foo() -- properly create doc string'
>>> foo.version = 1.0
>>> foo.version
1.0
内部函数
在函数体内创建另外一个函数是完全合法的,这种函数叫做内部/内嵌函数
>>> def foo():
... def bar():
... print 'bar() is called'
... print 'foo() is called'
... bar()
...
>>> foo()
foo() is called
bar() is called
>>> bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'bar' is not defined
调用函数
函数操作符
使用一对圆括号()调用函数,如果没有圆括号,只是对函数的引用
任何输入的参数都必须放置在括号中
>>> def foo():
... print "hello world!"
...
>>> foo()
hello world!
>>> foo
<function foo at 0xb70a2bc4>
####引用:当你定义一个函数的时候,在内存中会分配一块内存,函数名指向这块内存,此为函数引用,当你在把foo 赋值给一个变量,这个变量的作用和foo是一样的。都是指向的一块内存地址
####调用:即执行函数体中的语句,将foo()赋值给b,foo() 函数中的函数体会被执行,b得到的是foo()函数的return结果,如果在函数中没有显示定义return,则返回none,此时b的值即为none。
关键字参数
关键字参数的概念仅仅针对函数调用
这种理念是让调用者通过函数调用中的参数名字来区分参数
这种规范允许参数确实或者不按顺序
>>> def getinfo(name,age):
... print "%s 's age is %s" % (name,age)
...
>>> getinfo(23,'bob')
23 's age is bob
>>> getinfo(age=23,name = 'bob')
bob 's age is 23
参数组
python允许程序员执行一个没有显示定义参数的函数
相应的方法是通过一个把元组(非关键字参数)或字典(关键字参数)作为参数组传递给函数
func(*tuple_grp_nonkv_args,**dict_grp_kw_args)
>>> def foo(*args):
... print args
...
>>> foo()
()
>>> foo(10)
(10,)
>>> foo(10,20,'hello')
(10, 20, 'hello')
>>> def add(x,y):
... return x + y
...
>>> add(10,20)
30
>>> add([10,20])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: add() takes exactly 2 arguments (1 given)
>>> add(*[10,20]) #“解开”
30
>>> def bar(**args):
... print args
...
>>> bar(name='bob',age=20)
{'age': 20, 'name': 'bob'}
>>> bar()
{}
>>> def func1(args,*non_kwagrs,**kwargs):
... print args
... print non_kwagrs
... print kwargs
...
>>> func1()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: func1() takes at least 1 argument (0 given)
>>> func1(10)
10
()
{}
>>> func1(10,20,30,name='bb')
10
(20, 30)
{'name': 'bb'}
习题训练:数学游戏
编写一个简单的加减法数学游戏
1、随机生成两个100以内的数字
2、随机选择家法或是减法
3、总是是用大的数字减去小的数字
4、如果用户打错三次,程序给出正确答案
#!/usr/bin/env python
import random
def add(x,y):
return x + y
def sub(x,y):
return x - y
def probe():
CMDs = {'+': add,'-': sub}
#CMDS = {'+': lambda x,y: x + y,'-': lambda x,y: x-y} #可以不用定义上面的两个函数,简化代码
nums = [random.randint(1,50) for i in range(2)]
nums.sort(reverse=True)
op = random.choice('+-')
answer = CMDs[op](*nums)
prompt = "%s %s %s = " % (nums[0],op,nums[1])
tries = 0
while tries < 3:
try:
result = int(raw_input(prompt))
except:
continue
if answer == result:
print "Very good!!!"
break
print "Wrong answer."
tries += 1
else:
print "\033[31;1m%s%s\033[0m" %(prompt,answer)
if __name__ == '__main__':
while True:
probe()
try:
yn = raw_input("Continue(y/n)? ").strip()[0]
except(KeyboardInterrupt,EOFError):
print "\nBye-Bye"
yn = 'n'
except IndexError:
continue
if yn in 'Nn':
break
匿名函数
lambda
python允许lambda关键字创造匿名函数
匿名是因为不需要以标准的def方式来声明
一个完成的lambda“语句”代表了一个表达式,这个表达式的定义体必须和声明放在同一行
lambda[arg1[,arg2,...argN]]:expression
>>> a = lambda x,y:x + y
>>> print a(3,4)
7
应用实例见上面的数学游戏程序
filter()函数
filter(func,seq):调用一个布尔函数func来迭代遍历每个序列中的元素;返回一个使func返回值为true的元素的序列
如果布尔函数比较简单,直接使用lambda匿名函数就显得非常方便了
>>> data = filter(lambda x: x % 2,[num for num in range(10)])
>>> print data
[1, 3, 5, 7, 9]
map()函数
reduce()函数
reduce(func,seq[,init]):将二元函数作用于seq序列的元素,每次携带一对(先前的结果以及下一个序列元素),连续的将现有的结果和下一个给值作用在获得的随后的结果上,最后减少序列为一个单一的返回值
>>> data = reduce(lambda x,y: x + y,range(1,6))
>>> print data
15
习题训练:
家庭理财系统
记账: