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

あなたのアプローチに問題はありませんが、これは、この質問で Python に実装されている Perl の Autovivification 機能に似ています。これについては@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

編集b[1]:ironchefpythonがコメントで指摘したように、私の例ではまだであるため、これは実際には私が思っていたようには機能していません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)んが、私はそれを見つけることができませんでした、あなたが方法を知っているならコメントしてください。

于 2011-07-21T20:15:04.057 に答える
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]
于 2011-07-21T20:15:32.843 に答える
2

あなたはそれをすることはできません。

ソースコードを調べた後、 のサブクラスであるdict(d)を呼び出すと、C で基になるハッシュの高速コピーが作成され、新しい dict オブジェクトが返されることがわかったため、以前の回答を削除しました。ddict

ごめん。

本当にこの動作が必要な場合は、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())

の代わりに、 Python 2self.items()で使用する必要があります。self.iteritems()

編集:OK、これはあなたの問題のようです:

>>> 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:明確にするためにdefaultdict、のサブクラスですdict。dict(a_defaultdict) はまだノーオペレーションです。

于 2011-07-21T19:01:16.100 に答える
0

ディクティファイ機能が機能したら、

dict = dictify

更新: これは、この再帰的 dict を持つ簡単な方法です:

>>> 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 に答える