如何更改实例的dict()行为

时间:2011-07-21 18:36:12

标签: python dictionary override autovivification

所以我正在编写一个扩展字典的类,该字典现在使用一种方法“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()

相同

6 个答案:

答案 0 :(得分:28)

您的方法没有任何问题,但这与Perl的Autovivification功能相似,后者已在Python in this question中实现。为此向@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&gt; = 2.5

class RecursiveDict(dict):
    """Implementation of perl's autovivification feature."""
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

答案 1 :(得分: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'>

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

答案 2 :(得分:2)

你想要打印它就像一个字典吗?用这个:

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]

答案 3 :(得分:2)

你不能这样做。

我删除了之前的回答,因为我在查看源代码后发现,如果您在dict(d)的{​​{1}}上调用d,则会快速C中底层哈希的副本,并返回一个新的dict对象。

对不起。

如果您确实需要此行为,则需要创建一个不从dict继承的RecursiveDict类,并实现dict接口。

答案 4 :(得分:1)

您需要覆盖__iter__

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

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

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

>>> 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)仍然是一个无操作。

答案 5 :(得分: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})})})

我没有看到一种巧妙的方式来实现思想。