“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): 

如果你的意思是一个最简单的forms,去这个:

 if (not a or not b or not c) and (a or b or c): 

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

更新:正如波动和Supr所说的那样,你可以应用德摩根定律并获得同样的结果:

 if (a or b or c) and not (a and b and c): 

我的build议是使用任何forms对您和其他程序员更重要。 第一个意思是“有事假,但也有事” ,第二个“有事有事,但不是事事” 。 如果我要优化或硬件做这个,我会select第二个,这里只select最可读的(也考虑到你将要testing的条件和他们的名字)。 我选了第一个。

怎么样:

 conditions = [a, b, c] if any(conditions) and not all(conditions): ... 

其他变体:

 if 1 <= sum(map(bool, conditions)) <= 2: ... 

这个问题已经有了很多高回报的答案和一个被接受的答案,但是到目前为止,他们都被各种各样的expression布尔问题的方式分散了,而错过了一个关键点:

我有一个python脚本,可以接收零或三个命令行参数。 (它可以运行默认行为或需要指定所有三个值)

这个逻辑不应该是你的代码的责任 ,而应该由argparse模块来处理。 不要打扰写一个复杂的if语句,而宁愿设置你的参数parsing器如下所示:

 #!/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的位置参数,你可以如何编写它。 我认为以前的界面是更好的风格,因为可选的参数应该是选项 ,但为了完整性,这是一个替代方法。 注意创buildparsing器时重写的kwarg usage ,因为argparse会自动生成误导性的用法消息,否则!

 #!/usr/bin/env python import argparse as ap parser = ap.ArgumentParser(usage='%(prog)s [-h] [abc]\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] [abc] three_or_none.py: error: expected 3 arguments 

我会去的:

 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) 

英文句子:

“如果a或b或c但不是全部”

转化为这个逻辑:

 (a or b or c) and not (a and b and c) 

“but”一词通常意味着一个连词,换句话说“和”。 而且,“所有这些”都转化为条件的联合:这个条件,那个条件, 以及其他条件。 “不”颠倒了整个连词。

我不同意接受的答案。 作者忽略了对规范应用最直接的解释,而忽略了应用德摩根定律来简化expression为更less的经营者:

  not a or not b or not c -> not (a and b and c) 

同时声称答案是“最小的forms”。

如果三个条件中只有一个为True则返回True 。 在你的示例代码中可能是你想要的。

 if sum(1 for x in (a,b,c) if x) == 1: 

那么呢:(独特的条件)

 if (bool(a) + bool(b) + bool(c) == 1): 

注意,如果你允许两个条件,你也可以这样做

 if (bool(a) + bool(b) + bool(c) in [1,2]): 

要清楚的是,你想根据多less参数是逻辑TRUE(在string参数的情况下 – 而不是空的)做出决定?

 argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0) 

然后你做出了一个决定:

 if ( 0 < argsne < 3 ): doSth() 

现在这个逻辑更清楚了。

为什么不数一数呢?

 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 

如果你不介意有点神秘,你可以用0 < (a + b + c) < 3来模拟滚动,如果你有一个到两个真实的语句,将会返回true ,如果全都是假或者假都是假的,则返回假。

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

这个问题表明,你需要所有三个参数(a和b和c)或者不需要(不是(a或b或c))

这给了:

(a和b和c)还是不是(a或b或c)

据我所知,你有一个函数接收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 ”值不会被传递给这个方法。

如果您使用条件迭代器,则访问速度可能会很慢。 但是,您不需要多次访问每个元素,而且您并不总是需要读取所有元素。 这是一个可以与无限生成器一起工作的解决scheme:

 #!/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]) 

当每个给定的boolTrue ,或者当每个给定的boolFalse
他们都是平等的对方!

所以,我们只需要find两个评估不同bool的元素
要知道至less有一个True ,至less有一个False

我的简短解决scheme

 not bool(a)==bool(b)==bool(c) 

我相信它短路,导致AFAIK a==b==c等于a==b and b==c

我的一般化解决scheme:

 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) 

我也写了一些代码处理多个迭代,但我从这里删除它,因为我认为这是毫无意义的。 不过这里还是可以的。

这基本上是“一些(但不是全部)”function(与any()all()内build函数相比)。

这意味着结果中应该有False True 。 因此,您可以执行以下操作:

 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) 

这个代码的一个优点是你只需要遍历一次结果(布尔值)的项目。

一个缺点是,所有这些真值expression式总是被评估,而不是像/ and操作符那样进行短路 。