【问题标题】:Python lambda with list comprehension具有列表理解的 Python lambda
【发布时间】:2016-06-21 13:05:26
【问题描述】:

我对带有内部列表理解操作的 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]

在 lambda 表达式之外实例化?

我找不到任何关于它的文档。

【问题讨论】:

    标签: python python-2.7 lambda list-comprehension


    【解决方案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 仅在选择使用函数符号时才有意义生成器/理解 - 在大多数情况下会有一个额外的函数调用的开销。

    【讨论】:

    • 让这个函数返回一个生成器可能是一个很好的改进。
    • 不看看这些结果是如何使用的就知道了。由于原始代码确实返回了一个列表,因此必须假设代码可能想要多次迭代它。否则,对于这个的大多数用途,我认为“集合”会比生成器更有用 - 但它们,“存储项目”必须是可散列的。
    【解决方案2】:

    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 { ... }。

    【讨论】:

    • 有趣的是,看到提议的解决方案如何快速收敛☺。一个关于发现差异的问题。过滤函数的速度与其他两种解决方案中提出的列表推导相比如何?
    • @Ev.Kounis 它可能会稍微慢一些。因为它必须对每个元素执行一个函数调用,而列表理解避免了那一点点开销......但是在 python3 filter 是惰性的,这意味着如果你只需要,比如说,前 10 个元素结果filter 可能比列表理解快得多,在这种情况下,您想使用生成器表达式而不是列表理解。
    【解决方案3】:

    按照您的编写方式,列表是 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
    

    这主要做两件事来提高效率:

    1. 它一次创建一个集合,用于快速成员资格检查
    2. 它将 lambda/filter 组合替换为更有效的推导式。

    【讨论】:

      猜你喜欢
      • 2015-03-31
      • 2013-02-21
      • 2018-02-06
      • 2023-01-02
      • 2018-04-13
      • 2016-01-19
      • 2012-09-22
      • 1970-01-01
      • 2018-04-29
      相关资源
      最近更新 更多