我有一个可以接收零个或三个命令行参数的 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):
?
len(sys.argv)
将始终至少为 1:它将可执行文件包含为 argv[0]
。
if not (a and b and c)
(零个参数),然后说if a and b and c
(所有三个参数)吗?
如果您的意思是最小形式,请使用:
if (not a or not b or not c) and (a or b or c):
这翻译了你的问题的标题。
更新:正如 Volatility 和 Supr 正确所说,您可以应用德摩根定律并获得等价物:
if (a or b or c) and not (a and b and c):
我的建议是使用对您和其他程序员更重要的形式。第一个意思是“有些东西是假的,但也是真的”,第二个意思是“有些东西是真的,但不是一切”。如果我要在硬件中进行优化或执行此操作,我会选择第二个,这里只选择最易读的(还要考虑您将要测试的条件及其名称)。我选了第一个。
怎么样:
conditions = [a, b, c]
if any(conditions) and not all(conditions):
...
其他变体:
if 1 <= sum(map(bool, conditions)) <= 2:
...
2
,例如 True
,则 sum(conditions)
可能会出错。
sum(map(bool, conditions))
这个问题已经有很多被高度评价的答案和一个被接受的答案,但到目前为止,他们都被各种表达布尔问题的方式分散了注意力,错过了一个关键点:
我有一个可以接收零个或三个命令行参数的 python 脚本。 (要么以默认行为运行,要么需要指定所有三个值)
这个逻辑首先不应该是库代码的责任,而应该由命令行解析(通常是 Python 中的 argparse
模块)处理。不要费心编写复杂的 if 语句,而是更喜欢像这样设置参数解析器:
#!/usr/bin/env python
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo', nargs=3, default=['x', 'y', 'z'])
args = parser.parse_args()
print(args.foo)
是的,它应该是一个选项而不是位置参数,因为它毕竟是可选的。
已编辑: 为了解决评论中 LarsH 的担忧,下面是一个示例,说明如果您确定要使用 3 或 0 的接口,如何编写它 < em>位置参数。我认为以前的界面风格更好(因为 optional 参数应该是 options),但为了完整起见,这里有另一种方法。请注意,我们在创建解析器时会覆盖 kwarg usage
,否则 argparse
会自动生成误导性使用消息!
#!/usr/bin/env python
import argparse
parser = argparse.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
$ ./three_or_none.py
['x', 'y', 'z']
# explicit case
$ ./three_or_none.py 1 2 3
['1', '2', '3']
# example failure mode
$ ./three_or_none.py 1 2
usage: three_or_none.py [-h] [a b c]
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] = [True, True, False]
,您的代码不应该“打印”False
,而预期的输出是 True
?
iter
之前已完全构建。 any
和 all
会懒惰地消耗列表,没错,但是当您到达那里时,列表已经被完全评估了!
英文句子:
“如果 a 或 b 或 c 但不是全部”
翻译成这个逻辑:
(a or b or c) and not (a and b and c)
“但是”这个词通常意味着一个连词,也就是“和”。此外,“所有这些”转化为条件的结合:这个条件、那个条件和其他条件。 “不”反转了整个连词。
我不同意接受的答案。作者忽略了对规范应用最直接的解释,也忽略了应用德摩根定律将表达式简化为更少的运算符:
not a or not b or not c -> not (a and b and c)
同时声称答案是“最小形式”。
怎么样:(独特的条件)
if (bool(a) + bool(b) + bool(c) == 1):
请注意,如果您也允许两个条件,则可以这样做
if (bool(a) + bool(b) + bool(c) in [1,2]):
1 <= bool(a) + bool(b) + bool(c) <= 2
。
如果三个条件中的一个且只有一个是 True
,则返回 True
。可能是您在示例代码中想要的。
if sum(1 for x in (a,b,c) if x) == 1:
需要明确的是,您想根据有多少参数是逻辑 TRUE(如果是字符串参数 - 不为空)来做出决定?
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
,如果全部为假或没有一个为假,则返回假。
如果您使用函数来评估布尔值,这也会简化,因为您只评估一次变量,这意味着您可以内联编写函数而无需临时存储变量。 (示例:0 < ( a(x) + b(x) + c(x) ) < 3
。)
该问题表明您需要所有三个参数(a 和 b 和 c)或不需要所有三个参数(不是(a 或 b 或 c))
这给出了:
(a and b and c) or not (a or b or 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
”值不会传递到此方法中。
如果您使用条件迭代器,访问速度可能会很慢。但是您不需要多次访问每个元素,并且您并不总是需要阅读所有元素。这是一个适用于无限生成器的解决方案:
#!/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])
当每个给定的 bool
是 True
时,或者当每个给定的 bool
是 False
时...
它们都彼此相等!
因此,我们只需要找到两个评估为不同 bool
s
的元素即可知道至少有一个 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)
我还写了一些处理多个迭代的代码,但我从这里删除了它,因为我认为它没有意义。但是它仍然可用here。
这基本上是“一些(但不是全部)”功能(与 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
运算符那样做 short-circuiting。
.issuperset
而不仅仅是检查长度 2,bool
无论如何都不能返回 True 和 False 以外的任何东西。为什么将 lambda(读取:匿名函数)分配给名称而不是仅使用 def?
def
,则需要 return
。我认为这个解决方案的普遍性很好。没有必要将自己限制为布尔值,问题本质上是“我如何确保所有这些元素都出现在我的列表中”。如果您不需要可变性,为什么要 set
?如果您不需要性能,则更多的不变性总是更好。
tee
itertools.tee
但 1)我正在寻找一个简单/足够小以保证复制粘贴的单行,2)您已经给出了使用该技术的答案 :-)
不定期副业成功案例分享
if not (a and b and c) and (a or b or c)
if (a or b or c) and not (a and b and c)
以完美匹配标题;)if any([a,b,c]) and not all([a,b,c])