lambda 和 iterable

Lambda 表達式

你可使用 Lambda 表達式建立匿名函數,即沒有名稱的函數。lambda 表達式很是適合快速建立在代碼中之後不會用到的函數。尤爲對高階函數或將其餘函數做爲參數的函數來講,很是實用。python

咱們可使用 lambda 表達式將如下函數less

def multiply(x, y):
    return x * y

簡寫爲:函數

double = lambda x, y: x * y測試

Lambda 函數的組成部分

  1. 關鍵字 lambda 表示這是一個 lambda 表達式。
  2. lambda 以後是該匿名函數的一個或多個參數(用英文逗號分隔),而後是一個英文冒號 :。和函數類似,lambda 表達式中的參數名稱是隨意的。
  3. 最後一部分是被評估並在該函數中返回的表達式,和你可能會在函數中看到的 return 語句很像。

練習:Lambda 與 Map

map() 是一個高階內置函數,接受函數和可迭代對象做爲輸入,並返回一個將該函數應用到可迭代對象的每一個元素的迭代器。下面的代碼使用 map() 計算 numbers 中每一個列表的均值,並建立列表 averages。測試運行這段代碼,看看結果如何。spa

經過將 mean 函數替換爲在 map() 的調用中定義的 lambda 表達式,重寫這段代碼,使代碼更簡練。code

numbers = [
              [34, 63, 88, 71, 29],
              [90, 78, 51, 27, 45],
              [63, 37, 85, 46, 22],
              [51, 22, 34, 11, 18]
           ]

def mean(num_list):
    return sum(num_list) / len(num_list)

averages = list(map(mean, numbers))
print(averages)

我這麼改:orm

mean = lambda num_list: sum(num_list) / len(num_list)
averages = list(map(mean, numbers))

但其實能夠直接將lambda整個寫到map參數中averages = list(map(lambda x: sum(x) / len(x), numbers)) 對象

練習:Lambda 與 Filter

filter() 是一個高階內置函數,接受函數和可迭代對象做爲輸入,並返回一個由可迭代對象中的特定元素(該函數針對該元素會返回 True)組成的迭代器。下面的代碼使用 filter()cities 中獲取長度少於 10 個字符的名稱以建立列表 short_cities。測試運行這段代碼,看看結果如何。ip

經過將 is_short 函數替換爲在 filter() 的調用中定義的 lambda 表達式,重寫這段代碼,使代碼更簡練。內存

cities = ["New York City", "Los Angeles", "Chicago", "Mountain View", "Denver", "Boston"]

def is_short(name):
    return len(name) < 10

short_cities = list(filter(is_short, cities))
print(short_cities)

改成 short_cities = list(filter(lambda x: len(x) < 10, cities))

迭代器和生成器

迭代器是每次能夠返回一個對象元素的對象,例如返回一個列表。咱們到目前爲止使用的不少內置函數(例如 enumerate)都會返回一個迭代器。

迭代器是一種表示數據流的對象。這與列表不一樣,列表是可迭代對象,但不是迭代器,由於它不是數據流。

生成器是使用函數建立迭代器的簡單方式。也可使用類定義迭代器,更多詳情請參閱此處。

下面是一個叫作 my_range 的生成器函數,它會生成一個從 0 到 (x - 1) 的數字流。

def my_range(x):
    i = 0
    while i < x:
        yield i
        i += 1

注意,該函數使用了 yield 而不是關鍵字 return。這樣使函數可以一次返回一個值,而且每次被調用時都從停下的位置繼續。關鍵字 yield 是將生成器與普通函數區分開來的依據。

練習:實現 my_enumerate

請本身寫一個效果和內置函數 enumerate 同樣的生成器函數。

以下所示地調用該函數:

lessons = ["Why Python Programming", "Data Types and Operators", "Control Flow", "Functions", "Scripting"]

for i, lesson in my_enumerate(lessons, 1):
    print("Lesson {}: {}".format(i, lesson))

應該會輸出:

Lesson 1: Why Python Programming
Lesson 2: Data Types and Operators
Lesson 3: Control Flow
Lesson 4: Functions
Lesson 5: Scripting

my_enumerate 函數實現:

def my_enumerate(iterable, start=0):
    """ return enumerate iterable """
    for x in zip(range(start, len(iterable)+start), iterable):
        yield x

練習:Chunker

若是可迭代對象太大,沒法完整地存儲在內存中(例如處理大型文件時),每次可以使用一部分頗有用。

實現一個生成器函數 chunker,接受一個可迭代對象並每次生成指定大小的部分數據。

以下所示地調用該函數:

for chunk in chunker(range(25), 4):
    print(list(chunk))

應該會輸出:

[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10, 11]
[12, 13, 14, 15]
[16, 17, 18, 19]
[20, 21, 22, 23]
[24]

chunker 函數實現以下:

def chunker(iterable, size):
    """Yield successive chunks from iterable of length size."""
    for i in range(0, len(iterable), size):
        yield iterable[i:i + size]

可迭代的range()很強大,能夠用len()也能夠用切片。

爲什麼要使用生成器?

你可能會疑問,爲什麼要使用生成器,而不使用列表。下面這段摘自 stack overflow 頁面 的內容回答了這個問題:

生成器是構建迭代器的 「懶惰」 方式。當內存不夠存儲完整實現的列表時,或者計算每一個列表元素的代價很高,你但願儘可能推遲計算時,就可使用生成器。可是這些元素只能遍歷一次。

另外一種詳細的解釋以下(詳細說明參見 該 stack overflow 頁面。)

因爲使用生成器是一次處理一個數據,在內存和存儲的需求上會比使用list方式直接所有生成再存儲節省不少資源。

由此區別,在處理大量數據時,常用生成器初步處理數據後,再進行長期存儲,而不是使用 list。由於不管使用生成器仍是 list,都是使用過就要丟棄的臨時數據。既然功能和結果同樣,那就不如用生成器。

可是生成器也有本身的侷限,它產生的數據不能回溯,不像list能夠任意選擇。

相關文章
相關標籤/搜索