1

这里找到的递归字典的解决方案中,我看到了以下 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]
4

2 回答 2

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],则等价将不再成立。

于 2013-02-27T22:04:02.417 回答
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        

最右边的值(在我type(self)()的实际情况下为 1)被加载到堆栈上(想想“局部变量”)然后复制到堆栈上(在基于堆栈的 VM 操作中消耗堆栈,所以如果你想“保持"一个值,您需要制作多个副本)然后分配,然后复制,然后分配等等。即,转换回 Python 这类似于:

def f():
    t = 1
    a = t
    b = t
    c = t
    return a

编译器甚至保留了从左到右 (a, b, c) 的赋值顺序。

于 2013-02-27T22:01:08.407 回答