if语句 - “如果a或b或c但不是全部”的Python语法
我有一个python脚本,可以接收零或三个命令行参数。 (它可以在默认行为上运行,也可以需要指定所有三个值。)
什么是理想的语法:
if a and (not b or not c) or b and (not a or not c) or c and (not b or not a):
?
15个解决方案
231 votes
怎么样:
conditions = [a, b, c]
if any(conditions) and not all(conditions):
...
其他变种:
if 1 <= sum(map(bool, conditions)) <= 2:
...
defuz answered 2019-06-11T01:04:47Z
222 votes
如果您的意思是最小形式,请使用以下内容:
if (not a or not b or not c) and (a or b or c):
这翻译了你的问题的标题。
更新:正如Volatility和Supr所说,您可以应用De Morgan定律并获得等价物:
if (a or b or c) and not (a and b and c):
我的建议是使用对您和其他程序员来说更重要的形式。 第一个意思是“有一些错误,但也有一些真实的东西”,第二个意思是“有一些东西是真的,但不是一切”。 如果我要优化或在硬件中执行此操作,我会选择第二个,这里只选择最具可读性(同时考虑您将要测试的条件及其名称)。 我选了第一个。
Stefano Sanfilippo answered 2019-06-11T01:04:22Z
112 votes
这个问题已经有很多高度赞成的答案和一个公认的答案,但到目前为止所有这些答案都被表达布尔问题的各种方式分散了注意力并错过了一个关键点:
我有一个python脚本,可以接收零或三个命令 行参数。 (它可以在默认行为上运行,也可能需要全部三个 指定的值)
这个逻辑首先不应该是您的代码的责任,而应该由argparse模块处理。 不要费心编写复杂的if语句,而是更喜欢设置你的参数解析器:
#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser()
parser.add_argument('--foo', nargs=3, default=['x', 'y', 'z'])
args = parser.parse_args()
print(args.foo)
是的,它应该是一个选项而不是位置参数,因为它毕竟是可选的。
编辑:为了解决LarsH在评论中的关注,下面是一个例子,说明如果你确定你想要3或0位置args的接口你可以编写它。 我认为前面的接口是更好的样式,因为可选参数应该是选项,但是为了完整性,这里是另一种方法。 在创建解析器时请注意重写的kwarg usage,因为argparse会自动生成误导性的使用消息!
#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser(usage='%(prog)s [-h] [a b c]\n')
parser.add_argument('abc', nargs='*', help='specify 3 or 0 items', default=['x', 'y', 'z'])
args = parser.parse_args()
if len(args.abc) != 3:
parser.error('expected 3 arguments')
print(args.abc)
以下是一些用法示例:
# default case
wim@wim-zenbook:/tmp$ ./three_or_none.py
['x', 'y', 'z']
# explicit case
wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 3
['1', '2', '3']
# example failure mode
wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2
usage: three_or_none.py [-h] [a b c]
three_or_none.py: error: expected 3 arguments
wim answered 2019-06-11T01:05:48Z
33 votes
我会去:
conds = iter([a, b, c])
if any(conds) and not any(conds):
# okay...
我认为这应该相当有效地短路
说明
通过使conds成为迭代器,any的第一次使用将短路,并且如果任何项为真,则将迭代器指向下一个元素; 否则,它将消耗整个列表并且是False。下一个any获取可迭代中的剩余项目,并确保没有任何其他真值...如果有,则整个语句不能为真 因此没有一个独特的元素(所以再次短路)。 最后any将返回False或将耗尽该可迭代并且为True。
注意:以上检查是否只设置了一个条件
如果要检查是否设置了一个或多个项目,而不是每个项目,那么您可以使用:
not all(conds) and any(conds)
Jon Clements answered 2019-06-11T01:06:44Z
22 votes
英文句子:
“如果a或b或c但不是全部”
转换为这个逻辑:
(a or b or c) and not (a and b and c)
“但”这个词通常意味着一个连词,换句话说就是“和”。 此外,“所有这些”都转化为条件的结合:这种情况,条件和其他条件。 “不”反转了整个连词。
我不同意接受的答案。 作者忽略了对规范应用最直接的解释,而忽略了应用De Morgan定律来简化表达式给更少的算子:
not a or not b or not c -> not (a and b and c)
同时声称答案是“最小形式”。
Kaz answered 2019-06-11T01:07:43Z
10 votes
如果三个条件中只有一个是True,则返回True.可能是您在示例代码中想要的内容。
if sum(1 for x in (a,b,c) if x) == 1:
eumiro answered 2019-06-11T01:08:10Z
9 votes
怎么样:(独特的条件)
if (bool(a) + bool(b) + bool(c) == 1):
请注意,如果您也允许两个条件,那么您可以这样做
if (bool(a) + bool(b) + bool(c) in [1,2]):
Casimir et Hippolyte answered 2019-06-11T01:08:43Z
6 votes
要清楚,你想根据有多少参数是逻辑TRUE做出决定(如果是字符串参数 - 不是空的话)?
argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)
然后你做出了决定:
if ( 0 < argsne < 3 ):
doSth()
现在逻辑更清晰了。
Danubian Sailor answered 2019-06-11T01:09:23Z
5 votes
为什么不算数呢?
import sys
a = sys.argv
if len(a) = 1 :
# No arguments were given, the program name count as one
elif len(a) = 4 :
# Three arguments were given
else :
# another amount of arguments was given
Louis answered 2019-06-11T01:09:49Z
5 votes
如果你不介意有点神秘,你可以用0 < ( a(x) + b(x) + c(x) ) < 3轻轻滚动,如果你有一到两个真实的陈述,它将返回true,如果所有都是假或者没有假,则返回false。
如果您使用函数来评估bool,这也简化了,因为您只评估变量一次,这意味着您可以内联编写函数,而不需要临时存储变量。 (例如:0 < ( a(x) + b(x) + c(x) ) < 3。)
Spinno answered 2019-06-11T01:10:23Z
4 votes
问题表明你需要所有三个参数(a和b和c)或者都不需要(不是(a或b或c))
这给出了:
(a和b和c)与否(a或b或c)
Relaxing In Cyprus answered 2019-06-11T01:10:58Z
4 votes
据我所知,你有一个接收3个参数的函数,但如果没有,它将在默认行为上运行。 由于您没有解释在提供1或2个参数时应该发生什么,我将假设它应该只是执行默认行为。 在这种情况下,我认为你会发现以下答案非常有利:
def method(a=None, b=None, c=None):
if all([a, b, c]):
# received 3 arguments
else:
# default behavior
但是,如果您希望以不同方式处理1或2个参数:
def method(a=None, b=None, c=None):
args = [a, b, c]
if all(args):
# received 3 arguments
elif not any(args):
# default behavior
else:
# some args (raise exception?)
注意:这假定“False”值不会传递给此方法。
Inbar Rose answered 2019-06-11T01:11:40Z
2 votes
如果使用条件迭代器,则访问速度可能会很慢。 但是您不需要多次访问每个元素,并且您并不总是需要阅读所有元素。 这是一个适用于无限生成器的解决方案:
#!/usr/bin/env python3
from random import randint
from itertools import tee
def generate_random():
while True:
yield bool(randint(0,1))
def any_but_not_all2(s): # elegant
t1, t2 = tee(s)
return False in t1 and True in t2 # could also use "not all(...) and any(...)"
def any_but_not_all(s): # simple
hadFalse = False
hadTrue = False
for i in s:
if i:
hadTrue = True
else:
hadFalse = True
if hadTrue and hadFalse:
return True
return False
r1, r2 = tee(generate_random())
assert any_but_not_all(r1)
assert any_but_not_all2(r2)
assert not any_but_not_all([True, True])
assert not any_but_not_all2([True, True])
assert not any_but_not_all([])
assert not any_but_not_all2([])
assert any_but_not_all([True, False])
assert any_but_not_all2([True, False])
Janus Troelsen answered 2019-06-11T01:12:07Z
0 votes
当每个给定的a==b==c是a==b and b==c,或者每个给定的False是False ...
他们都彼此相等!
所以,我们只需要找到两个评估不同a==b==c的元素
要知道至少有一个a==b and b==c和至少一个False。
我的短解决方案:
not bool(a)==bool(b)==bool(c)
我认为它短路,导致AFAIK a==b==c等于a==b and b==c。
我的通用解决方案:
def _any_but_not_all(first, iterable): #doing dirty work
bool_first=bool(first)
for x in iterable:
if bool(x) is not bool_first:
return True
return False
def any_but_not_all(arg, *args): #takes any amount of args convertable to bool
return _any_but_not_all(arg, args)
def v_any_but_not_all(iterable): #takes iterable or iterator
iterator=iter(iterable)
return _any_but_not_all(next(iterator), iterator)
我还写了一些处理多个迭代的代码,但我从这里删除了它,因为我觉得它没有意义。 但它仍然可以在这里使用。
GingerPlusPlus answered 2019-06-11T01:13:25Z
-2 votes
这基本上是“一些(但不是全部)”功能(与or和and内置函数形成对比)。
这意味着结果中应该有or和and。 因此,您可以执行以下操作:
some = lambda ii: frozenset(bool(i) for i in ii).issuperset((True, False))
# one way to test this is...
test = lambda iterable: (any(iterable) and (not all(iterable))) # see also http://stackoverflow.com/a/16522290/541412
# Some test cases...
assert(some(()) == False) # all() is true, and any() is false
assert(some((False,)) == False) # any() is false
assert(some((True,)) == False) # any() and all() are true
assert(some((False,False)) == False)
assert(some((True,True)) == False)
assert(some((True,False)) == True)
assert(some((False,True)) == True)
此代码的一个优点是您只需要遍历生成的(布尔值)项目。
一个缺点是所有这些真值表达式总是被评估,并且不像or/and运算符那样进行短路。
Abbafei answered 2019-06-11T01:14:16Z