錯誤和異常
基本概念
錯誤
從軟件方面來說,錯誤是語法或是邏輯上的
- 語法錯誤指示軟件的結構上有錯誤,導致不能被解釋器解釋或編譯器無法編譯,這些錯誤必須在程序執行前糾正
- 邏輯錯誤可能是由於不完整或是不合法的輸入所致,還可能十邏輯無法生成、計算,或是輸出結果需要的無法執行
異常
當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
習題訓練:
家庭理財系統
記賬: