访问defaultdict工厂中的键值

21

我希望你能做类似于这个的事情:

from   collections import defaultdict
import hashlib

def factory():
    key = 'aaa'
    return { 'key-md5' : hashlib.md5('%s' % (key)).hexdigest() }

a = defaultdict(factory)
print a['aaa']

实际上,我需要在工厂中访问密钥的原因并不是为了计算md5,而是出于其他原因;这只是一个例子。

正如您所看到的,在工厂中我无法访问密钥:我只是强制使用它,这根本没有任何意义。

是否可能以一种方式使用defaultdict,以便可以在工厂中访问密钥?


出于好奇,你为什么需要使用defaultdict,它只是用来纠正缺失值吗?因为除了返回{}之外,它没有其他作用吗? - Torxed
是的,它是为字典中缺失的键提供值。这就是defaultdict的全部意义,不是吗?问题在于,我(真实)存储在defaultdict中的数据结构,其字段取决于键。因此,每当我尝试访问不存在的元素时,我需要使用defaultdict的键作为参数,在工厂中创建它。 - blueFast
担心你会做一些超出寻常的事情。请参考@falsetru的解决方案,因为那正是我在使用自定义构建字典时要向你建议的。 - Torxed
3个回答

31

defaultdict__missing__方法不会把key传递给工厂函数。

如果default_factory不是None,它将不带参数被调用,为给定的键提供默认值,该值将插入到字典中并返回。

使用自定义__missing__方法创建自己的字典类。

>>> class MyDict(dict):
...     def __init__(self, factory):
...         self.factory = factory
...     def __missing__(self, key):
...         self[key] = self.factory(key)
...         return self[key]
... 
>>> d = MyDict(lambda x: -x)
>>> d[1]
-1
>>> d
{1: -1}

我仍然不明白为什么defaultdict不支持这种类型的工厂。有人能解释一下吗? - midas
@midas 我不确定,但我猜测没有什么好的方法可以在保持可以插入任何不需要参数构造函数 (defaultdict(int), defaultdict(MyClass)) 的类型的能力时完成它。 - ralokt
1
@ralokt,有人能解释一下为什么要使用工厂而不是对象,比如defaultdict(0)吗? - Alexey
2
@Alexey 因为有时静态值并不足够,这是我的猜测。这还不如不传递缺失的键更糟糕,因为没有可以传递的东西。此外,带有可变类型的静态值将是完全不可行的,每个缺少的键都将指向相同的对象。 - ralokt
我同意没有访问“key”的方式是愚蠢的。对于您不需要“key”的情况,您可以简单地忽略它,例如defaultdict(lambda key: 0) - sam-6174
1
在我看来,定义自己的字典子类的另一个优点是,当你打印它时,它仍然会像常规字典一样显示(不像defaultdict)。 - martineau

6
很不幸,defaultdict规定default_factory必须不带参数调用,因此不能直接这样做。但是可以使用defaultdict作为基类,并具有所需的行为。请参考链接:http://docs.python.org/2/library/collections.html#collections.defaultdict
class CustomDefaultdict(defaultdict):
    def __missing__(self, key):
        if self.default_factory:
            dict.__setitem__(self, key, self.default_factory(key))
            return self[key]
        else:
            defaultdict.__missing__(self, key)

这对我有效:
>>> a = CustomDefaultdict(factory)
>>> a
defaultdict(<function factory at 0x7f0a70da11b8>, {})
>>> print a['aaa']
{'key-md5': '47bce5c74f589f4867dbd57e9ca9f808'}
>>> print a['bbb']
{'key-md5': '08f8e0260c64418510cefb2b06eee5cd'}

3
不需要使用defaultdict作为基类:自Python 2.2起,可以直接继承dict。 - Anton Bryzgalov

1
在我需要一个默认字典并且工厂中的键时,我发现lru_cache也解决了我的问题。
import functools

@functools.lru_cache(maxsize=None)
def use_func_as_dict(key='') # Or whatever type
    with open(key, 'r') as ifile:
        return ifile.readlines()

f1 = use_func_as_dict('test.txt')
f2 = use_func_as_dict('test2.txt')
# This will reuse the old value instead of re-reading the file
f3 = use_func_as_dict('test.txt')
assert f3 is f1

从理论上讲,这其实更加合理,因为你需要的是输入的函数而不是一个一致的虚拟备选项。


有趣,但“实际上更有意义”在我的情况下绝对不适用。 - Robert Siemer

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