8

我一直在重构一些相当笨拙的代码,并遇到了以下相当奇怪的结构:

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

我确实启动了一个解释器并实际尝试了第一个构造......它似乎只在值全部为假并且这些假值中的最后一个为无时才有效。(换句话说,CPython 的实现似乎从or表达式链中返回第一个真值或最后一个假值)。

我仍然怀疑正确的代码应该使用在 2.5 中添加的any()all()内置插件(有问题的代码已经需要 2.7)。我还不确定哪个是首选/预期的语义,因为我刚刚开始这个项目。

那么在任何情况下这个原始代码是有意义的吗?

4

4 回答 4

5

之所以这样做,是因为它or是一个短路运算符,详细信息在docs中。因此,您的第一if条语句等于:

if opts.baz is None

我们可以猜出该代码的作者所期望的。我认为,正如你提到的,他考虑过使用not all([opts.foo, opts.bar, opts.baz]).

于 2013-04-05T07:38:42.193 回答
5

短路行为会导致foo or bar or baz返回三个布尔值中的第一个值,如果都是布尔值,则返回最后一个值。所以它基本上意味着“如果所有都是假的,最后一个是无”。

您更改的版本略有不同。 if None in (opts.foo, opts.bar, opts.baz)例如,if如果为 None 并且其他两个为 1,则将进入该块opts.foo,而原始版本不会(因为None or 1 or 1将评估为 1,这不是 None)。if当三个中的任何一个为 None 时,您的版本将输入,而不管其他两个是什么,而原始版本将if仅在最后一个为 None并且其他两个是任何 boolean-false 值时输入。

您想要两个版本中的哪一个取决于其余代码的结构以及选项可能采用的值(特别是,它们是否可能具有除 None 之外的布尔假值,例如False0或空字符串)。直觉上你的版本似乎更合理,但如果代码中有这样的特殊技巧,你永远不知道会出现什么极端情况。

于 2013-04-05T07:47:04.840 回答
0

我会选择

 if any(i is None for i in (opts.foo, opts.bar, opts.baz))

因为它准确地表达了预期的目标。

哦,

not all([opts.foo, opts.bar, opts.baz])

确实检查错误,而不是检查None.

原始代码似乎没有意义;它似乎是由不知道自己在做什么的人写的。

于 2013-04-05T07:54:05.013 回答
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

您可以看到您的重写与原始代码不同。

只有当所有变量都为 None 时,您的第一个条件才返回 True

In [19]: (None or None or None) is None
Out[19]: True

所以你可以将你的第一个条件重写为:

if foo == bar == bar == None:
于 2013-04-05T08:00:53.897 回答