Python list comprehension - want to avoid repeated evaluation

前端 未结 12 2194
被撕碎了的回忆
被撕碎了的回忆 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 05:56

    How about defining:

    def truths(L):
        """Return the elements of L that test true"""
        return [x for x in L if x]
    

    So that, for example

    > [wife.children for wife in henry8.wives]
    [[Mary1], [Elizabeth1], [Edward6], [], [], []]
    
    > truths(wife.children for wife in henry8.wives) 
    [[Mary1], [Elizabeth1], [Edward6]]
    
    0 讨论(0)
  • 2020-11-27 05:57

    Here is my solution:

    filter(None, [f(x) for x in l])
    
    0 讨论(0)
  • 2020-11-27 05:58
    [y for y in [f(x) for x in l] if y]
    

    For your updated problem, this might be useful:

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

    A solution (the best if you have repeated value of x) would be to memoize the function f, i.e. to create a wrapper function that saves the argument by which the function is called and save it, than return it if the same value is asked.

    a really simple implementation is the following:

    storage = {}
    def memoized(value):
        if value not in storage:
            storage[value] = f(value)
        return storage[value]
    
    [memoized(x) for x in l if memoized(x)]
    

    and then use this function in the list comprehension. This approach is valid under two condition, one theoretical and one practical. The first one is that the function f should be deterministic, i.e. returns the same results given the same input, and the other is that the object x can be used as a dictionary keys. If the first one is not valid than you should recompute f each timeby definition, while if the second one fails it is possible to use some slightly more robust approaches.

    You can find a lot of implementation of memoization around the net, and I think that the new versions of python have something included in them too.

    On a side note, never use the small L as a variable name, is a bad habit as it can be confused with an i or a 1 on some terminals.

    EDIT:

    as commented, a possible solution using generators comprehension (to avoid creating useless duplicate temporaries) would be this expression:

    [g(x, fx) for x, fx in ((x,f(x)) for x in l) if fx]
    

    You need to weight your choice given the computational cost of f, the number of duplication in the original list and memory at you disposition. Memoization make a space-speed tradeoff, meaning that it keep tracks of each result saving it, so if you have huge lists it can became costly on the memory occupation front.

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

    There have been a lot of answers regarding memoizing. The Python 3 standard library now has a lru_cache, which is a Last Recently Used Cache. So you can:

    from functools import lru_cache
    
    @lru_cache()
    def f(x):
        # function body here
    

    This way your function will only be called once. You can also specify the size of the lru_cache, by default this is 128. The problem with the memoize decorators shown above is that the size of the lists can grow well out of hand.

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

    You should use a memoize decorator. Here is an interesting link.


    Using memoization from the link and your 'code':

    def memoize(f):
        """ Memoization decorator for functions taking one or more arguments. """
        class memodict(dict):
            def __init__(self, f):
                self.f = f
            def __call__(self, *args):
                return self[args]
            def __missing__(self, key):
                ret = self[key] = self.f(*key)
                return ret
        return memodict(f)
    
    @memoize
    def f(x):
        # your code
    
    [f(x) for x in l if f(x)]
    
    0 讨论(0)
提交回复
热议问题