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 throughdefaultdict
This type is not ordinarydict
, the other is to define one for yourselfdict
Subclass 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 of
d[k]
The problem in. Created in userdefaultdict
When 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 adefaultdict
When 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-key
If it does not exist in dd, the expressiondd['new-key']
Will follow the steps below.
- Call
list()
To create a new list - Take this new list as a value, ‘
new-key
’ As its key, put it indd
middle - Return to this list
Quote
This callable object used to generate default values is stored in the namedefault_factory
In the instance attribute.
Notice:
If it is being createddefaultdict
No specified at the timedefault_factory
, querying non-existent keys will triggerKeyError
。
defaultdict
The insidedefault_factory
Only in__getitem__
If it is called, it will not work in other methods at all. for example,dd
It's adefaultdict
,k
It's a key that cannot be found.dd[k]
This expression will be calleddefault_factory
Create a default value, and(k)
Then it will returnNone
。
The heroes behind all this are actually special methods__missing__
. It will be indefaultdict
Called when you encounter a key that cannot be founddefault_factory
, but in fact this feature isall
Mapping types are OKchoose
Go to support.
Special method__missing__
All mapping types are being processedKey not found
When it comes to__missing__
method. This is also called "missing
"The reason. Although the base classdict
This method is not defined, butdict
I 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,Python
WillCall it automatically
instead of throwing oneKeyError
Exception.
Notice:__missing__
Methods will only be__getitem__
Call (such as in expressiond[k]
middle). supply__missing__
Methodsget
or__contains__
(in
Operators will use this method) The use of these methods has no effect. This is what I mentioned above,defaultdict
Indefault_factory
Only right__getitem__
The reason for its function.
If you want to customize a mapping type, the more appropriate strategy is actually inheritance.kind. Here we
dict
Inheritance, just for demonstration__missing__
How is itdict.__getitem__
Called. (Direct Inheritancedict
There 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!