python字典setdefault用法困惑
我在找一个算法,但我搞不懂为什么字典 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 个回答
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
的解释,因为你的问题特别针对这个情况,但我也想回答你具体的问题。
d = dict()
--> 这行代码是用来创建一个空字典,并把它命名为 d
; 这样你就有了一个字典对象({}
),它的名字是 d
在外层循环中
curr = d
--> 这行代码是把另一个名字 curr
绑定到同一个字典对象上。所以,d
和 curr
这两个名字指向的是同一个字典
在内层循环中
在第一次循环时,letter = 'f'
curr = curr.setdefault(letter, {})
在上面的语句中,有两个事情发生:
A) curr.setdefault(letter, {})
--> 根据文档的说明:
"如果键在字典中,返回它的值。如果没有,就插入这个键,并给它一个默认值,然后返回这个默认值。默认值是 None。"
因为字母 'f' 不在最初的字典中,所以它把原来的字典变成了 {'f':{}}
,并返回值 {}
。这个值不是最初的字典,而是因为 setdefault 语句创建的一个新字典。此时,curr
和 d
都指向已经变成 {'f':{}}
的原始字典。
B) 这时,curr
被重新赋值为上面提到的返回值。现在,curr
和 d
指向的是不同的对象。d
仍然指向 {'f':{}}
,而 curr
指向的是一个空字典,这个空字典实际上是 d['f']
的值。
这就是为什么在原始字典中会出现嵌套的原因,因为我们在循环中不断处理这个字典。
查看一下关于 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': {}}