Python 3 Tutorial 第四堂(1)資料處理函式


在 Python 的 __builtin__ 模組中有一些函式,不需要 import 也可以使用,有幾個簡單好用的資料處理函式值得介紹:

  • range(start, stop[, step])
  • zip([iterable, ...])
  • enumerate(sequence, start=0)
  • filter
  • map

rangezipenumerate

要瞭解這些資料處理函式如何使用,最好的方式就是從練習開始 …

練習 6:使用 rangezipenumerate

在 Python 中,如果想使用 for in 語法來迭代 list,而且希望取得索引資訊,該如何進行呢?例如,如果有個 list = ['Justin', 'caterpillar', 'openhome'],想要有以下的顯示結果:

0, Justin
1, caterpillar
2, openhome 

先給個小提示,程式基本上可以長這樣:

names = ['Justin', 'caterpillar', 'openhome']
for ______ in ______:
    print('{0}, {1}'.format(______))

試著想想,如果使用分別使用 rangezipenumerate,那麼程式中三個空白處怎麼寫呢?別忘了,查看一下 rangezipenumerate 的文件,他們都在 __builtin__ 模組的文件中。

別忘了,你可以在 Lab 檔案的 solutions 目錄找到解答。

filtermap

如果你有個 lt = ['Justin', 'caterpillar', 'openhome'],現在打算過濾出字串長度大於 6 的元素,按照命式式的寫法可能是:

lt = ['Justin', 'caterpillar', 'openhome']
result = []
for ele in lt:
    if len(ele) > 6:
        result.append(ele)
print(result)

仔細想想,這類過濾某清單而後取得另一清單的流程,你寫過幾次呢?每次其實只有過濾的條件不同,其他流程都是相同的,如果將重複的流程提取出來,封裝為函式如何呢?

def filter_lt(predicate, lt):
    result = []
    for ele in lt:
        if predicate(ele):
            result.append(ele)
    return result

lt = ['Justin', 'caterpillar', 'openhome']
print(filter_lt(lambda ele: len(ele) > 6, lt))
print(filter_lt(lambda ele: 'i' in ele, lt))

可以看到,將重複的流程提取出來後,你就可以呼叫函式,然後每次給予不同的 lambda 來設定過濾條件。類似地,如果你想將 lt 的元素全部轉為大寫後傳回新的清單,按照命令式的寫法,可能會是:

lt = ['Justin', 'caterpillar', 'openhome']

result = []
for ele in lt:
    result.append(ele.upper())
print(result)

同樣地,將清單元素轉換為另一組清單,也是你寫過無數次的操作,何不將其中重複的流程抽取出來呢?

def map_lt(mapper, lt):
    result = []
    for ele in lt:
        result.append(mapper(ele))
    return result

lt = ['Justin', 'caterpillar', 'openhome']
print(map_lt(lambda ele: ele.upper(), lt))
print(map_lt(lambda ele: len(ele), lt))

可以看到,將重複的流程提取出來後,你就可以呼叫函式,然後每次給予不同的 lambda 來設定對應轉換的方式。實際上,Python 就內建有 filtermap 函式可以直接取用。例如:

t = ['Justin', 'caterpillar', 'openhome']
print(list(filter(lambda ele: len(ele) > 6, lt)))
print(list(filter(lambda ele: 'i' in ele, lt)))
print(list(map(lambda ele: ele.upper(), lt)))
print(list(map(lambda ele: len(ele), lt)))

要注意的是,在 Python 3.x 中,mapfilter 傳回的實例並不是 list,而是個產生器(Generator),這是為了效率,因為實際上還沒有真正進行過濾或轉換的動作,只有在真正需要結果元素時,才會執行相對應的操作,這稱之為惰性求值(Lazy evaluation),想想看,如果你想依某條件過濾清單後取得結果的前三個元素,惰性求值下,實際上就只要過濾出三個元素就可以了,若原始清單很長,這種惰性的特性對效率會很有助益。

因為 mapfilter 實際傳回產生器,為了要能取得最後的清單並顯示,最後是使用了 list 函式,從產生器逐一取得結果元素,然後組成一個 list 傳回。