在python中有没有类似于ruby的表达式||=

Is there any expression in python that similar to ruby's ||=

我在Ruby中看到了一个有趣的表达:

1
a ||="new"

这意味着,如果未定义a,则"新"值将分配给a;否则,a将与其相同。它在进行数据库查询时很有用。如果设置了该值,我不想触发另一个数据库查询。

所以我在python中尝试了类似的思维方式:

1
a = a if a is not None else"new"

它失败了。我认为这是因为如果没有定义a,就不能在python中执行"a=a"。

所以我可以得出的解决方案是检查locals()和globals(),或者使用try…except表达式:

1
myVar = myVar if 'myVar' in locals() and 'myVar' in globals() else"new"

1
2
3
4
5
6
try:
    myVar
except NameError:
    myVar = None

myVar = myVar if myVar else"new"

正如我们所看到的,解决方案并不那么优雅。所以我想问一下,有没有更好的办法?


使用未定义的变量作为"默认值"有点像代码味道。正如您发现的,它失败了,因为如果a不存在,您就不能执行a = a。最好用一些默认值(如none)初始化变量,然后检查它。所以你找到了解决办法。唯一的一点是,您不应该让变量不倾斜,而是尝试使用"不存在"作为"缺少"值,而是应该显式初始化它。所以,不要这样做:

1
2
 # code with no initialization. . .
 a ||= blah

这样做:

1
2
3
4
a = None
# code
if a is None:
    a = blah


怎么样?

1
2
3
4
try:
    a = a
except NameError:
    a ="new"

它不是很短,但确实清楚地(至少对我来说)解释了代码的意图。


考虑一下使用记忆术,这是一种很好的Python。下面是一些python 2代码:

1
2
3
4
5
6
7
8
9
10
11
12
# Backport of Python 3's lru_cache
# pip install functools32
from functools32 import lru_cache

@lru_cache()
def expensive_db_query(arg):
    print"Running expensive db query for", arg
    return arg[::-1]

expensive_db_query("spam") # prints"Running expensive db query for spam"
expensive_db_query("spam") # No output
expensive_db_query("eggs") # prints"Running expensive db query for eggs"

如果要让缓存在经过足够的时间后忘记值并再次查询数据库,请检查iLialuk的LRU缓存。


在使用之前,尤其是在本地/全局变量中,您应该知道它,而不是猜测它在哪里。你可以写这段代码,但它不是Python。

1
2
3
a = None
# other codes
a = a or 'new'

没事吧


如果您真的想要这种行为,最好在Python中使用dict,例如:d.get('a', 'new')。这意味着你可以和globals()/locals().get('a', 'new')混在一起,但这通常不是用python做事情的方法——每个名称绑定都应该有一个初始值,即使它是某种sentinel值(如None)。

使用globals()和setdefault()的可怕示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> a
Traceback (most recent call last):
  File"<pyshell#66>", line 1, in <module>
    a
NameError: name 'a' is not defined
>>> globals().setdefault('a', 'new')
'new'
>>> a
'new'
>>> a = 'old'
>>> globals().setdefault('a', 'new')
'old'
>>> a
'old'


下面是一个由这个答案启发而来的变量,关于如何检查是否定义了一个变量:

1
a = a if"a" in vars() or"a" in globals() else"new"

它没有那么短,但至少有一条线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [1]: a
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-60b725f10c9c> in <module>()
----> 1 a

NameError: name 'a' is not defined

In [2]: a = a if"a" in vars() or"a" in globals() else"new"

In [3]: a
Out[3]: 'new'

In [4]: a ="A exists!"

In [5]: a = a if"a" in vars() or"a" in globals() else"new"

In [6]: a
Out[6]: 'A exists!'

也就是说,我同意Brenbarn的观点,您应该避免使用未定义变量的问题,只需在可能不设置该变量的块之前用a=None声明该变量即可。