在赋值右侧使用'or'符合Python风格吗?
情况
(注意:以下情况只是个例子。这个问题适用于任何可以判断真假(bool)的情况)
如果用户没有提供自定义列表,就应该使用默认列表:
default_list = ...
custom_list = ...
if custom_list:
list = custom_list
else:
list = default_list
你可以把它简化为:
default_list = ...
custom_list = ...
list = custom_list if custom_list else default_list
根据 这个链接 ...
表达式
x or y
首先会判断x
;如果 x 为真,它就返回 x 的值;否则,就会判断 y,并返回 y 的值。
... or
并不是返回一个真假值,而是返回第一个不是假的值。因此,下面的代码是有效的:
list = custom_list or default_list
这和 C# 的空合并运算符有点像,但在 Python 中应该称为 假合并运算符,它返回第一个 非假 的参数。
问题
最后一个例子似乎更容易阅读,但这算不算是 Python 风格呢?
无论是 pep8(一个代码风格检查工具)还是 pylint(另一个检查工具)都没有提出问题。
2 个回答
是的,以前大家常常用 or
来利用它的短路特性,这在语言中添加条件表达式之前是很普遍的做法。
用下面的方式是完全符合 Python 风格的:
foo = bar or baz
这样可以在 bar
为假(在布尔上下文中计算为假)时,使用一个默认值。因为它有短路特性,所以你还可以做到:
foo = bar or expensive_calculation(baz) # only if bar is false-y
这样就不会在 bar
为真(在布尔上下文中计算为真)时执行 expensive_calculation()
。同样,你也可以用 and
来确保某些前提条件被满足:
foo = bar and bar(baz) # call `bar()` only if it is truth-y
不过,对于以下情况,你 应该 使用条件表达式:
foo = bar and spam or eggs
这是条件表达式的目的所在。它的想法是,如果 bar
为真,就选择 spam
,否则选择 eggs
。但如果 spam
为假,这个逻辑就会出问题!这常常是错误的来源,而
foo = spam if bar else eggs
在 bar
为真时,总是选择 spam
。
这完全是有效的,你可以这样使用。甚至连关于or
的文档里也有类似的例子。
需要注意的是,
and
和or
并不限制返回的值和类型为False
和True
,而是返回最后一个被计算的参数。这有时候很有用,比如,如果s
是一个字符串,当它为空时应该用默认值替代,那么表达式s or 'foo'
就能得到你想要的值。
不过,or
方法有一个局限性。如果你想故意允许一个非真值,那么用这个方法是不可能的。
假设你想允许一个空列表
my_list = [] or default_list
这总是会返回default_list
。比如,
print [] or [1, 2, 3]
# [1, 2, 3]
但是使用条件表达式我们可以这样处理
custom_list if isinstance(custom_list, list) else default_list
翻看一下旧文档,引用一下BDFL的常见问题解答,
4.16. 问:有没有类似C语言的“?:”三元运算符?
答:没有直接的等价物。在很多情况下,你可以用
a and b or c
来模拟a?b:c
,但有一个缺陷:如果b
是零(或为空,或None
——任何测试为假的东西),那么就会选择c
。在很多情况下,你可以通过查看代码证明这种情况不会发生(例如,因为b
是一个常量或有一个永远不会为假的类型),但一般来说,这可能会成为一个问题。Steve Majewski(还是Tim Peters?)建议了以下解决方案:
(a and [b] or [c])[0]
。因为[b]
是一个单元素列表,它永远不会为假,所以不会走错路径;然后对整个表达式应用[0]
就能得到你真正想要的b
或c
。虽然看起来不太优雅,但在某些情况下,确实不方便用'if'重写代码时,这个方法能帮你解决问题。