2.4 Python 異常處理與函數

本節要點

  • 瞭解 Python 中異常處理的概念
  • 重點掌握 Python 中函數的概念、用法

課前準備

檢查pip命令

在 cmd 輸入 pip 或終端中輸入 pip 或 pip3 ,會輸出 pip 的使用方法

課程內容

異常處理

1. 程式常見的錯誤

什麼是異常?

  • 異常即是一個事件,該事件會在程式執行過程中發生,影響了程式的正常執行。
  • 一般情況下,在 Python 無法正常處理程式時就會發生一個異常。
  • 異常是 Python 對象,表示一個錯誤。
  • 當 Python 腳本發生異常時我們需要捕獲處理它,否則程式會終止執行。

異常名稱

描述

BaseException

所有異常的基類

SystemExit

解釋器請求退出

KeyboardInterrupt

使用者中斷執行(通常是輸入^C)

Exception

常規錯誤的基類

StopIteration

迭代器沒有更多的值

GeneratorExit

生成器(generator)發生異常來通知退出

StandardError

所有的內建標準異常的基類

ArithmeticError

所有數值計算錯誤的基類

FloatingPointError

浮點計算錯誤

OverflowError

數值運算超出最大限制

ZeroDivisionError

除(或取模)零 (所有資料類型)

AssertionError

斷言語句失敗

AttributeError

對象沒有這個屬性

EOFError

沒有內建輸入,到達 EOF 標記

EnvironmentError

操作系統錯誤的基類

IOError

輸入/輸出操作失敗

OSError

操作系統錯誤

WindowsError

系統調用失敗

ImportError

匯入模塊/對象失敗

LookupError

無效資料查詢的基類

IndexError

序列中沒有此索引(index)

KeyError

映射中沒有這個鍵

MemoryError

內存溢出錯誤(對於Python 解釋器不是致命的)

NameError

未聲明/初始化對象 (沒有屬性)

UnboundLocalError

造訪未初始化的本地變數

ReferenceError

弱引用(Weak reference)試圖造訪已經垃圾回收了的對象

RuntimeError

一般的運行時錯誤

NotImplementedError

尚未實現的方法

SyntaxError

Python 語法錯誤

IndentationError

縮進錯誤

TabError

Tab 和空格混用

SystemError

一般的解釋器系統錯誤

TypeError

對類型無效的操作

ValueError

傳入無效的參數

UnicodeError

Unicode 相關的錯誤

UnicodeDecodeError

Unicode 解碼時的錯誤

UnicodeEncodeError

Unicode 編碼時錯誤

UnicodeTranslateError

Unicode 轉換時錯誤

Warning

警告的基類

DeprecationWarning

關於被棄用的特徵的警告

FutureWarning

關於構造將來語義會有改變的警告

OverflowWarning

舊的關於自動提升為長整型(long)的警告

PendingDeprecationWarning

關於特性將會被廢棄的警告

RuntimeWarning

可疑的運行時行為(runtime behavior)的警告

SyntaxWarning

可疑的語法的警告

UserWarning

使用者程式碼生成的警告

2. 如何判斷和捕捉異常

捕捉異常可以使用 try/except 語句。

try/except 語句用來檢測 try 語句塊中的錯誤,從而讓 except 語句捕獲異常資訊並處理。 如果你不想在異常發生時結束你的程式,只需在 try 裏捕獲它。

以下為簡單的 try…except…else 的語法:

try:
<語句>        #運行別的程式碼
except <名字>:
<語句>        #如果在try部份引發了'name'異常
except <名字>,<資料>:
<語句>        #如果引發了'name'異常,獲得附加的資料
else:
<語句>        #如果沒有異常發生
  • try 的工作原理是,當開始一個 try 語句後,python 就在當前程式的上下文中作標記,這樣當異常出現時就可以回到這裡,try 子句先執行,接下來會發生什麼依賴於執行時是否出現異常。
  • 如果當 try 後的語句執行時發生異常,python 就跳回到 try 並執行第一個匹配該異常的 except 子句,異常處理完畢,控制流就透過整個 try 語句(除非在處理異常時又引發新的異常)。
  • 如果在 try 後的語句裏發生了異常,卻沒有匹配的 except 子句,異常將被遞交到上層的 try,或者到程式的最上層(這樣將結束程式,並列印預設的出錯資訊)。
  • 如果在 try 子句執行時沒有發生異常,python 將執行 else 語句後的語句(如果有 else 的話),然後控制流透過整個 try 語句。

下面是簡單的例子,它打開一個文件,在該文件中的內容寫入內容,且並未發生異常:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
try:
    fh = open("testfile", "w")
    fh.write("這是一個測試文件,用於測試異常!!")
except IOError:
    print ("Error: 沒有找到文件或讀取文件失敗")
else:
    print ("內容寫入文件成功")
    fh.close()

下面是簡單的例子,它打開一個文件,在該文件中的內容寫入內容,但文件沒有寫入權限,發生了異常:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
try:
    fh = open("testfile", "w")
    fh.write("這是一個測試文件,用於測試異常!!")
except IOError:
    print ("Error: 沒有找到文件或讀取文件失敗")
else:
    print ("內容寫入文件成功")
    fh.close()

函數

1. 函數的概念

  • 你可以定義一個由自己想要功能的函數,以下是簡單的規則:
  • 函數程式碼塊以 def 關鍵詞開頭,後接函數標識符名稱和圓括號 ()
  • 任何傳入參數和自變數必須放在圓括號中間。圓括號之間可以用於定義參數;
  • 函數的第一行語句可以選擇性地使用文件字串—用於存放函數說明;
  • 函數內容以冒號起始,並且縮進;
  • return [表達式]結束函數,選擇性地返回一個值給調用方。不帶表達式的 return 相當於返回 None。

建立一個函數

在 Python 中,定義一個函數要使用 def 語句:

def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x
print(my_abs(-99))

函數可以返回多個值嗎?

import math

def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny
x, y = move(100, 100, 60, math.pi / 6)
print(x, y)

形參與實參

username 就是一個形參,小明是我在調用函數時傳入的一個實參,它的值被儲存在形參 username 中:

局部變數與全局變數

# -*- coding: UTF-8 -*-
total = 0 # 這是一個全局變數
# 可寫函數說明
def sum( arg1, arg2 ):
   #返回2個參數的和."
   total = arg1 + arg2 # total在這裡是局部變數.
   print ("函數內是局部變數 : ", total)
   return total
#調用sum函數
sum( 10, 20 )
print("函數外是全局變數 : ", total)

在函數中聲明變數為全局變數:

#在函數裏定義全局變數
a=0
def plus_a():
  global a
  a +=1

關鍵詞參數、預設參數、收集參數

1. 位置參數

我們先寫一個計算 x^2 的函數:

def power(x):
    return x * x

對於 power(x) 函數,參數x就是一個位置參數。 當我們調用 power 函數時,必須傳入有且僅有的一個參數x:

>>> power(5)
25
>>> power(15)
225

現在,如果我們要計算 x3 怎麼辦?可以再定義一個 power3 函數,但是如果要計算 x4、x5…… 怎麼辦?我們不可能定義無限多個函數。 你也許想到了,可以把 power(x) 修改為 power(x, n),用來計算 xn:

def power(x, n):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

對於這個修改後的 power(x, n) 函數,可以計算任意 n 次方:

>>> power(5, 2)
25
>>> power(5, 3)
125

修改後的 power(x, n) 函數有兩個參數:x 和 n,這兩個參數都是位置參數,調用函數時,傳入的兩個值按照位置順序依次賦給參數 x 和 n。

2. 預設參數

新的 power(x, n) 函數定義沒有問題,但是,舊的調用程式碼失敗了,原因是我們增加了一個參數,導致舊的程式碼因為缺少一個參數而無法正常調用:

>>> power(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: power() missing 1 required positional argument: 'n'

Python 的錯誤資訊很明確:調用函數 power() 缺少了一個位置參數 n。 這個時候,預設參數就排上用場了。由於我們經常計算 x2,所以,完全可以把第二個參數 n 的預設值設定為 2:

def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

這樣,當我們調用 power(5) 時,相當於調用 power(5, 2):

>>> power(5)
25
>>> power(5, 2)
25

而對於 n > 2 的其他情況,就必須明確地傳入 n,比如 power(5, 3)。

從上面的例子可以看出,預設參數可以簡化函數的調用。設定預設參數時,有幾點要注意:

  • 一是必選參數在前,預設參數在後,否則 Python 的解釋器會報
  • 二是如何設定預設參數。

當函數有多個參數時,把變化大的參數放前面,變化小的參數放後面。變化小的參數就可以作為預設參數。

使用預設參數有什麼好處?最大的好處是能降低調用函數的難度。

3. 可變參數

在 Python 函數中,還可以定義可變參數。顧名思義,可變參數就是傳入的參數個數是可變的,可以是 1 個、2 個到任意個,還可以是 0 個。

我們以數學題為例子,給定一組數字 a,b,c……,請計算 a2 + b2 + c2 + ……。

要定義出這個函數,我們必須確定輸入的參數。由於參數個數不確定,我們首先想到可以把 a,b,c…… 作為一個 list 或 tuple 傳進來,這樣,函數可以定義如下:

def calc(numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

但是調用的時候,需要先組裝出一個 list 或 tuple:

>>> calc([1, 2, 3])
14
>>> calc((1, 3, 5, 7))
84

所以,我們把函數的參數改為可變參數:

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

定義可變參數和定義一個 list 或 tuple 參數相比,僅僅在參數前面加了一個 * 號。在函數內部,參數numbers接收到的是一個tuple,因此,函數程式碼完全不變。但是,調用該函數時,可以傳入任意個參數,包括 0 個參數:

>>> calc(1, 2)
5
>>> calc()
0

如果已經有一個 list 或者 tuple,要調用一個可變參數怎麼辦?可以這樣做:

>>> nums = [1, 2, 3]
>>> calc(nums[0], nums[1], nums[2])
14

這種寫法當然是可行的,問題是太繁瑣,所以Python允許你在list或tuple前面加一個*號,把list或tuple的元素變成可變參數傳進去:

>>> nums = [1, 2, 3]
>>> calc(*nums)
14

附註

nums 表示把 nums 這個 list 的所有元素作為可變參數傳進去。這種寫法相當有用,而且很常見。

4. 關鍵字參數

可變參數允許你傳入 0 個或任意個參數,這些可變參數在函數調用時自動組裝為一個 tuple。而關鍵字參數允許你傳入 0 個或任意個含參數名的參數,這些關鍵字參數在函數內部自動組裝為一個dict。請看範例:

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

函數 person 除了必選參數 name 和 age 外,還接受關鍵字參數 kw。在調用該函數時,可以只傳入必選參數:

>>> person('Michael', 30)
name: Michael age: 30 other: {}
也可以传入任意个数的关键字参数:
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

關鍵字參數有什麼用?它可以擴展函數的功能。比如,在 person 函數裏,我們保證能接收到 name 和 age 這兩個參數,但是,如果調用者願意提供更多的參數,我們也能收到。試想你正在做一個使用者註冊的功能,除了使用者名和年齡是必填項外,其他都是可選項,利用關鍵字參數來定義這個函數就能滿足註冊的需求。

和可變參數類似,也可以先組裝出一個 dict,然後,把該 dict 轉換為關鍵字參數傳進去:

>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, city=extra['city'], job=extra['job'])
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

當然,上面複雜的調用可以用簡化的寫法:

>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

extra 表示把該變數的所有 key-value 用關鍵字參數傳入到函數的 kw 參數,kw 將獲得一個 dict,注意 kw 獲得的 dict 是extra 的一份拷貝,對 kw 的改動不會影響到函數外的 extra。

這項資訊有幫助嗎?
需要更多協助嗎?聯繋技術支援。