18

因此,我正在编写一个扩展字典的类,该字典现在使用“dictify”方法将自身转换为字典。我想做的是改变它,以便在对象上调用 dict() 导致相同的行为,但我不知道要覆盖哪个方法。这是不可能的,还是我错过了一些非常明显的东西?(是的,我知道下面的代码不起作用,但我希望它说明了我正在尝试做的事情。)

from collections import defaultdict

class RecursiveDict(defaultdict):
    '''
    A recursive default dict.

    >>> a = RecursiveDict()
    >>> a[1][2][3] = 4
    >>> a.dictify()
    {1: {2: {3: 4}}}
    '''
    def __init__(self):
        super(RecursiveDict, self).__init__(RecursiveDict)

    def dictify(self):
        '''Get a standard dictionary of the items in the tree.'''
        return dict([(k, (v.dictify() if isinstance(v, dict) else v))
                     for (k, v) in self.items()])

    def __dict__(self):
        '''Get a standard dictionary of the items in the tree.'''
        print [(k, v) for (k, v) in self.items()]
        return dict([(k, (dict(v) if isinstance(v, dict) else v))
                     for (k, v) in self.items()])

编辑:为了更清楚地显示问题:

>>> b = RecursiveDict()
>>> b[1][2][3] = 4
>>> b
defaultdict(<class '__main__.RecursiveDict'>, {1: defaultdict(<class '__main__.RecursiveDict'>, {2: defaultdict(<class '__main__.RecursiveDict'>, {3: 4})})})
>>> dict(b)
{1: defaultdict(<class '__main__.RecursiveDict'>, {2: defaultdict(<class '__main__.RecursiveDict'>, {3: 4})})}
>>> b.dictify()
{1: {2: {3: 4}}}

我希望 dict(b) 与 b.dictify() 相同

4

6 回答 6

29

您的方法没有问题,但这类似于 Perl 的 Autovivification 功能,该功能已在此问题中的 Python 中实现。为此向@nosklo 提供道具。

class RecursiveDict(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

>>> a = RecursiveDict()
>>> a[1][2][3] = 4
>>> dict(a)
{1: {2: {3: 4}}}

编辑

正如@Rosh Oxymoron 所建议的那样,使用__missing__结果可以实现更简洁的实现。需要 Python >= 2.5

class RecursiveDict(dict):
    """Implementation of perl's autovivification feature."""
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value
于 2011-07-21T19:12:39.230 回答
2

编辑:正如 ironchefpython 在评论中指出的那样,这实际上并没有像我想象的那样做,因为在我的示例b[1]中仍然是RecursiveDict. 这可能仍然有用,因为您基本上得到了一个与 Rob Cowie 的答案非常相似的对象,但它是建立在defaultdict.


您可以通过覆盖获得您想要的行为(或非常相似的行为)__repr__,请查看:

class RecursiveDict(defaultdict):
    def __init__(self):
        super(RecursiveDict, self).__init__(RecursiveDict)

    def __repr__(self):
        return repr(dict(self))

>>> a = RecursiveDict()
>>> a[1][2][3] = 4
>>> a             # a looks like a normal dict since repr is overridden
{1: {2: {3: 4}}}
>>> type(a)
<class '__main__.RecursiveDict'>
>>> b = dict(a)
>>> b             # dict(a) gives us a normal dictionary
{1: {2: {3: 4}}}
>>> b[5][6] = 7   # obviously this won't work anymore
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 5
>>> type(b)
<type 'dict'>

可能有更好的方法来获得正常的字典视图,defaultdictdict(self)我找不到,如果你知道如何评论。

于 2011-07-21T20:15:04.057 回答
2

你想像dict 一样打印它吗?用这个:

from collections import defaultdict

class RecursiveDict(defaultdict):
    '''
    A recursive default dict.

    >>> a = RecursiveDict()
    >>> a[1][2][3] = 4
    >>> a.dictify()
    {1: {2: {3: 4}}}
    >>> dict(a)
    {1: {2: {3: 4}}}

    '''
    def __init__(self):
        super(RecursiveDict, self).__init__(RecursiveDict)

    def dictify(self):
        '''Get a standard dictionary of the items in the tree.'''
        return dict([(k, (v.dictify() if isinstance(v, dict) else v))
                     for (k, v) in self.items()])

    def __dict__(self):
        '''Get a standard dictionary of the items in the tree.'''
        print [(k, v) for (k, v) in self.items()]
        return dict([(k, (dict(v) if isinstance(v, dict) else v))
                     for (k, v) in self.items()])

    def __repr__(self):
        return repr(self.dictify())

也许您正在寻找__missing__

class RecursiveDict(dict):
    '''
    A recursive default dict.

    >>> a = RecursiveDict()
    >>> a[1][2][3] = 4
    >>> a
    {1: {2: {3: 4}}}
    >>> dict(a)
    {1: {2: {3: 4}}}

    '''

    def __missing__(self, key):
        self[key] = self.__class__()
        return self[key]
于 2011-07-21T20:15:32.843 回答
2

你不能这样做。

我删除了我之前的答案,因为我在查看源代码后发现,如果你调用dict(d)ad的子类dict,它会快速复制 C 中的底层哈希,并返回一个新的 dict 对象。

对不起。

如果你真的想要这种行为,你需要创建一个RecursiveDict不继承自的类dict,并实现__iter__接口。

于 2011-07-22T13:55:35.697 回答
1

您需要覆盖__iter__.

def __iter__(self): 
    return iter((k, (v.dictify() if isinstance(v, dict) else v)) 
                for (k, v) in self.items())

而不是self.items(),您应该self.iteritems()在 Python 2 上使用。

编辑:好的,这似乎是你的问题:

>>> class B(dict): __iter__ = lambda self: iter(((1, 2), (3, 4)))
... 
>>> b = B()
>>> dict(b)
{}
>>> class B(list): __iter__ = lambda self: iter(((1, 2), (3, 4)))
... 
>>> b = B()
>>> dict(b)
{1: 2, 3: 4}

dict()因此,如果您调用的对象是 dict 的子类,则此方法不起作用。

编辑2:要清楚,defaultdictdict. dict(a_defaultdict) 仍然是空操作。

于 2011-07-21T19:01:16.100 回答
0

一旦你有你的 dictify 功能工作就做

dict = dictify

更新:这是获得此递归字典的一种简短方法:

>>> def RecursiveDict():
...   return defaultdict(RecursiveDict)

那么你就可以:

d[1][2][3] = 5
d[1][2][4] = 6
>>> d
defaultdict(<function ReturnsRecursiveDict at 0x7f3ba453a5f0>, {1: defaultdict(<function ReturnsRecursiveDict at 0x7f3ba453a5f0>, {2: defaultdict(<function ReturnsRecursiveDict at 0x7f3ba453a5f0>, {3: 5, 4: 6})})})

我没有看到实现 dictify 的简洁方法。

于 2011-07-21T19:07:51.297 回答