Python 的 `dict` 无法识别自己的密钥
Python's `dict` doesn't recognize its own key
编辑 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)
在python3.6中输出为:
On setting: True
On checking: False
而在 3.8 中是:
On setting: True
On checking: True
如果这还不够奇怪,如果我从 Generic[T]
中删除继承,一切都很好。
原版
我正在使用 Python 3.6,当我尝试从字典中获取键时得到 KeyError
:
# d: Dict[type, Any]
tp = list(d.keys())[0]
d[tp]
# KeyError: ...
意思是从字典中取出的键导致了这个异常。请注意 d
只有一个条目。键类型是一个以 GenericMeta
作为其元类的类型对象,所以这可能是问题所在吗?
我使用调试器验证了以下属性:
id(tp)
多次调用相同
hash(tp)
多次调用相同
tp is list(d.keys())[0]
tp == list(d.keys())[0]
len(d) == 1
编辑:
print(type(tp)) # <class 'typing.GenericMeta'>
print(type(tp)) # <class 'dict'>
我的问题是:造成这种行为的原因可能是什么?
由于某些软件包的兼容性问题,我无法更新 python 版本,所以请不要告诉我更新,除非它是一个已在更高版本中解决的已知错误。
这是一个初始化顺序问题。
在 Python 3.6 上,Class
是 typing.GenericMeta
的一个实例。 typing.GenericMeta
在 __new__
中执行重要的初始化,但该初始化只能开始一次 type.__new__
returns 要初始化的东西。 type.__new__
负责调用 __init_subclass__
,因此您的 __init_subclass__
在任何 GenericMeta
初始化发生之前运行。
当您的 __init_subclass__
将 Class
添加到字典时,尚未执行 ==
和 hash
正常工作所需的初始化。此操作最终使用无效的散列。后来,一旦初始化完成,查找使用正确的散列,找不到 Class
.
在后来的 Python 版本中,整个通用 class 实现被完全改变了。 typing.GenericMeta
不再存在。
编辑 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)
在python3.6中输出为:
On setting: True
On checking: False
而在 3.8 中是:
On setting: True
On checking: True
如果这还不够奇怪,如果我从 Generic[T]
中删除继承,一切都很好。
原版
我正在使用 Python 3.6,当我尝试从字典中获取键时得到 KeyError
:
# d: Dict[type, Any]
tp = list(d.keys())[0]
d[tp]
# KeyError: ...
意思是从字典中取出的键导致了这个异常。请注意 d
只有一个条目。键类型是一个以 GenericMeta
作为其元类的类型对象,所以这可能是问题所在吗?
我使用调试器验证了以下属性:
id(tp)
多次调用相同hash(tp)
多次调用相同tp is list(d.keys())[0]
tp == list(d.keys())[0]
len(d) == 1
编辑:
print(type(tp)) # <class 'typing.GenericMeta'>
print(type(tp)) # <class 'dict'>
我的问题是:造成这种行为的原因可能是什么?
由于某些软件包的兼容性问题,我无法更新 python 版本,所以请不要告诉我更新,除非它是一个已在更高版本中解决的已知错误。
这是一个初始化顺序问题。
在 Python 3.6 上,Class
是 typing.GenericMeta
的一个实例。 typing.GenericMeta
在 __new__
中执行重要的初始化,但该初始化只能开始一次 type.__new__
returns 要初始化的东西。 type.__new__
负责调用 __init_subclass__
,因此您的 __init_subclass__
在任何 GenericMeta
初始化发生之前运行。
当您的 __init_subclass__
将 Class
添加到字典时,尚未执行 ==
和 hash
正常工作所需的初始化。此操作最终使用无效的散列。后来,一旦初始化完成,查找使用正确的散列,找不到 Class
.
在后来的 Python 版本中,整个通用 class 实现被完全改变了。 typing.GenericMeta
不再存在。