在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
Run Code Online (Sandbox Code Playgroud)
这似乎不优雅,不是非常pythonic.有更聪明的方法吗?
Jon*_*nts 234
一个不需要进口:
def single_true(iterable):
i = iter(iterable)
return any(i) and not any(i)
Run Code Online (Sandbox Code Playgroud)
或者,可能是更易读的版本:
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
Run Code Online (Sandbox Code Playgroud)
这个:
i有任何真正的价值Dav*_*son 46
这取决于您是只是在寻找值True还是正在寻找其他可以在True逻辑上评估的值(例如11或"hello").如果是前者:
def only1(l):
return l.count(True) == 1
Run Code Online (Sandbox Code Playgroud)
如果是后者:
def only1(l):
return sum(bool(e) for e in l) == 1
Run Code Online (Sandbox Code Playgroud)
因为这样可以在一次迭代中完成计数和转换,而无需构建新的列表.
moo*_*eep 40
最详细的解决方案并不总是最不优雅的解决方案.因此,我只添加了一个小修改(为了保存一些冗余的布尔值评估):
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
Run Code Online (Sandbox Code Playgroud)
以下是一些比较时间:
# 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
Run Code Online (Sandbox Code Playgroud)
我的输出:
$ 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
Run Code Online (Sandbox Code Playgroud)
可以看出,OP解决方案明显优于此处发布的大多数其他解决方案.正如预期的那样,最好的是具有短路行为的那些,尤其是Jon Clements发布的解决方案.至少对于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
Run Code Online (Sandbox Code Playgroud)
我没有检查统计显着性,但有趣的是,这次FJ建议的方法,尤其是Jon Clements的方法再次显然更优越.
And*_*ark 21
保留短路行为的单线答案:
from itertools import ifilter, islice
def only1(l):
return len(list(islice(ifilter(None, l), 2))) == 1
Run Code Online (Sandbox Code Playgroud)
对于具有相对较早的两个或更多真值的非常大的迭代,这将明显快于其他替代方案.
ifilter(None, itr)给出一个只能产生真实元素的迭代(x如果bool(x)返回则是真实的True). islice(itr, 2)给出一个只能产生前两个元素的迭代itr.通过将其转换为列表并检查长度是否等于1,我们可以验证确实存在一个真实元素,而不需要在找到两个之后检查任何其他元素.
以下是一些时序比较:
设置代码:
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
Run Code Online (Sandbox Code Playgroud)表现出短路行为:
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
Run Code Online (Sandbox Code Playgroud)没有发生短路的大型清单:
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
Run Code Online (Sandbox Code Playgroud)小清单:
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
Run Code Online (Sandbox Code Playgroud)因此,sum()对于非常小的列表,该方法更快,但随着输入列表变大,即使无法进行短路,我的版本也会更快.当在大输入端上进行短路时,性能差异很明显.
Ant*_*ala 14
我想获得死灵法师徽章,所以我概括了Jon Clements的优秀答案,保留了短路逻辑和快速谓词检查的好处.
这样就是:
N(真实)= n
def n_trues(iterable, n=1):
i = iter(iterable)
return all(any(i) for j in range(n)) and not any(i)
Run Code Online (Sandbox Code Playgroud)
N(真实)<= n:
def up_to_n_trues(iterable, n=1):
i = iter(iterable)
all(any(i) for j in range(n))
return not any(i)
Run Code Online (Sandbox Code Playgroud)
N(真实)> = n:
def at_least_n_trues(iterable, n=1):
i = iter(iterable)
return all(any(i) for j in range(n))
Run Code Online (Sandbox Code Playgroud)
m <= N(真实)<= n
def m_to_n_trues(iterable, m=1, n=1):
i = iter(iterable)
assert m <= n
return at_least_n_trues(i, m) and up_to_n_trues(i, n - m)
Run Code Online (Sandbox Code Playgroud)
小智 11
>>> l = [0, 0, 1, 0, 0]
>>> has_one_true = len([ d for d in l if d ]) == 1
>>> has_one_true
True
Run Code Online (Sandbox Code Playgroud)
if sum([bool(x) for x in list]) == 1
Run Code Online (Sandbox Code Playgroud)
(假设您的所有值都是布尔值。)
这可能会更快,只是总结一下
sum(list) == 1
Run Code Online (Sandbox Code Playgroud)
尽管它可能会导致一些问题,具体取决于列表中的数据类型。
你可以做:
x = [bool(i) for i in x]
return x.count(True) == 1
Run Code Online (Sandbox Code Playgroud)
或者
x = map(bool, x)
return x.count(True) == 1
Run Code Online (Sandbox Code Playgroud)
基于@JoranBeasley 的方法:
sum(map(bool, x)) == 1
Run Code Online (Sandbox Code Playgroud)