Python 的 dict 无法识别自己的密钥

EZL*_*ner 1 python dictionary python-3.6

编辑2:最后我能够生成一个MWE:

from typing import Generic, TypeVar
T = TypeVar('T')

class Cache:
    __dict = {}

    @classmethod
    def add(cls, item):
        cls.__dict[item] = (item, [item, item, item, {item: item}])
        print('On setting:', item in cls.__dict)

    def __init_subclass__(cls, **kwargs):
        Cache.add(cls)


class Class(Cache, Generic[T]):
    pass

d = Cache._Cache__dict
tp = list(d)[0]
print('On checking:', tp in d)
Run Code Online (Sandbox Code Playgroud)

在 python 3.6 中,输出是:

On setting: True
On checking: False
Run Code Online (Sandbox Code Playgroud)

而在 3.8 中则是:

On setting: True
On checking: True
Run Code Online (Sandbox Code Playgroud)

如果这还不够好奇,如果我删除 的继承Generic[T],一切都很好。

原来的

我正在使用 Python 3.6,KeyError当尝试从字典中获取密钥时,我得到了:

On setting: True
On checking: False
Run Code Online (Sandbox Code Playgroud)

这意味着从字典中获取的键会导致此异常。请注意,d只有一个条目。键类型是一个类型对象GenericMeta,其元类是这样的,所以这可能是问题所在吗?

我使用调试器验证了以下属性:

  1. id(tp)多次调用都是一样的。
  2. hash(tp)多次调用都是一样的。
  3. tp is list(d.keys())[0]
  4. tp == list(d.keys())[0]
  5. len(d) == 1

编辑:

  1. print(type(tp)) # <class 'typing.GenericMeta'>
  2. print(type(tp)) # <class 'dict'>

我的问题是:这种行为的原因可能是什么?

由于某些软件包的兼容性问题,我无法更新 python 版本,所以请不要告诉我更新,除非这是一个已知的错误,并在以后的版本中解决了。

use*_*ica 5

这是一个初始化顺序问题。

在 Python 3.6 上,Classtyping.GenericMeta. typing.GenericMeta在 中执行重要的初始化__new__,但初始化只能在type.__new__返回要初始化的内容后开始。type.__new__负责调用__init_subclass__,因此您可以在发生__init_subclass__任何GenericMeta初始化之前运行。

当您__init_subclass__添加Class到字典时,尚未执行正确工作==所需的初始化。hash此操作最终使用无效的哈希值。稍后,初始化完成后,查找将使用正确的哈希值,但找不到Class.

在后来的 Python 版本中,整个泛型类的实现被彻底改变。typing.GenericMeta不复存在。