How can I check that a list has one and only one truthy value?

后端 未结 17 2530
暗喜
暗喜 2020-11-27 12:23

In python, I have a list that should have one and only one truthy value (that is, bool(value) is True). Is there a clever way to check for this

17条回答
  •  星月不相逢
    2020-11-27 13:08

    A one-line answer that retains the short-circuiting behavior:

    from itertools import ifilter, islice
    
    def only1(l):
        return len(list(islice(ifilter(None, l), 2))) == 1
    

    This will be significantly faster than the other alternatives here for very large iterables that have two or more true values relatively early.

    ifilter(None, itr) gives an iterable that will only yield truthy elements (x is truthy if bool(x) returns True). islice(itr, 2) gives an iterable that will only yield the first two elements of itr. By converting this to a list and checking that the length is equal to one we can verify that exactly one truthy element exists without needing to check any additional elements after we have found two.

    Here are some timing comparisons:

    • Setup code:

      In [1]: from itertools import islice, ifilter
      
      In [2]: def fj(l): return len(list(islice(ifilter(None, l), 2))) == 1
      
      In [3]: def david(l): return sum(bool(e) for e in l) == 1
      
    • Exhibiting short-circuit behavior:

      In [4]: l = range(1000000)
      
      In [5]: %timeit fj(l)
      1000000 loops, best of 3: 1.77 us per loop
      
      In [6]: %timeit david(l)
      1 loops, best of 3: 194 ms per loop
      
    • Large list where short-circuiting does not occur:

      In [7]: l = [0] * 1000000
      
      In [8]: %timeit fj(l)
      100 loops, best of 3: 10.2 ms per loop
      
      In [9]: %timeit david(l)
      1 loops, best of 3: 189 ms per loop
      
    • Small list:

      In [10]: l = [0]
      
      In [11]: %timeit fj(l)
      1000000 loops, best of 3: 1.77 us per loop
      
      In [12]: %timeit david(l)
      1000000 loops, best of 3: 990 ns per loop
      

    So the sum() approach is faster for very small lists, but as the input list gets larger my version is faster even when short-circuiting is not possible. When short-circuiting is possible on a large input, the performance difference is clear.

提交回复
热议问题