问题:“如果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):
?
I have a python script that can receive either zero or three command line arguments. (Either it runs on default behavior or needs all three values specified.)
What’s the ideal syntax for something like:
if a and (not b or not c) or b and (not a or not c) or c and (not b or not a):
?
回答 0
如果您的意思是最小形式,请执行以下操作:
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):
我的建议是使用对您和其他程序员更重要的形式。第一个意思是“有一些错误,但也有一些真实”,第二个意思是“有一些真实,但不是一切”。如果要在硬件上进行优化或执行此操作,则选择第二个,这里只选择最易读的代码(还要考虑要测试的条件及其名称)。我选了第一个。
If you mean a minimal form, go with this:
if (not a or not b or not c) and (a or b or c):
Which translates the title of your question.
UPDATE: as correctly said by Volatility and Supr, you can apply De Morgan’s law and obtain equivalent:
if (a or b or c) and not (a and b and c):
My advice is to use whichever form is more significant to you and to other programmers. The first means “there is something false, but also something true”, the second “There is something true, but not everything”. If I were to optimize or do this in hardware, I would choose the second, here just choose the most readable (also taking in consideration the conditions you will be testing and their names). I picked the first.
回答 1
怎么样:
conditions = [a, b, c]
if any(conditions) and not all(conditions):
...
其他变体:
if 1 <= sum(map(bool, conditions)) <= 2:
...
How about:
conditions = [a, b, c]
if any(conditions) and not all(conditions):
...
Other variant:
if 1 <= sum(map(bool, conditions)) <= 2:
...
回答 2
这个问题已经有很多被高度认可的答案和一个被接受的答案,但是到目前为止,所有这些都被表达布尔问题的各种方法分散了注意力,并且错过了关键点:
我有一个可以接收零或三个命令行参数的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)
是的,它应该是一个选项而不是位置参数,因为它毕竟是optional。
编辑: 为了解决LarsH在注释中的问题,下面是一个示例示例,如果您确定要使用3个或0个位置 args的接口,可以如何编写它。我认为以前的接口是更好的样式,因为可选参数应该是 options,但是出于完整性考虑,这是一种替代方法。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)
以下是一些用法示例:
# 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
This question already had many highly upvoted answers and an accepted answer, but all of them so far were distracted by various ways to express the boolean problem and missed a crucial point:
I have a python script that can receive either zero or three command
line arguments. (Either it runs on default behavior or needs all three
values specified)
This logic should not be the responsibility of your code in the first place, rather it should be handled by argparse
module. Don’t bother writing a complex if statement, instead prefer to setup your argument parser something like this:
#!/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)
And yes, it should be an option not a positional argument, because it is after all optional.
edited: To address the concern of LarsH in the comments, below is an example of how you could write it if you were certain you wanted the interface with either 3 or 0 positional args. I am of the opinion that the previous interface is better style, because optional arguments should be options, but here’s an alternative approach for the sake of completeness. Note the overriding kwarg usage
when creating your parser, because argparse
will auto-generate a misleading usage message otherwise!
#!/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)
Here are some usage examples:
# 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
回答 3
我会去:
conds = iter([a, b, c])
if any(conds) and not any(conds):
# okay...
我认为这应该有效地短路
说明
通过做conds
一个迭代器,any
如果有任何一项为true ,则第一次使用will将使迭代器短路并使迭代器指向下一个元素。否则,它将消耗整个列表,并且为False
。下一个any
将迭代器中的其余项取为零,并确保没有其他任何真值…如果有,则整个语句不可能为真,因此就没有一个唯一元素(因此短路再次)。最后一个any
将返回False
或将耗尽iterable和be True
。
注意:以上检查是否仅设置了一个条件
如果要检查是否设置了一个或多个项目,但没有设置每个项目,则可以使用:
not all(conds) and any(conds)
I’d go for:
conds = iter([a, b, c])
if any(conds) and not any(conds):
# okay...
I think this should short-circuit fairly efficiently
Explanation
By making conds
an iterator, the first use of any
will short circuit and leave the iterator pointing to the next element if any item is true; otherwise, it will consume the entire list and be False
. The next any
takes the remaining items in the iterable, and makes sure than there aren’t any other true values… If there are, the whole statement can’t be true, thus there isn’t one unique element (so short circuits again). The last any
will either return False
or will exhaust the iterable and be True
.
note: the above checks if only a single condition is set
If you want to check if one or more items, but not every item is set, then you can use:
not all(conds) and any(conds)
回答 4
英文句子:
“如果是a或b或c,但不是全部”
转换为以下逻辑:
(a or b or c) and not (a and b and c)
单词“但是”通常表示连词,即“和”。此外,“所有这些”转换为的条件结合:这种情况下,和该条件下,和其它条件。“ not”反转整个连接。
我不同意接受的答案。作者忽略了将最直接的解释应用于规范,而忽略了应用戴摩根定律(De Morgan’s Law)将表达式简化为更少的运算符:
not a or not b or not c -> not (a and b and c)
同时声称答案是“最小形式”。
The English sentence:
“if a or b or c but not all of them”
Translates to this logic:
(a or b or c) and not (a and b and c)
The word “but” usually implies a conjunction, in other words “and”. Furthermore, “all of them” translates to a conjunction of conditions: this condition, and that condition, and other condition. The “not” inverts that entire conjunction.
I do not agree that the accepted answer. The author neglected to apply the most straightforward interpretation to the specification, and neglected to apply De Morgan’s Law to simplify the expression to fewer operators:
not a or not b or not c -> not (a and b and c)
while claiming that the answer is a “minimal form”.
回答 5
True
如果三个条件之一并且只有一个是,则返回此值True
。可能是您在示例代码中想要的。
if sum(1 for x in (a,b,c) if x) == 1:
This returns True
if one and only one of the three conditions is True
. Probably what you wanted in your example code.
if sum(1 for x in (a,b,c) if x) == 1:
回答 6
关于:(唯一条件)
if (bool(a) + bool(b) + bool(c) == 1):
注意,如果您同时允许两个条件,也可以这样做
if (bool(a) + bool(b) + bool(c) in [1,2]):
What about: (unique condition)
if (bool(a) + bool(b) + bool(c) == 1):
Notice, if you allow two conditions too you could do that
if (bool(a) + bool(b) + bool(c) in [1,2]):
回答 7
需要明确的是,您要基于多少个参数为逻辑TRUE(在使用字符串参数的情况下-不为空)做出决定?
argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)
然后您做出了决定:
if ( 0 < argsne < 3 ):
doSth()
现在的逻辑更加清晰了。
To be clear, you want to made your decision based on how much of the parameters are logical TRUE (in case of string arguments – not empty)?
argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)
Then you made a decision:
if ( 0 < argsne < 3 ):
doSth()
Now the logic is more clear.
回答 8
为何不仅仅计算它们呢?
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
And why not just count them ?
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
回答 9
如果您不介意有点晦涩难懂,则可以简单地滚动一下0 < (a + b + c) < 3
,true
如果您有一个和两个真实的陈述,则返回,如果全部为假或没有为假,则返回false。
这也简化了您是否使用函数来评估布尔值,因为您只评估了一次变量,这意味着您可以内联编写函数,而无需临时存储变量。(例如:0 < ( a(x) + b(x) + c(x) ) < 3
。)
If you don’t mind being a bit cryptic you can simly roll with 0 < (a + b + c) < 3
which will return true
if you have between one and two true statements and false if all are false or none is false.
This also simplifies if you use functions to evaluate the bools as you only evaluate the variables once and which means you can write the functions inline and do not need to temporarily store the variables. (Example: 0 < ( a(x) + b(x) + c(x) ) < 3
.)
回答 10
问题表明您需要全部三个参数(a和b以及c),或者都不需要(而不是(a或b或c))
这给出:
(a and b and c)否(a或b或c)
The question states that you need either all three arguments (a and b and c) or none of them (not (a or b or c))
This gives:
(a and b and c) or not (a or b or c)
回答 11
据我了解,您有一个接收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
”值不会传递到此方法中。
As I understand it, you have a function that receives 3 arguments, but if it does not it will run on default behavior. Since you have not explained what should happen when 1 or 2 arguments are supplied I will assume it should simply do the default behavior. In which case, I think you will find the following answer very advantageous:
def method(a=None, b=None, c=None):
if all([a, b, c]):
# received 3 arguments
else:
# default behavior
However, if you want 1 or 2 arguments to be handled differently:
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?)
note: This assumes that “False
” values will not be passed into this method.
回答 12
如果使用条件迭代器,则访问速度可能很慢。但是,您不需要一次访问每个元素,也不总是需要读取所有元素。这是可与无限生成器一起使用的解决方案:
#!/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])
If you work with an iterator of conditions, it could be slow to access. But you don’t need to access each element more than once, and you don’t always need to read all of it. Here’s a solution that will work with infinite generators:
#!/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])
回答 13
当每一个给定bool
的True
,或者当每一个给定bool
的False
……
他们都是彼此相等!
所以,我们只需要找到两个元素的计算结果为不同的这bool
小号
知道至少有一个True
和至少一个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)
我也写了一些处理多个可迭代对象的代码,但是我从这里删除了它,因为我认为这没有意义。但是,这里仍然可用。
When every given bool
is True
, or when every given bool
is False
…
they all are equal to each other!
So, we just need to find two elements which evaluates to different bool
s
to know that there is at least one True
and at least one False
.
My short solution:
not bool(a)==bool(b)==bool(c)
I belive it short-circuits, cause AFAIK a==b==c
equals a==b and b==c
.
My generalized solution:
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)
I wrote also some code dealing with multiple iterables, but I deleted it from here because I think it’s pointless. It’s however still available here.
回答 14
基本上,这是“一些(但不是全部)”功能(与any()
和all()
内置功能相比)。
这意味着结果之间应该有False
s 和 True
s。因此,您可以执行以下操作:
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
This is basically a “some (but not all)” functionality (when contrasted with the any()
and all()
builtin functions).
This implies that there should be False
s and True
s among the results. Therefore, you can do the following:
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)
One advantage of this code is that you only need to iterate once through the resulting (booleans) items.
One disadvantage is that all these truth-expressions are always evaluated, and do not do short-circuiting like the or
/and
operators.