2016-06-21 121 views
0

我對lambda表達式與內部列表理解操作有所懷疑。Python lambda與列表理解

在下面的代碼中,lambda會每次爲每個項目實例化一個列表?

def _find_items_not_present_in_store(self, store_today, store_yesterday): 
     # finding what items are not in the store anymore 
     items_not_in_store_anymore = filter(lambda item1: item1.item_id not in 
             [item2.item_id for item2 in store_today.store_items], 
             store_yesterday) 
     return items_not_in_store_anymore 

會更好,有這個名單

[item2.item_id for item2 in store.store_items] 

實例化的la​​mbda表達式之外?

我找不到任何有關它的文檔。

回答

1

您正在爲列表中的每個項目執行線性搜索 - 而且這絕對是次優的。對於有100萬件商品的商店,帽子可能會導致(1000000)²比較的順序,即使對於快速計算機來說,這也是一個相當大的負擔。這只是開始

要做的事情是創建一個集合的ID之一,並使用集合的「包含」(相同的in運算符) - 它在恆定時間內進行搜索。

def _find_items_not_present_in_store(self, store_today, store_yesterday): 
    yesterday_ids = set(item.item_id for item in store_yesterday) 
    return [item for item in store_today if item.item_id not in yesterday_ids] 

而且 - 在你的代碼 - 除了在列表中進行搜索,而不是在一組,你實際上是重新創建爲在今天的列表中的每個項目整體昨天的ID列表 - 爲您的列表生成器表達式在lambda函數內部。在上面的方法中,我預先計算了一次ID集合 - 這是有道理的。

除此之外,你可以看到,在Python列表理解和生成器表達式有一個if條款,取代了filter函數的用法 - filter纔有意義,當一個人選擇使用的功能符號,而不是生成/推導 - 並且在大多數情況下會有額外的函數調用的開銷。

+0

這個函數返回一個生成器可能是一個不錯的改進。 –

+0

如果不考慮如何使用這些結果,就無法知道它。由於原始代碼確實返回了一個列表,因此必須假定代碼可能不止一次地迭代它。否則,對於這個的大多數用途,我認爲一個「集合」比一個生成器更有用 - 但他們,「商店項目」必須是可散列的。 – jsbueno

1

lambda函數的每次調用都將重新創建該列表,因此將該構造移到lambda之外將會提高性能。

此外,使用list檢查in不是一個好主意,因爲它需要線性時間。考慮使用set代替:

def _find_items_not_present_in_store(self, store_today, store_yesterday): 
     today_ids = {item2.item_if for item2 in store_today.store_items} 

     items_not_in_store_anymore = filter(
      lambda item1: item1.item_id not in today_ids, 
      store_yesterday 
     ) 
     return items_not_in_store_anymore 

在舊版本的Python,你需要做的,而不是set( ...)set -comprehension { ... }

+0

有趣的是,看到如何提出的解決方案收斂快速☺。雖然有關於差異的一個問題被發現。過濾器函數的速度與其他兩種解決方案中提出的列表理解相比如何? –

+0

@ Ev.Kounis它可能會稍微慢一些。因爲它必須爲每個元素執行一個函數調用,而列表理解避免了一點點的開銷......但是在python3中'filter'是懶惰的,這意味着如果你只需要前面10個元素結果'過濾器'可能比列表理解要快得多,在這種情況下,您希望使用生成器表達式而不是列表理解。 – Bakuriu

1

你寫它的方式,列表是lambda表達式的一部分,所以每次lambda被調用時都會被評估。

這裏是實現你的函數的最有效的方法:

def _find_items_not_present_in_store(self, store_today, store_yesterday): 
    s = set(item2.item_id for item2 in store_today.store_items) 
    items_not_in_store_anymore = [item1 for item1 in store_yesterday 
            if item1.item_id not in s] 
    return items_not_in_store_anymore 

這確實2分主要的事情是提高效率:

  1. 它創建了一組,一次,快速成員檢查
  2. 它將lambda/filter組合替換爲更高效的理解。