拆解與捕捉

既然可迭代待物可以進行拆解,那他一定也有捕捉 (或稱集成) 的手法,這通常也會運動到星號 *

平行賦值中的捕捉

不廢話,我們來看一個例子,一樣我們有個描述個人資料的文件 text.txt

Bill is 28 years old and he is a magician
Cynthia is 18 years old and she is a student
dokelung is 26 years old and he is a software engineer

我們一樣想要從中抽取出姓名和年齡,根據前面的做法我們會:

age_dic = {}

with open('text.txt') as fp:
    for line in fp:
        name, _, age, _, _, _, _, _, _, _ = line.strip().split() # 一個很醜的拆解
        age_dic[name] = int(age)

實際運行時由於第三個句子的字數並非十個,還會出現錯誤:

ValueError: too many values to unpack (expected 10)

這種時候與其使用拆解,還不如老老實實地用索引值取出:

age_dic = {}

with open('text.txt') as fp:
    for line in fp:
         items = line.strip().split()
         name = items[0]
         age = items[2]
         age_dic[name] = int(age)

如果我們仍然想要使用拆解的手法該如何呢?我們可以利用星號來捕捉任意數量的項目:

age_dic = {}

with open('text.txt') as fp:
    for line in fp:
        name, _, age, *rest = line.strip().split()
        print(name, age, rest) # 讓我們來看看 rest 捕捉到什麼!
        age_dic[name] = int(age)

輸出:

Bill 28 ['years', 'old', 'and', 'he', 'is', 'a', 'magician']
Cynthia 18 ['years', 'old', 'and', 'she', 'is', 'a', 'student']
dokelung 26 ['years', 'old', 'and', 'he', 'is', 'a', 'software', 'engineer']

我們會發現加上星號的變數將會捕捉所有沒有對應變數的項目,在這個例子中,rest 前面有三個變數後面有零個變數,所以他會捕捉第四個變數到最後一個變數並將其集成為一個清單。值得注意的是,每次的平行賦值只能夠有一個變數使用 * 捕捉,但可以是任何一個變數。

函式參數的捕捉

如同拆解可以用於函式的引數,捕捉也可以用在函式的參數,我們來看個例子。假設我們今天要撰寫求取平方和的函式,且我們想要同時支援不同數量的參數形式:

def sum_of_square2(a, b):
    return a**2 + b**2

def sum_of_square3(a, b, c):
    return a**2 + b**2 + c**2

# 沒完沒了...

由於 Python 不支援 同名異式的多載 (overloading),要完成這個任務,我們只得寫出若干個名稱不同的版本,這還不打緊,無窮無盡的參數數量可能會讓我們筋疲力竭。此時,我們可以利用星號 * 幫我們完成任務:

def sum_of_square(*n):
    print(n) # 讓我們來看看 n 是什麼
    result = 0
    for k in n:
        result += k**2
    return result

# 或是使用 generator expression 的版本 (在本書後面的章節會討論到)
def sum_of_square(*n):
    print(n) # 讓我們來看看 n 是什麼
    return sum(k**2 for k in n)

測試:

>>> sum_of_square(1, 2, 3)
(1, 2, 3) # print 印出來的元組
14 # 平方和

利用星號我們將所有傳入的引數都用 n 這個參數捕捉了,並集成一個元組 (在一般的平行賦值中,* 會集成清單,但在函式的參數捕捉上,會集成元組),如此一來我們可以只定義一個函式就能接收任意數量的引數了,這就是 Python 完成多載的方式,這就是迭代之美!

這邊還有一點要注意,就是若將星號使用在函式的參數上,則其位置是有限制的,單星號 * 只能使用在最後一個位置參數,在其後的參數只能是關鍵字參數。

其實關於拆解和集成,還有非迭代式映射型的做法,用以拆解和集成字典,不過由於不屬於迭代要討論的範疇,我們就不多做討論,有興趣的讀者可以參閱任何一個足夠完整的 Python 教學,一定都有提及。

results matching ""

    No results matching ""