我一直在重构一些相当狡猾的代码,并遇到了以下相当奇怪的构造:
#!/usr/bin/env python2.7
# ...
if (opts.foo or opts.bar or opts.baz) is None:
# (actual option names changed to protect the guilty)
sys.stderr.write("Some error messages that these are required arguments")
......我想知道这是否会产生任何可以想象的感觉。
我将其更改为:
#!/usr/bin/env python2.7
if None in (opts.foo, opts.bar, opts.baz):
# ...
我确实启动了一个解释器,并且实际上尝试了第一个构造...它似乎只有在值和时才会起作用,这些错误值的最后一个是None。 (换句话说,CPython的实现似乎从 或 表达式链中返回第一个真值或最后一个假值。)
我仍然怀疑正确的代码应该使用 any() 或 all() 构建-ins加上2.5(有问题的代码已经要求2.7)。我还不确定哪个是首选的/预期的语义,因为我刚开始这个项目。
那么这个原始代码是否有意义呢?
答案 0 :(得分:5)
它的行为方式是因为or
是一个短路运算符,详细信息在docs。因此,您的第一个if
语句等于:
if opts.baz is None
我们可以猜出该代码的作者应该是什么。我认为,正如你所提到的,他想到了使用not all([opts.foo, opts.bar, opts.baz])
。
答案 1 :(得分:5)
短路行为导致foo or bar or baz
返回布尔值为true的三个值中的第一个值,或者如果all为boolean-false则返回最后一个值。所以它基本上意味着“如果一切都是假的,最后一个是无”。
您的更改版本略有不同。例如,如果if None in (opts.foo, opts.bar, opts.baz)
为无,而其他两个为1,则if
将输入opts.foo
块,而原始版本则不会(因为None or 1 or 1
将评估为1 ,这不是无)。当三个中的任何为None时,您的版本将输入if
,无论其他两个是什么,而原始版本只有在if > last 是None 和其他两个是boolean-false值。
您想要的两个版本中的哪一个取决于代码的其余部分的结构以及选项可能采用的值(特别是,它们是否可能具有除None之外的boolean-false值,例如False
或0
或空字符串)。直觉上你的版本似乎更合理,但如果代码中有这样的特殊技巧,你就不会知道可能会出现什么样的角落。
答案 2 :(得分:0)
我更喜欢
if any(i is None for i in (opts.foo, opts.bar, opts.baz))
因为它完全表达了预期的目标。
OTOH,
not all([opts.foo, opts.bar, opts.baz])
会检查是否存在错误,而不是None
。
原始代码似乎没有意义;它似乎是由一个不知道自己在做什么的人写的。
答案 3 :(得分:0)
让我们试试你的两个代码:
In [20]: foo = True
In [22]: bar = None
In [23]: baz = None
In [24]: foo or bar or baz
Out[24]: True
In [25]: (foo or bar or baz) is None
Out[25]: False
In [28]: ((foo or bar or baz) is None) == (None in (foo, bar, baz))
Out[28]: False
您可以看到您的重写与原始代码不同。
当您的所有变量都为无
时,您的第一个条件仅返回TrueIn [19]: (None or None or None) is None
Out[19]: True
所以你可以将你的第一个条件重写为:
if foo == bar == bar == None: