【问题标题】:How do I efficiently filter computed values within a Python list comprehension?如何有效地过滤 Python 列表理解中的计算值?
【发布时间】:2008-09-24 22:08:57
【问题描述】:

Python 列表推导式语法使过滤推导式中的值变得容易。例如:

result = [x**2 for x in mylist if type(x) is int]

将返回 mylist 中整数平方的列表。但是,如果测试涉及一些(昂贵的)计算并且您想过滤结果怎么办?一种选择是:

result = [expensive(x) for x in mylist if expensive(x)]

这将产生一个非“假”的值的列表,昂贵的(x)值,但是对于每个 x 调用两次昂贵的()。是否有一种理解语法允许您进行此测试,同时每个 x 只调用一次昂贵的?

【问题讨论】:

    标签: python list-comprehension


    【解决方案1】:

    经过一分钟的思考,我想出了自己的答案。它可以通过嵌套推导来完成:

    result = [y for y in (expensive(x) for x in mylist) if y]
    

    我想这行得通,虽然我发现嵌套推导式的可读性很差

    【讨论】:

    • 同意可读性。您可以在第一行分配一个生成器,然后在第二行进行过滤。
    【解决方案2】:

    如果计算已经很好地捆绑到函数中,那么使用filtermap 怎么样?

    result = filter (None, map (expensive, mylist))
    

    如果列表很大,您可以使用itertools.imap

    【讨论】:

    • filter(expensive, mylist) 在 Python 3 中,返回一个列表:list(filter(expensive, mylist))
    【解决方案3】:

    最明显(我认为最易读)的答案是不使用列表推导式或生成器表达式,而是使用真正的生成器:

    def gen_expensive(mylist):
        for item in mylist:
            result = expensive(item)
            if result:
                yield result
    

    它需要更多的水平空间,但一眼就能看出它的作用要容易得多,而且你最终不会重复自己。

    【讨论】:

    • 我认为只有在您要多次使用 gen_expensive() 时才值得这样做,即使那样,我认为过滤结果会胜出,因为您可以在一行上看到发生了什么,而不是而不是必须挖掘 gen_expensive() 的定义。
    【解决方案4】:
    result = [x for x in map(expensive,mylist) if x]
    

    map() 将返回 mylist 中每个对象的值的列表,这些对象的值被传递给开销 ()。然后你可以列出理解,并丢弃不必要的值。

    这有点像嵌套推导式,但应该更快(因为 python 解释器可以很容易地对其进行优化)。

    【讨论】:

    • 我喜欢这个解决方案。它的一个问题是 map 创建了一个列表,因此您将拥有一个可能很大的临时列表,最终会被拆除。
    • @Nick:如果创建一个大列表是个问题,请使用itertools.imap
    【解决方案5】:

    这正是生成器适合处理的问题:

    result = (expensive(x) for x in mylist)
    result = (do_something(x) for x in result if some_condition(x))
    ...
    result = [x for x in result if x]  # finally, a list
    
    1. 这使得流水线的每个阶段发生的事情完全清楚。
    2. 显式优于隐式
    3. 在最后一步之前在任何地方都使用生成器,因此没有大型中间列表

    cf:'Generator Tricks for System Programmers' by David Beazley

    【讨论】:

      【解决方案6】:

      您总是可以 memoize expensive() 函数,这样第二次调用它只是查找 x 的计算值。

      Here's just one of many implementations of memoize as a decorator.

      【讨论】:

        【解决方案7】:

        你可以记住昂贵的(x)(如果你经常调用昂贵的(x),你可能应该以任何方式记住它。这个页面给出了一个 python 的 memoize 实现:

        http://code.activestate.com/recipes/52201/

        这还有一个额外的好处是,昂贵的(x)可以运行少于次,因为任何重复的条目都将使用上一次执行的备忘录。

        请注意,这假设昂贵(x)是一个真正的函数,并且不依赖于可能改变的外部状态。如果昂贵(x) 确实依赖于外部状态,并且您可以检测到该状态何时发生变化,或者您知道它不会在列表理解期间发生变化,那么您可以在理解之前重置备忘录。

        【讨论】:

          【解决方案8】:

          我会偏爱:

          itertools.ifilter(bool, (expensive(x) for x in mylist))
          

          这样做的好处是:

          【讨论】:

            【解决方案9】:

            for 循环的普通旧用法也可以附加到列表:

            result = []
            for x in mylist:
                expense = expensive(x)
                if expense:
                    result.append(expense)
            

            【讨论】:

              猜你喜欢
              • 2016-10-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-02-11
              • 1970-01-01
              • 1970-01-01
              • 2018-07-14
              • 1970-01-01
              相关资源
              最近更新 更多