"如果a或b或c但不是全部"的Python语法

Chr*_*son 128 python if-statement

我有一个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):
Run Code Online (Sandbox Code Playgroud)

def*_*fuz 235

怎么样:

conditions = [a, b, c]
if any(conditions) and not all(conditions):
   ...
Run Code Online (Sandbox Code Playgroud)

其他变种:

if 1 <= sum(map(bool, conditions)) <= 2:
   ...
Run Code Online (Sandbox Code Playgroud)

  • @PaulScheltema第一种形式对任何人来说都更容易理解. (13认同)
  • 没错,你需要一个丑陋的总和(地图(布尔,条件))` (6认同)
  • 这个"任何而不是全部"是最好和最清楚的布尔方法,只要注意arg存在和arg是'truthy'之间的重要区别 (6认同)
  • 请注意,这不是短路,因为所有条件都是预先评估的. (5认同)
  • 如果sum(conditions)中的任何一个返回2,例如True,则可能出错。 (2认同)

Ste*_*ppo 227

如果您的意思是最小形式,请使用以下内容:

if (not a or not b or not c) and (a or b or c):
Run Code Online (Sandbox Code Playgroud)

这翻译了你的问题的标题.

更新:正如Volatility和Supr所说,您可以应用De Morgan定律并获得等价物:

if (a or b or c) and not (a and b and c):
Run Code Online (Sandbox Code Playgroud)

我的建议是使用对您和其他程序员来说更重要的形式.第一个意思是"有一些错误,但也有一些真实的东西",第二个意思是"有一些东西是真的,但不是一切".如果我要优化或在硬件中执行此操作,我会选择第二个,这里只选择最具可读性(同时考虑您将要测试的条件及其名称).我选了第一个.

  • 甚至`if(a或b或c)而不是(a和b和c)`以完美匹配标题;) (206认同)
  • @Supr`如果有的话([a,b,c])而不是全部([a,b,c])` (61认同)
  • 我会让它变得更简洁,如果不是(a和b和c)和(a或b或c),那就去吧 (37认同)
  • 所有伟大的答案,但这简洁,并有很大的短路.谢谢大家! (3认同)
  • @HennyH我相信这个问题要求"至少有一个条件是真的但不是全部",而不是"只有一个条件是真的". (3认同)
  • 这并不是最小的,就我所见,它也没有完全翻译问题的标题 (2认同)

wim*_*wim 113

这个问题已经有许多高度赞成的答案和一个公认的答案,但到目前为止所有这些答案都被表达布尔问题的各种方式分散了注意力并错过了一个关键点:

我有一个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)
Run Code Online (Sandbox Code Playgroud)

是的,它应该是一个选项而不是位置参数,因为它毕竟是可选的.


编辑: 为了解决LarsH在评论中的关注,下面是一个例子,说明如果你确定你想要3或0位置 args的接口你可以编写它.我认为前面的接口是更好的样式,因为可选参数应该是选项,但是为了完整性,这里是另一种方法.usage在创建解析器时请注意覆盖的kwarg,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)
Run Code Online (Sandbox Code Playgroud)

以下是一些用法示例:

# 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
Run Code Online (Sandbox Code Playgroud)

  • 单独的问题.你不相信它是好的CLI,你可以争论这一点,OP可能会得到说服.但是你的答案明显偏离了这个问题,需要提到规范的变化.您似乎正在弯曲规范以适应可用工具,而不提及更改. (8认同)
  • 是的,我有意添加了.有可能使参数成立,并强制准确消耗3或0,但它不会成为一个好的CLI所以我没有推荐它. (4认同)
  • @LarsH好的,我添加了一个更符合问题中隐含的原始界面的示例.现在弯曲工具以满足可用的规格...;) (2认同)
  • 这是我投票的唯一答案.+1回答*真正的问题*. (2认同)

Jon*_*nts 32

我会去:

conds = iter([a, b, c])
if any(conds) and not any(conds):
    # okay...
Run Code Online (Sandbox Code Playgroud)

我认为这应该相当有效地短路

说明

通过创建conds一个迭代器,第一次使用any会短路,如果任何项为真,则将迭代器指向下一个元素; 否则,它将消耗整个列表并且是False.下一个any采用iterable中的剩余项,并确保没有任何其他真值...如果有,则整个语句不能为真,因此没有一个唯一元素(如此短路)再次).最后一个any将返回False或将耗尽迭代和True.

注意:以上检查是否只设置了一个条件


如果要检查是否设置了一个或多个项目,而不是每个项目,那么您可以使用:

not all(conds) and any(conds)
Run Code Online (Sandbox Code Playgroud)

  • 这非常聪明,但是:如果您不知道前面有多少条件,我会使用这种方法,但对于固定的,已知的条件列表,可读性的损失根本不值得. (6认同)
  • 我不明白.它读起来像:如果是真而不是真.帮帮我理解 (5认同)
  • 这不会短路.该列表在传递给`iter`之前完全构建.`any`和`all`将懒惰地消耗列表,是的,但是到达那里时列表已经完全被评估了! (4认同)
  • 即使有解释我也无法理解行为......用`[a,b,c] = [True,True,False]`你的代码不应该"打印"`False`,而预期输出是'True`? (2认同)

Kaz*_*Kaz 22

英文句子:

"如果a或b或c但不是全部"

转换为这个逻辑:

(a or b or c) and not (a and b and c)
Run Code Online (Sandbox Code Playgroud)

"但"这个词通常意味着一个连词,换句话说就是"和".此外,"所有这些"转换为的条件结合:这种情况下,该条件下,其它条件."不"反转了整个连词.

我不同意接受的答案.作者忽略了对规范应用最直接的解释,而忽略了应用De Morgan定律来简化表达式给更少的算子:

 not a or not b or not c  ->  not (a and b and c)
Run Code Online (Sandbox Code Playgroud)

同时声称答案是"最小形式".


eum*_*iro 10

这将返回True如果有且仅有的三个条件之一True.可能是您在示例代码中想要的内容.

if sum(1 for x in (a,b,c) if x) == 1:
Run Code Online (Sandbox Code Playgroud)


Cas*_*yte 9

怎么样:(独特的条件)

if (bool(a) + bool(b) + bool(c) == 1):
Run Code Online (Sandbox Code Playgroud)

请注意,如果您也允许两个条件,那么您可以这样做

if (bool(a) + bool(b) + bool(c) in [1,2]):
Run Code Online (Sandbox Code Playgroud)


Dan*_*lor 6

要清楚,你想根据有多少参数是逻辑TRUE做出决定(如果是字符串参数 - 不是空的话)?

argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)
Run Code Online (Sandbox Code Playgroud)

然后你做出了决定:

if ( 0 < argsne < 3 ):
 doSth() 
Run Code Online (Sandbox Code Playgroud)

现在逻辑更清晰了.


Lou*_*uis 5

为什么不算数呢?

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
Run Code Online (Sandbox Code Playgroud)


小智 5

如果你不介意有点神秘,你可以简单地滚动,如果你有一到两个真实的陈述0 < (a + b + c) < 3将返回true,如果所有都是假或没有假,则返回false.

如果您使用函数来评估bool,这也简化了,因为您只评估变量一次,这意味着您可以内联编写函数,而不需要临时存储变量.(例如:0 < ( a(x) + b(x) + c(x) ) < 3.)