Python的collections.defaultdict正确返回默认值,除非首先引用该键

时间:2015-03-17 20:26:14

标签: python python-3.x dictionary collections

基本上,如果我设置defaultdict并按键引用它,它将正常工作并返回我设置为默认值。但是,如果我在其上使用.get(),则不会返回默认值。这是我能给出的最简单的例子。

basedict = defaultdict(dict)
assert(basedict['junk'] == {}) # Pass
assert(basedict.get('junk') == {}) # Pass
assert(basedict.get('popcorn') == {}) # Fail

为了完成,我正在使用dicts和我的代码,我最初遇到的问题看起来有点像这样

from collections import defaultdict

basedict = defaultdict(dict)
assert(basedict['junk'] == {})

basedict[69] = defaultdict(lambda: 1000)
assert(basedict[69]['junk'] == 1000)
assert(basedict[69].get('junk') == 1000)
assert(basedict[69].get('junk', 420) == 1000)

# The above works fine, but if I call .get on a default dict using a key I've
# never referenced before it returns None or the .get supplied default value
assert(basedict[69].get('garbage') == 1000) # Returns None
assert(basedict[69].get('garbage', 420) == 1000) # Returns 420
assert(basedict[69].get('dumpster', 420) == 1000) # Returns 420

# But if I place a an assert before the calling .get(garbage) that 
# checks basedict[69]['garbage'] then the asserts after work until I get 
# to 'dumpster' at which point it fails again
# It also fails if I set the defaultdict to something other than a lambda
basedict[7] = defaultdict(dict)
assert(basedict[7]['taco'] == {}) # Pass
assert(basedict[7].get('taco') == {}) # Pass
assert(basedict[7].get('burrito') == {}) # Fail

2 个答案:

答案 0 :(得分:4)

defaultdict.get() 填充密钥,没有。这是按设计,否则会破坏该方法的目的。这同样适用于会员资格测试:

>>> from collections import defaultdict
>>> d = defaultdict(dict)
>>> 'foo' in d
False
>>> d['foo']
{}
>>> 'foo' in d
True

如果您需要为缺失密钥调用默认工厂,请使用defaultdict.__getitem__(例如defaultdict_instance[key])。

如果您需要设置默认值其他而不是工厂提供的,请使用dict.setdefault()

>>> d = defaultdict(dict)
>>> d.setdefault('bar', 42)
42
>>> d.setdefault('bar', 30)
42
>>> d
defaultdict(<type 'dict'>, {'bar': 42})

如果只需要获取默认值,请使用dict.get()

>>> d = defaultdict(dict)
>>> d.get('bar', {}).get('baz', 1000)
1000

请注意,我锁定了.get()次来电;如果缺少第一个键,则第一个.get()返回一个空字典。

答案 1 :(得分:1)

我最近遇到了这个问题,基本上必须做这样的事情:

from collections import defaultdict

class BetterDict(defaultdict):
    def get(self, item, default=None):
        # default is not really used, but leaving here for compatibility
        # This will either return the actual value, or the defaultdict's
        # default, as generated by default_factory
        return self.__getitem__(item)

现在:

basedict = BetterDict(dict)
assert(basedict['junk'] == {}) # Pass
assert(basedict.get('junk') == {}) # Pass
assert(basedict.get('popcorn') == {}) # Pass