我如何检查一个列表是否只有一个真实值?

2024-09-28 20:53:12 发布

您现在位置:Python中文网/ 问答频道 /正文

在python中,我有一个列表应该有一个且只有一个truthy值(即bool(value) is True)。有没有聪明的方法来检查这个?现在,我只是遍历列表并手动检查:

def only1(l)
    true_found = False
    for v in l:
        if v and not true_found:
            true_found=True
        elif v and true_found:
             return False #"Too Many Trues"
    return true_found

这看起来不雅,也不是很Python。有比这更聪明的方法吗?


Tags: and方法falsetrue列表returnisvalue
3条回答

最冗长的解决方案并不总是最不合理的解决方案。因此,我只添加了一个小修改(为了保存一些多余的布尔计算):

def only1(l):
    true_found = False
    for v in l:
        if v:
            # a True was found!
            if true_found:
                # found too many True's
                return False 
            else:
                # found the first True
                true_found = True
    # found zero or one True value
    return true_found

下面是一些比较的时间安排:

# file: test.py
from itertools import ifilter, islice

def OP(l):
    true_found = False
    for v in l:
        if v and not true_found:
            true_found=True
        elif v and true_found:
             return False #"Too Many Trues"
    return true_found

def DavidRobinson(l):
    return l.count(True) == 1

def FJ(l):
    return len(list(islice(ifilter(None, l), 2))) == 1

def JonClements(iterable):
    i = iter(iterable)
    return any(i) and not any(i)

def moooeeeep(l):
    true_found = False
    for v in l:
        if v:
            if true_found:
                # found too many True's
                return False 
            else:
                # found the first True
                true_found = True
    # found zero or one True value
    return true_found

我的输出:

$ python -mtimeit -s 'import test; l=[True]*100000' 'test.OP(l)' 
1000000 loops, best of 3: 0.523 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.DavidRobinson(l)' 
1000 loops, best of 3: 516 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.FJ(l)' 
100000 loops, best of 3: 2.31 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.JonClements(l)' 
1000000 loops, best of 3: 0.446 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.moooeeeep(l)' 
1000000 loops, best of 3: 0.449 usec per loop

可以看到,OP解决方案明显优于这里发布的大多数其他解决方案。正如所料,最好的是那些短路行为,特别是由乔恩克莱门茨张贴的解决方案。至少对于长列表中两个早期True值的情况。

这里完全没有True值:

$ python -mtimeit -s 'import test; l=[False]*100000' 'test.OP(l)' 
100 loops, best of 3: 4.26 msec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.DavidRobinson(l)' 
100 loops, best of 3: 2.09 msec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.FJ(l)' 
1000 loops, best of 3: 725 usec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.JonClements(l)' 
1000 loops, best of 3: 617 usec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.moooeeeep(l)' 
100 loops, best of 3: 1.85 msec per loop

我没有检查统计意义,但有趣的是,这次F.J.提出的方法,特别是Jon Clements提出的方法,似乎再次显示出明显的优越性。

不需要进口的:

def single_true(iterable):
    i = iter(iterable)
    return any(i) and not any(i)

或者,也许是更可读的版本:

def single_true(iterable):
    iterator = iter(iterable)
    has_true = any(iterator) # consume from "i" until first true or it's exhuasted
    has_another_true = any(iterator) # carry on consuming until another true value / exhausted
    return has_true and not has_another_true # True if exactly one true found

这:

  • 试图确保i具有任何真实值
  • 从iterable中的那一点开始一直寻找,以确保没有其他真正的值

这取决于您是在寻找值True,还是在寻找逻辑上计算为True的其他值(如11"hello")。如果前者:

def only1(l):
    return l.count(True) == 1

如果是后者:

def only1(l):
    return sum(bool(e) for e in l) == 1

因为这将在一次迭代中完成计数和转换,而无需构建新的列表。

相关问题 更多 >