SoFunction
Updated on 2025-04-18

Solution to the non-existence of key in python dictionary d[k]

Sometimes for convenience, even if a key does not exist in the map, we hope to get a value when reading it through this key.default value. There are two ways to help us achieve this goal, one is throughdefaultdictThis type is not ordinarydict, the other is to define one for yourselfdictSubclass of  , then implement it in the subclass
__missing__method. These two methods will be introduced below.

defaultdict: A choice for handling the keys that cannot be found

The following example isSolved elegantly with the help ofd[k]The problem in. Created in userdefaultdictWhen an object is used, it needs to configure a method to create default values ​​for keys that cannot be found.

# -*- coding: utf-8 -*-

from collections import defaultdict

index = defaultdict(list)  # Create a defaultdict with the list constructor as default_factoryprint(index)
# defaultdict(<class 'list'>, {})

word = 'name'
# If index does not have a word record, default_factory will be called to create a value for keys that cannot be found.# This value is an empty list here, and then this empty list is assigned to index[word], followed by# is returned as a return value, so the .append(location) operation always succeeds.index[word].append('mmmm')  
print(index)
# defaultdict(<class 'list'>, {'name': ['mmmm']})

Specifically, in instantiating adefaultdictWhen it comes to the construction method, it is necessary to provide aCalling the object, this callable object will be in__getitem__When you encounter a key that cannot be found, it will be called, let__getitem__Return to some kinddefault value

For example, we created a new dictionary:dd = defaultdict(list), if keynew-keyIf it does not exist in dd, the expressiondd['new-key']Will follow the steps below.

  • Calllist()To create a new list
  • Take this new list as a value, ‘new-key’ As its key, put it inddmiddle
  • Return to this listQuote

This callable object used to generate default values ​​is stored in the namedefault_factoryIn the instance attribute.

Notice
If it is being createddefaultdictNo specified at the timedefault_factory, querying non-existent keys will triggerKeyError

defaultdictThe insidedefault_factoryOnly in__getitem__If it is called, it will not work in other methods at all. for example,ddIt's adefaultdictkIt's a key that cannot be found.dd[k]This expression will be calleddefault_factoryCreate a default value, and(k)Then it will returnNone

The heroes behind all this are actually special methods__missing__. It will be indefaultdictCalled when you encounter a key that cannot be founddefault_factory, but in fact this feature isallMapping types are OKchooseGo to support.

Special method__missing__

All mapping types are being processedKey not foundWhen it comes to__missing__method. This is also called "missing"The reason. Although the base classdictThis method is not defined, butdictI know that such a thing exists. That is to say, if there is a class that inheritsdict, and then this inheritance class provides__missing__Method, then in__getitem__When you encounter a key that cannot be found,PythonWillCall it automaticallyinstead of throwing oneKeyErrorException.

Notice:__missing__Methods will only be__getitem__Call (such as in expressiond[k]middle). supply__missing__Methodsgetor__contains__inOperators will use this method) The use of these methods has no effect. This is what I mentioned above,defaultdictIndefault_factoryOnly right__getitem__The reason for its function.

If you want to customize a mapping type, the more appropriate strategy is actually inheritance.kind. Here wedictInheritance, just for demonstration__missing__How is itdict.__getitem__Called. (Direct InheritancedictThere are some problems, let's talk about it later)

# -*- coding: utf-8 -*-


class StrKeyDict0(dict):  # StrKeyDict0 inherits dict.    def __missing__(self, key):
        if isinstance(key, str):  # If the key that cannot be found is a string itself, throw a KeyError exception.            raise KeyError(key)
        return self[str(key)]  # If the key that cannot be found is not a string, then convert it into a string and then search it
    def get(self, key, default=None):
        try:
            # get method delegates the search work to __getitem__ in the form of self[key], so that the search fails            Before #, you can also give a chance to a certain key through __missing__.            return self[key]
        except KeyError:
            return default  # If a KeyError is thrown, it means that __missing__ has also failed, so the default is returned.
    def __contains__(self, key):
        # First look up according to the original value of the passed key (our mapping type may contain non-string keys), if not        # Find it, then convert the key into a string using str() method and search it again.        return key in () or str(key) in ()


if __name__ == '__main__':
    d = StrKeyDict0([('2', 'two'), ('4', 'four')])
    print(d['2'])  # two
    print(d[1])
    # Traceback (most recent call last):
    #   File "C:/myFiles/company_project/xbot/tests/miss_test.py", line 27, in <module>
    #     print(d[1])
    #   File "C:/myFiles/company_project/xbot/tests/miss_test.py", line 8, in __missing__
    #     return self[str(key)]  # If the key that cannot be found is not a string, then convert it into a string and then search it    #   File "C:/myFiles/company_project/xbot/tests/miss_test.py", line 7, in __missing__
    #     raise KeyError(key)
    # KeyError: '1'
    print(2 in d)
    # True
    print(1 in d)
    # False

Notice

  • Let’s see why isinstance(key, str) test is required in the above __missing__.
  • If there is no such test, as long as str(k) returns an existing key, then the __missing__ method is fine, whether it is a string key or a non-string key, it can run normally. But if str(k) is not an existing key, the code will fall into infinite recursion. This is because self[str(key)] in the last line of __missing__ will call __getitem__, and this str(key) does not exist, so __missing__ will be called again.
  • In order to maintain consistency, the __contains__ method is also necessary here. This is because the operation k in d will call it, but the __contains__ method we inherit from dict will not call the __missing__ method when the key is not found. There is another detail in __contains__, which is that we do not use a more Python-style method - k in my_dict - to check whether the key exists, because that will also cause __contains__ to be called recursively. In order to avoid this situation, a more explicit method is adopted here, and query directly in this () .
  • Operations like k in my_dict.keys() are very fast in Python 3, and it doesn't matter even if the mapping type object is huge. This is because the return value of () is a "view". A view is like a collection, and similar to a dictionary, it is very fast to find an element in a view. Documentation about this detail can be found in "Dictionary view objects" (/3/library/#dictionary-view-objects). Python 2’s () returns a list, so although the above method is still correct, it will not be too efficient when dealing with large objects, because the k in my_list operation requires scanning the entire list.

This is the end of this article about the solution that the key does not exist in the python dictionary d[k]. For more related content in the python dictionary d[k], please search for my previous article or continue browsing the related articles below. I hope everyone will support me in the future!