在 dict 解包期间内部发生了什么?

What happened internally during dict unpacking?

我想知道解压字典时调用了哪些方法,以便我可以自定义过程?

我想下面的 hack 会向我展示在 Dict 实例的任何方法访问期间调用的方法:

class Dict(dict):
    def __getattribute__(self, name):
        print(name)
        return super().__getattribute__(name)

但是下面的session显示没有调用dict解包的方法?

In [1]: d = Dict(a=1)
__class__
__class__

In [2]: {**d}
Out[2]: {'a': 1}

那么这里到底发生了什么?如何自定义解包过程?


编辑

我不认为这个问题与 the other one 重复。即使实现了该问题的答案中提到的所有特殊方法,在 dict 解包期间也没有调用它们。

In [66]: class Dict(dict):
    ...:     def __getattribute__(self, name):
    ...:         print(name)
    ...:         return super().__getattribute__(name)
    ...:     def keys(self):
    ...:         print("hello")
    ...:         return super().keys()
    ...:     def __getitem__(self, key):
    ...:         print("hello")
    ...:         return super().__getitem__(key)
    ...:     def __len__(self):
    ...:         print("hello")
    ...:         return super().__len__()
    ...:     def __iter__(self):
    ...:         print("hello")
    ...:         return super().__iter__()
    ...:     

In [67]: d = Dict(a=1)
__class__
__class__

In [68]: {**d}
Out[68]: {'a': 1} 

您可以看到没有任何 print 行被调用。所以我的问题仍然没有答案。

FWIW,python 版本是 Python 3.6.5。

这是 a bug in how Python handled dict subclasses that was fixed in late September, 2018

the fix 之前,任何 dict 子类都使用具体的 dict-特定的 C API 方法(绕过所有动态定义的覆盖)。修复后,代码检查 __iter__(好吧,C 等价物,tp_iter)是否已被覆盖,如果是,它不会使用 dict 的快速路径。检查 __iter__ 有点错误 IMO(它实际上 使用 的唯一两种方法是 keys__getitem__),但是如果你重写 keys,你可能也应该重写 __iter__,所以这样做并不是什么大麻烦(在许多情况下,一个可以是另一个的别名,或者至多是一个 slim wrapper,这取决于是否keys returns 迭代器或视图对象)。

鉴于错误修复是最近的,您需要升级 Python 才能获得好处。 3.6.7 和 3.7.1 是它们各自的次要版本行中第一个包含修复程序的微版本,因此升级到任何一个都应该可以使您的代码正常工作。