Python - 多重赋值语句有优缺点吗?
在这里,我看到一个关于递归字典的解决方案,链接在这里。里面有一个Python的写法:
class RecursiveDict(dict):
"""Implementation of perl's autovivification feature."""
def __missing__(self, key):
value = self[key] = type(self)()
return value
关于这一行代码value = self[key] = type(self)()
:这行代码相比于下面的代码,有什么优点或缺点吗?还是说这只是语法上的不同?
self[key] = type(self)()
value = self[key]
2 个回答
1
这只是语法问题。有时候这样写代码更容易读懂,有时候则不然。
那么到底发生了什么呢?另一个回答让我产生了好奇:我在下面评论说编译器可能使用了“寄存器或变量”,结果确实是这样。我们来看看一个简单函数的反编译:
>>> import dis
>>> def f():
... a = b = c = 1
... return a
...
>>> dis.dis(f)
2 0 LOAD_CONST 1 (1)
3 DUP_TOP
4 STORE_FAST 0 (a)
7 DUP_TOP
8 STORE_FAST 1 (b)
11 STORE_FAST 2 (c)
3 14 LOAD_FAST 0 (a)
17 RETURN_VALUE
最右边的值(在我的例子中是1,实际情况是type(self)()
)被加载到栈上(可以理解为“局部变量”),然后在栈上复制一份(在基于栈的虚拟机中,操作会消耗栈上的值,所以如果你想“保留”一个值,就需要多复制几份),接着进行赋值,然后再复制,再赋值,依此类推。换句话说,转换回Python代码大概是这样的:
def f():
t = 1
a = t
b = t
c = t
return a
编译器甚至保持了从左到右的赋值顺序(a, b, c)。
4
这主要是语法上的简化,但并不是你列出的那些语句;相反,它等价于
value = type(self)()
self[key] = value
要查看区别,可以在你的Python提示符下输入以下内容:
>>> class FakeDict(object):
... def __setitem__(self, k, v):
... pass
... def __getitem__(self, k):
... raise KeyError("boom!")
...
>>> d = FakeDict()
>>> x = d[1] = 42
>>> d[1] = 42
>>> x = d[1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __getitem__
KeyError: boom!
当然,如果你的 dict
使用得当,这个区别就不大,但
self[key] = type(self)()
value = self[key]
在第二行中确实会多做一次 dict
的查找,所以在循环中使用简写可能会提高性能。
一般情况下,这其实比我上面说的要复杂一点。简写的赋值实际上等价于
__some_temporary = type(self)()
value = __some_temporary
self[key] = __some_temporary
而简化的形式 value = type(self)(); self[key] = value
之所以成立,仅仅是因为 value
是一个简单的局部变量。如果 value
被替换成像 container[key]
这样的表达式,而这个表达式可能会失败,那么这种等价关系就不再成立了。