在Python中压缩`x if x else y`语句

44

我非常熟悉 Python 中三元运算符的用法:

value = foo if something else bar

我的问题很简单:在没有先前赋值的情况下,是否有方法可以从一个返回操作数(if ...else ...)中引用正在评估的项?

这里的动机是有时我在if ...表达式中使用的正是我想要作为三元运算结果的东西。然而对于小表达式来说重复它并没有问题,但对于更长的表达式来说,它会变得有些麻烦。以这个为例:

value = info.findNext("b") if info.findNext("b") else "Oompa Loompa"

1
嗯,Oompa Loompa很有趣! - Muhammad Talha Akbar
@AspiringAqib,我们没有作业,但我们仍然可以享受一些翁巴族人(: - Rubens
@AspiringAqib 是的,我想这已经足够了,甚至可能是最好的解决方案(: 最好保持简单原则 (: - Rubens
@abarnert findNext 不是我编写的函数;这是来自 BeautifulSoup 解析器 API。 - Rubens
@abarnert 我会写太多的两行包装器^^我对合并运算符感到满意。 - Rubens
显示剩余3条评论
3个回答

54

没有办法做到这一点,这是有意为之的。三元条件运算符只应该用于简单的情况。

如果想要多次使用计算结果,请将其放入临时变量中:

value = info.findNext("b")
value = value if value else "Oompa Loompa"

一旦你这样做,就会清楚你正在做一些傻事,实际上编写这个的Pythonic方式是:

value = info.findNext("b")
if not value:
    value = "Oompa Loompa"

这实际上比您最初的尝试少了5个按键。

如果您真的想节省按键,您可以改为这样做:

value = info.findNext("b") or "Oompa Loompa"

但是许多风格指南和BDFL都不鼓励这样做。

如果您只需要执行此操作一次,则最好更加明确。 如果您需要执行六次,将findNext设置为可选默认返回而不是None会更简单 - 就像所有那些内置和标准库函数一样:

def findNext(self, needle, defvalue=None):
    # same code as before, but instead of return None or falling off the end,
    # just return defvalue.

那么你可以这样做:

value = info.findNext("b", "Oompa Loompa")

返回 None 和返回 null 是一样的,因此同样糟糕 - user824425
1
@Rubens: 编写一行解决方案的Pythonic方式是将任何复杂的内容封装在一个明确的函数中。如果您能够在findNext中添加defval,请这么做;如果不能,请编写一个包装器。不同的语言有不同的习惯用法(例如,在Haskell中,给你只会调用一次的函数命名是代码异味),而不同的语言更强烈地遵循它们的惯例(例如,在perl中,“只有一种方法可以做到这一点”被认为是一个错误,而不是一个特性)。但是如果您选择了Python,您应该学习惯用的Python方式。 - abarnert
@Tinctorius:在示例中加入 None 是因为我假设这是 OP 原始的 findNext 返回的内容,就像 dict.get 和同一个模型上的所有其他方法一样。它显然会返回 某个 值为 falsey,而没有 真正 的结果可以是 falsey,否则 OP 的代码就是错误的,我假设它不是这样的。无论如何,这与 OP 的问题都无关。(但是,作为侧面说明:你认为 Haskell 的 Nothing 违反了空安全吗?如果不是,除了静态和动态类型之外,NothingNone 有什么区别?) - abarnert
@abarnert:如果你使用动态类型,那么我强烈推荐“默认返回值”方法。抛出KeyError也可以,但人们可能会觉得这会使代码变得混乱。我认为Haskell的Maybe根本不违反空安全性。一个不安全的语言默认将许多类型设为可为空。Maybe允许您选择加入可空性。Haskell唯一不安全的方式是通过其底部值(即难以捕获的终止,如undefined,或非终止,如fix id),但要解决这个问题,您的语言将变得更难使用和更难实现。 - user824425
不错 - 可能(_而不是返回None...只需返回defvalue_)-> 因此 def findNext(self, needle, defvalue=u'DEFVALUE'): - Mr_and_Mrs_D
显示剩余3条评论

22

完全不要使用 if ... else。相反,利用Python的合并运算符。

value = info.findNext("b") or "Oompa Loompa"

1
这绝对是一种捷径,@Rubens。x or y 应该编译成与 if bool(x) then x else y 相同的代码。bool 调用了 .__nonzero__() - user824425
1
你_可以_这样做,但这并不意味着你_应该_这样做。如果你重复这个动作足够多次以至于节省5个按键的时间很重要,那就修改findNext方法来接受一个默认值。否则,明确地写出来。 - abarnert
1
如果混合了任何“falsy”内容,这种情况可能会让你陷入麻烦。考虑使用[]或'default' - 这可能是您正在寻找的,也可能不是。 - Jonathan

1
如果您拥有Python 3.8或更高版本,则可以使用:=运算符。在您的示例中,它会像这样:
value = bb if (bb := info.findNext("b")) else "Oompa Loompa"

1
实际上,海象运算符是在Python 3.8中引入的。 - S.B
你忘记在等号前面加上冒号 : 了。 - S.B
感谢您指出,我已经编辑了我的回答。 - arunkannawadi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接