Python list comprehension - want to avoid repeated evaluation

前端 未结 12 2197
被撕碎了的回忆
被撕碎了的回忆 2020-11-27 05:23

I have a list comprehension which approximates to:

[f(x) for x in l if f(x)]

Where l is a list and f(x) is an expensive function which retu

相关标签:
12条回答
  • 2020-11-27 06:11
    [y for y in (f(x) for x in l) if y]
    

    Will do.

    0 讨论(0)
  • 2020-11-27 06:12

    Starting Python 3.8, and the introduction of assignment expressions (PEP 572) (:= operator), it's possible to use a local variable within a list comprehension in order to avoid calling twice the same function:

    In our case, we can name the evaluation of f(x) as a variable y while using the result of the expression to filter the list but also as the mapped value:

    [y for x in l if (y := f(x))]
    
    0 讨论(0)
  • 2020-11-27 06:13

    Nope. There's no (clean) way to do this. There's nothing wrong with a good-old-fashioned loop:

    output = []
    for x in l:
        result = f(x)
        if result: 
            output.append(result)
    

    If you find that hard to read, you can always wrap it in a function.

    0 讨论(0)
  • 2020-11-27 06:14

    Use map() !!

    comp = [x for x in map(f, l) if x]
    

    f is the function f(X), l is the list

    map() will return the result of f(x) for each x in the list.

    0 讨论(0)
  • 2020-11-27 06:16

    You can use memoization. It is a technique which is used in order to avoid doing the same computation twice by saving somewhere the result for each calculated value. I saw that there is already an answer that uses memoization, but I would like to propose a generic implementation, using python decorators:

    def memoize(func):
        def wrapper(*args):
            if args in wrapper.d:
                return wrapper.d[args]
            ret_val = func(*args)
            wrapper.d[args] = ret_val
            return ret_val
        wrapper.d = {}
        return wrapper
    
    @memoize
    def f(x):
    ...
    

    Now f is a memoized version of itself. With this implementation you can memoize any function using the @memoize decorator.

    0 讨论(0)
  • 2020-11-27 06:17

    As the previous answers have shown, you can use a double comprehension or use memoization. For reasonably-sized problems it's a matter of taste (and I agree that memoization looks cleaner, since it hides the optimization). But if you're examining a very large list, there's a huge difference: Memoization will store every single value you've calculated, and can quickly blow out your memory. A double comprehension with a generator (round parens, not square brackets) only stores what you want to keep.

    To come to your actual problem:

    [g(x, f(x)) for x in series if f(x)]
    

    To calculate the final value you need both x and f(x). No problem, pass them both like this:

    [g(x, y) for (x, y) in ( (x, f(x)) for x in series ) if y ]
    

    Again: this should be using a generator (round parens), not a list comprehension (square brackets). Otherwise you will build the whole list before you start filtering the results. This is the list comprehension version:

    [g(x, y) for (x, y) in [ (x, f(x)) for x in series ] if y ] # DO NOT USE THIS
    
    0 讨论(0)
提交回复
热议问题