Python中类似于Ruby的 ||= 运算符的等价写法

19
为了检查变量是否存在,如果存在则使用原始值,否则使用新分配的值。 在Ruby中,可以这样写: var ||= var_new 那么在Python中如何编写呢?
PS: 我不知道“||=”的名称,因此无法在Bing中搜索。

1
没有完美的并行结构与此相匹配。try except 可能是最接近的。 - dawg
1
那么在 Ruby 中使用这个语法的时候,你不知道 var 是否已经在当前作用域中被定义了?在 Python 中,这是一件通常会尽力避免的事情。或者只是 var 是空的,就像 Python 中的 None 一样? - Marius
我更倾向于Ruby而不是Python,但我认为Python有一些记忆化装饰器与这个讨论相关... - Benjamin Oakes
1
如果目标只是确保在通过可能(但不一定会)写入变量的代码块后,更标准的Python表达式就是在进入该代码块之前将其设置为默认值; 如果它没有被修改,则保留原始值。 不确定您的代码是否具有某些特殊需求,无法使用此方法。 - Corley Brigman
重複的問題: https://dev59.com/-G865IYBdhLWcg3wNLw7 但我更喜歡這裡的答案。 - CTS_AE
5个回答

29

有些人不确定条件赋值运算符(||=)的作用,也有一些人误解了Ruby中变量的生成方式。大家应该阅读这篇文章来了解这个主题。引用其中一句话:

一个普遍的误解是,a ||= b等同于a = a || b,但它的行为类似于a || a = b

a = a || b中,每次运行语句都会将a设置为某个值,而在a || a = b中,只有当a逻辑上为false时(即如果它是nil或false),才会设置a,因为||是“短路”的。也就是说,如果||比较的左边为true,则无需检查右边。

还有一个非常重要的提示:

......即使没有运行,变量赋值也会立即召唤该变量存在。

# Ruby
x = 10 if 2 == 5
puts x

尽管第一行不会被执行,但是变量x将在第二行存在,并且不会引发任何异常。

这意味着Ruby将绝对“确保”在任何右侧条件发生之前有一个变量容器可以放置值。如果a未定义,则||=不会进行赋值;如果a为假值(再次提醒,falsenil-nil是Ruby中默认的“无”的值),它将进行赋值,同时保证a已定义。

那么对于Python来说意味着什么?

好吧,如果a被定义了,接下来的代码:

# Ruby
a ||= 10

实际上等同于:

# Python
if not a:
    a = 10

以下是:

# Either language
a = a or 10

虽然 "is close" 与之前的示例相似,但它总是分配一个值,而前面的示例没有。

如果a未定义,则整个操作更接近于:

# Python
a = None
if not a:
    a = 10

a 未定义时,a ||= 10 的一个非常明确的例子是:

# Ruby
if not defined? a
    a = nil
end

if not a
    a = 10
end

归根结底,||= 运算符在任何“Pythonic”方式下都不能完全地被翻译为 Python,因为它依赖于 Ruby 中底层变量的生成。


1
在执行 a=None; if not a: a=10 后,a 肯定是 10,对吗?还不如直接写成 a=10 - dawg
@dawg 只是明确展示在 Python 中的样子,与 Ruby 中本来的方式相比。 - Oka
我做了一些编辑,希望这能稍微澄清一些事情。 - Oka
以前我习惯使用 if not 'var' in globals(): 这样的写法,现在简化了一点。 - ZK Zhao
2
听起来原问题中关于 ||= 的解释是错误的。能不能有更熟悉 Ruby 的人修改一下问题,正确地说明该操作符的行为呢? - wim
需要注意的一点是,即使使用示例 if not var_name:,pylint 也会抱怨您在变量声明之前使用了它。编辑:算了,似乎是我的作用域问题,在函数中使用但在外部定义(这本来就不好,但我想缓存一个变量/查找)。 - CTS_AE

5

在Python中,没有特别优雅的方法,因为把自己陷入不知道变量是否存在的情况并不是特别优雅的做法。然而,以下方法似乎最接近:

try:
    var
except NameError:
    var = var_new

我不熟悉 Ruby 中的 ||= 运算符,但从你所描述的来看,这个块应该具有正确的行为。也就是说,如果 var 已经存在,则我们保留其绑定状态;否则,我们将其设置为 var_new


我会使用 _ = var,这样你就不会在控制台上得到无意义的输出。 - Mark Ransom
1
@MarkRansom,除非您在REPL中运行,否则您不会得到此代码的控制台输出。 - wim

4
这大致而言是你想要的,并且措辞生动地表达出来:

var = var or var_new

对于Python中“不存在”的变量,其规则非常严格;如果未给var赋值,则会抛出异常。然而,如果var 求值为falsey,将会接受var_new的值。

我说这是“惯用法”,因为在Python中,这种情况的惯用较大结构如下:

var1 = None
var2 = None
var3 = None

# ... code here that may or may not set var1 through var3 ...

var1 = var1 or "default1"
var2 = var2 or "default2"
var3 = var3 or "default3"

注意,Python对"falsey"有一个相当广泛的概念。只有在var未被赋值为零、False或任何被认为是“空”的对象(例如""[]{}等)时,该结构才有效。如果你真的想要它仅在None上触发,你必须写更冗长的代码。
var = var if var is not None else var_new

相反,我通常会寻找另一种解决更大问题的方法,而不是这样做。

最后,如果你能像这样组织你的代码...

var1 = "default1"
var2 = "default2"
var3 = "default3"

# ... code here that may or may not set var1 through var3 ...

如果可以的话,你应该这样做,因为这样更短小精悍,更简单易懂,并且避免了None与falsey问题。


1
在Python中,如果a不存在,a=a or 10仍然会产生NameError。而在Ruby中,a||=10会创建名为a的变量(如果它不存在),并将10赋值给它。 - dawg
@dawg 是的,我不认为我所写的意味着其他的含义。 - zwol

3
不太安全,然而另一种选择是:
var = locals().get('var', var_new)

它可能是最接近的,但您可能还需要在globals()中进行检查以获得完整的解决方案... - Corley Brigman
2
@CorleyBrigman 可能会有同名的局部变量和全局变量,因此我只涵盖了局部作用域。 - Ozgur Vatansever
我几乎没有使用过Python字典,并且以前从未使用过locals()或globals(),但似乎var = locals().get('var') if 'var' in locals().keys() else globals().get('var',var_new)可以处理这两个。 - user380772

1
try: var
except NameError: var = var_new

或者

var = locals().get('var', var_new)

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