Python字典的setdefault方法,让你感到困惑了吗?

11

我在寻找算法,但我无法弄清楚为什么字典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个回答

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。正如您所看到的,循环后d被修改,因为它的值是{'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': {}}

我理解了setdefault的部分,我的代码和你的差别很大。 - user584583
@user584583 我坚信仔细阅读文档应该能解决你的问题,不过我已经针对你的示例添加了一些说明。 - Bakuriu

5

d = dict() --> 初始化一个空字典并将其绑定到名称 d 上;因此您拥有通过名称 d 引用的字典对象 ({})

在外层 for 循环中
curr = d --> 将另一个名称 curr 绑定到同一个对象。所以名称 (dcurr) 引用同一个对象。

在内层 for 循环中
在第一次迭代中,letter = 'f'

curr = curr.setdefault(letter, {})

上述语句中有两件事情发生了:
A) curr.setdefault(letter, {}) --> 根据文档:如果字典中存在key,则返回其value。否则,插入一个默认值为None的key,并返回该默认值。由于字母'f'不在初始字典对象中,因此它将初始对象变异为{'f':{}}并返回值{},这不是初始字典对象,而是由于setdefault语句创建的新对象。此时,currd都指向已经变异成{'f':{}}的初始字典对象。
B) 重新分配名称curr给上述返回值。现在,名称currd引用不同的对象。d引用对象{'f':{}},而curr引用空字典对象,实际上是d['f']的值。 这就是为什么在循环中嵌套原始字典对象的原因。

0

setdefault(key[, default)

来自文档:

如果字典中存在key,则返回其值。否则,插入key并将其值设置为default,然后返回defaultdefault默认为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中的每个元素添加了一个嵌套级别。

我还认为,你想要通过你的代码实现的目标是创建一个字典,其中words中的每个元素都作为键,{}作为值,所以你可以使用以下代码来实现,它使用了一个字典推导式

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

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


0
tl;dr setdefault()有一个返回值。(它返回刚刚插入的键的值。)例如:
>>> res = curr.setdefault(a_key, a_value)
>>> print(res)
a_value

我一开始也很困惑,以为setdefault没有返回值,因此
>>> res = curr.setdefault(a_key, a_value)
>>> print(res)
{a_key: a_value}  # incorrect

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接