python字典setdefault用法困惑

11 投票
4 回答
14029 浏览
提问于 2025-04-17 19:55

我在找一个算法,但我搞不懂为什么字典 d 里有值,而 curr 里没有。我觉得好像对字典 d 没有什么操作。

>>> def what(*words):
...     d = {}
...     print d
...     for word in words:
...     print 'word: ' + word
...         curr = d
...         for letter in word:
...             curr = curr.setdefault(letter, {})
...         curr = curr.setdefault('.', '.')
...     print d
...     print '?'
...     print curr
...     return 1
... 
>>> what('foo') 
{}
word: foo
{'f': {'o': {'o': {'.': '.'}}}}
?
.
1

4 个回答

0

setdefault(key[, default])

来自 官方文档

如果 key 在字典里,返回它的值。如果不在,就用 default 的值插入 key,并返回 default。如果你不提供 default,它默认是 None

使用示例

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d.setdefault('a') # returns the corresponding value for key 'a'
1
>>> d.setdefault('a', 10) # returns the corresponding value for key 'a'
1
>>> d.setdefault('b') # returns the corresponding value for key 'b'
2
>>> d.setdefault('c', 100) # returns the corresponding value for key 'c'
3
>>> type(d.setdefault('z')) # because 'z' is not a key of d, None is returned which is the default value of default 
<class 'NoneType'>
>>> d.setdefault('z', 666) # returns 666 since key 'z' is not in d
666

在你的代码中

我觉得你可能有点困惑,因为 curr = curr.setdefault(letter, {}) 这行代码总是会创建一个新的空字典,然后把它赋值给 curr。这意味着,你并不是在覆盖原来的值,而是为字典中的每个元素增加了一个嵌套层级。

我还认为,你想用代码创建一个字典,把 words 中的每个元素作为键,值都是 {},你可以用下面这段代码来实现,这段代码使用了 字典推导式

def what(*words):
    return {word: {} for word in set(words)}

注意:我添加了 setdefault 的解释,因为你的问题特别针对这个情况,但我也想回答你具体的问题。

5

d = dict() --> 这行代码是用来创建一个空字典,并把它命名为 d; 这样你就有了一个字典对象({}),它的名字是 d

在外层循环中
curr = d --> 这行代码是把另一个名字 curr 绑定到同一个字典对象上。所以,dcurr 这两个名字指向的是同一个字典

在内层循环中
在第一次循环时,letter = 'f'

curr = curr.setdefault(letter, {})

在上面的语句中,有两个事情发生:

A) curr.setdefault(letter, {}) --> 根据文档的说明:

"如果键在字典中,返回它的值。如果没有,就插入这个键,并给它一个默认值,然后返回这个默认值。默认值是 None。"

因为字母 'f' 不在最初的字典中,所以它把原来的字典变成了 {'f':{}},并返回值 {}。这个值不是最初的字典,而是因为 setdefault 语句创建的一个新字典。此时,currd 都指向已经变成 {'f':{}} 的原始字典。

B) 这时,curr 被重新赋值为上面提到的返回值。现在,currd 指向的是不同的对象。d 仍然指向 {'f':{}},而 curr 指向的是一个空字典,这个空字典实际上是 d['f'] 的值。 这就是为什么在原始字典中会出现嵌套的原因,因为我们在循环中不断处理这个字典。

14

查看一下关于 dict.setdefault 的文档:它的功能类似于 get,但如果这个键不存在,它还会创建一个新的键值对。

>>> my_dict = {}
>>> my_dict.setdefault('some key', 'a value')
'a value'
>>> my_dict
{'some key': 'a value'}
>>> my_dict.get('some key2', 'a value2')
'a value2'
>>> my_dict
{'some key': 'a value'}

稍微修改一下你的例子:

>>> def what(*words):
...     d = dict()
...     for word in words:
...             curr = d
...             for letter in word:
...                     curr = curr.setdefault(letter, {})
...             curr = curr.setdefault('.', '.')
...             print 'curr is now: %r while d is %r' % (curr, d)
... 
>>> what('foo')
curr is now: '.' while d is {'f': {'o': {'o': {'.': '.'}}}}

你可以看到 curr 的值发生了变化,因为在调用 setdefault 时,它有时(在你的例子中总是)会创建一个新的 dict,并把它作为值赋给 curr,而 d 始终指向原来的 dict。正如你所看到的,它在循环后确实被修改了,因为它的值变成了 {'f': {'o': {'o': {'.': '.'}}}},这和 {} 相差很大。

你可能感到困惑的原因是 curr = curr.setdefault(letter, {}) 总是 创建一个 新的空的 dict,然后将其赋值给 curr(因此对于每个字母,你都在原来的 dict 上增加了一个嵌套层,而不是覆盖原来的值)。

看看这个:

>>> my_dict = {}
>>> curr = my_dict
>>> for letter in 'foo':
...     print 'my_dict is now %r. curr is now %r' % (my_dict, curr)
...     curr = curr.setdefault(letter, {})
... 
my_dict is now {}. curr is now {}
my_dict is now {'f': {}}. curr is now {}
my_dict is now {'f': {'o': {}}}. curr is now {}
>>> my_dict
{'f': {'o': {'o': {}}}}

你可以看到,对于每一层,my_dict 都有一个新的嵌套层。

也许,我只是猜测,你想得到的结果是像 'foo' -> {'f': {}, 'o': {}} 这样的结构,在这种情况下你应该这样做:

>>> my_dict = {}
>>> for letter in 'foo':
...     my_dict.setdefault(letter, {})
... 
>>> my_dict
{'o': {}, 'f': {}}

撰写回答