Python递归字典创建疑问:setdefault方法作用解析
dict.setdefault() Behavior in Trie Construction The Code in Question
class StreamChecker(object): def __init__(self, words): """ :type words: List[str] """ print(words) self.waitlist = [] self.trie = dict() for word in words: temp_dict = self.trie for letter in word: temp_dict = temp_dict.setdefault(letter, dict()) temp_dict['#'] = '#' if __name__ == '__main__': a = StreamChecker(['abc', 'wef', 'ykj']) print(a.trie)
User's Confusion
After initialization,
print(a.trie)outputs:{'a': {'b': {'c': {'#': '#'}}}, 'w': {'e': {'f': {'#': '#'}}}, 'y': {'k': {'j': {'#': '#'}}}}I'm confused by the line
temp_dict = temp_dict.setdefault(letter, dict()): I thoughtsetdefaultalways returns an empty dictionary{}, so why doesself.triekeep getting updated? I expectedself.trieto be{'a': {}, 'w': {}, 'y': {}}instead. Please explain.
Explanation
Hey there! The core of your confusion is a misunderstanding of what dict.setdefault() actually returns—it doesn't always give you an empty dictionary. Let's break this down step by step to see how the trie gets built correctly.
1. What setdefault() Really Does
dict.setdefault(key, default) has two key behaviors:
- If the
keyalready exists in the dictionary, it returns the existing value tied to that key. - If the
keydoesn't exist, it addskey: defaultto the dictionary, then returns thedefaultvalue.
So the return value depends entirely on whether the key was present before the call—not just a blank dict every time.
2. Walkthrough of Trie Construction (Take 'abc' as an Example)
Let's trace how the first word 'abc' builds out self.trie:
- Start with
temp_dict = self.trie(which is an empty dict at first). - First letter 'a':
temp_dict.setdefault('a', dict())sees that 'a' isn't in the empty dict, so it adds'a': {}toself.trieand returns this new empty dict. Nowtemp_dictpoints toself.trie['a'], andself.triebecomes{'a': {}}.
- Second letter 'b':
- Now
temp_dictisself.trie['a'](still empty).setdefault('b', dict())adds'b': {}to this sub-dict and returns it.temp_dictnow points toself.trie['a']['b'], andself.trieupdates to{'a': {'b': {}}}.
- Now
- Third letter 'c':
temp_dictisself.trie['a']['b'].setdefault('c', dict())adds'c': {}to this sub-dict and returns it.temp_dictpoints toself.trie['a']['b']['c'], makingself.trie{'a': {'b': {'c': {}}}}.
- Final step:
temp_dict['#'] = '#'adds the end-of-word marker toself.trie['a']['b']['c'], turning it into{'#': '#'}.
3. Why self.trie Gets Modified
In Python, dictionaries are mutable objects—variables like temp_dict store references to the actual dict object, not a copy. When you operate on temp_dict, you're directly modifying the original dict (whether that's self.trie or one of its nested sub-dicts).
For example, when temp_dict = self.trie, both variables point to the same empty dict. Calling temp_dict.setdefault('a', dict()) modifies that shared dict, so self.trie immediately reflects the change. Later, when temp_dict points to a nested sub-dict, modifying it still alters the larger self.trie structure because it's all part of the same nested object.
4. Why Your Expected Result Was Wrong
You thought self.trie would only have empty sub-dicts because you assumed setdefault always returns a new blank dict. But in reality, after the first letter of each word, temp_dict is pointing to a nested sub-dict inside self.trie. Each subsequent setdefault fills in that sub-dict, building out the nested trie structure instead of leaving it empty.
内容的提问来源于stack exchange,提问作者Wenbin Xu

