Home > OS >  Python's `dict` doesn't recognize its own key
Python's `dict` doesn't recognize its own key

Time:01-04

EDIT 2: FINALLY I was able produce an 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)

In python 3.6 the output is:

On setting: True
On checking: False

While in 3.8 it is:

On setting: True
On checking: True

If that's not curious enough, if I remove the inheritance from Generic[T], everything is AOK.

ORIGINAL

I'm using Python 3.6 and I get a KeyError when trying to get a key from a dictionary:

# d: Dict[type, Any]

tp = list(d.keys())[0]
d[tp]
# KeyError: ...

Meaning that the very key taken from the dictionary causes this exception. Note that d has only one entry. The key-type is a type-object with GenericMeta as its metaclass, so might this be the issue?

I verified the following properties using the debugger:

  1. id(tp) is the same in multiple calls.
  2. hash(tp) is the same in multiple calls.
  3. tp is list(d.keys())[0]
  4. tp == list(d.keys())[0]
  5. len(d) == 1

EDIT:

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

My question is this: what might be the cause for this behavior?

I can't update the python version due to some of the packages' compatibility issues, so please don't tell me to update unless it's a known bug that's solved in a later version.

CodePudding user response:

This is an initialization order issue.

On Python 3.6, Class is an instance of typing.GenericMeta. typing.GenericMeta performs important initialization in __new__, but that initialization can only begin once type.__new__ returns something to initialize. type.__new__ is responsible for calling __init_subclass__, so your __init_subclass__ runs before any GenericMeta initialization can happen.

When your __init_subclass__ adds Class to the dict, initialization necessary for == and hash to work correctly has not yet been performed. This operation ends up using an invalid hash. Later, once initialization is complete, the lookup uses the correct hash and can't find Class.

On later Python versions, the whole generic class implementation was changed completely. typing.GenericMeta no longer exists.

  •  Tags:  
  • Related