2

私は2つの辞書を持っていますが、それらは同様の構造です...つまり、ネストされたキー内であっても、同じキー構造を持つ必要があります。また、これらのディクショナリは、ほとんどすべてのタイプのネストされた構造を持つ可能性があります...リスト、ディクテーションなど...これらのディクショナリを走査し、2 つの値を取得して関数から返すことができるようにしたいと考えています。

簡単な例:

dict_a = {'a':1, 'b':2, 'c':{'d':3}}
dict_b = {'a':2, 'b':4, 'c':{'d':6}}
#Note the structure is the same for these dicts
#I want to be able to do something like:
>>get_values( dict_a, dict_b)
[(1,2),(2,4),(3,6)]

1つの辞書をトラバースし、各キー(またはリストに遭遇した場合はインデックス)をリストに追加することで、自分で解決策を思いつきました...一種のキーパスとして:

key_map = []#A list of all key-paths for a dictionary
generate_key_paths(dict_a, [], key_map)
def generate_key_paths(value, key_list,key_map ):

    new_list = [item for item in key_list]
    if isinstance( value, dict):
        #Handle list
        for key, val in value.iteritems():
            new_list.append( key)
            self._generate_key_paths( val, new_list, key_map )
            new_list = [item for item in key_list]

    elif isinstance( value, list ):
        #Handle list
        for idx,item in enumerate(value):
            new_list.append( idx )  
            self._generate_key_paths( item, new_list, key_map )
            new_list = [item for item in key_list]
    else:
        #Handle data--reached farthest point you can go
        #So just append (key-path, value) to key_map
        key_map.append((new_list, value ) )

そして、キーパス、値のタプルのリストを取得したら...パスを取得し、2番目の辞書でそれに到達してその値を取得しようとします...

val_list = []
for item in key_map:
    value = get_value( item[0] )
    if value is not None:
        val_list.append( (item[1], value ) )
def get_value( key_list ):
    value = dict_b
    for item in key_list:
        try:
            value = value[item]
        except:
            value = None
            break
    return value

これは、ディクショナリが持つことができるすべての構造に対して非常にうまく機能しますが、多くの作業のように思えます。これを達成するためのよりpythonicな方法はありますか?より速く、より効率的な方法はありますか?

編集:リストでも辞書でもない値を探しているので、これらの値に到達すると、値が見つかるまでそれらの中で反復する必要があります。リストの場合は辞書のリストになることが保証されているため、常に何らかのキー:値の関係に従う必要があります。

たとえば、可能な dict は次のようになります。

dict_a = {'a':1, 'b':2, 'c':[{'d':5},{'e':6}]}

dict_b = {'a':2, 'b':4, 'c':[{'d':10},{'e':12}]}

答え:[(1,2), (2,4), (5,10), (6,12)]

4

2 に答える 2

10

flatten(zipTree(...))あなたは(存在しないが名前が私の要点を理解するはずの関数)に相当するものを探しています。

from collections import Mapping

def treezipFlat(t1,t2):
    if isinstance(t1,Mapping) and isinstance(t2,Mapping):
        assert set(t1)==set(t2)
        for k,v1 in t1.items():
            v2 = t2[k]
            for tuple in treezipFlat(v1,v2):
                yield tuple
    else:
        yield (t1,t2)

デモ:

>>> dict_a = {'a':1, 'b':2, 'c':{'d':3}}
>>> dict_b = {'a':2, 'b':4, 'c':{'d':6}}
>>> list( treezipFlat(dict_a, dict_b) )
[(1, 2), (3, 6), (2, 4)]

さらに、次のように関数を拡張することで、パスのタプルを生成できます。

from collections import Mapping

def treezipItems(t1,t2, path=[]):
    if isinstance(t1,Mapping) and isinstance(t2,Mapping):
        assert set(t1)==set(t2)
        for k,v1 in t1.items():
            v2 = t2[k]
            for tuple in treezipItems(v1,v2, path=path+[k]):
                yield tuple
    else:
        yield (path, (t1,t2))

>>> list( treezipItems(dict_a, dict_b) )
[(['a'], (1, 2)), (['c', 'd'], (3, 6)), (['b'], (2, 4))]

ここでの自然なことは、次の関数と呼ばれる関数だと思いますtreezip

def treezip(t1,t2):
    if isinstance(t1,Mapping) and isinstance(t2,Mapping):
        assert set(t1)==set(t2)
        R = {}
        for k,v1 in t1.items():
            v2 = t2[k]
            R[k] = treezip(v1,v2)
        return R
    else:
        return (t1,t2)

>>> from pprint import pprint as pp
>>> treezip(dict_a, dict_b)
{'a': (1, 2), 'c': {'d': (3, 6)}, 'b': (2, 4)}

次に、関数が呼び出されますflattenValues(またはflattenItemsキーを保持する場合)。

于 2012-04-24T22:14:53.503 に答える
3
dict_a = {'a':1, 'b':2, 'c':{'d':3,'e':{'f':4}}}
dict_b = {'a':2, 'b':4, 'c':{'d':6,'e':{'f':7}}}

これが最初のバージョンで、開梱して組み合わせるだけです

def gen(dict1, dict2):
    for key in dict1:
        if isinstance(dict1[key],dict):
            for item in gen(dict1[key],dict2[key]):
                yield item
        else:
            yield dict1[key], dict2[key]

# prints [(1, 2), (4, 7), (3, 6), (2, 4)]
print list(gen(dict_a, dict_b))

これが2番目のバージョンで、ソースも並べ替えられます(ただし、結果のリストを並べ替えた方がよいと思います)

def gen(dict1, dict2):
    for key in sorted(dict1):
        if isinstance(dict1[key],dict):
            for item in gen(dict1[key],dict2[key]):
                yield item
        else:
            yield dict1[key], dict2[key]

print list(gen(dict_a, dict_b))

# prints [(1, 2), (2, 4), (3, 6), (4, 7)]
print list(gen(dict_a,dict_b))

このsrcは、多くの異なるtutsに見られる可能性のあるflattengeneratorに基づいています。

于 2012-04-24T23:47:00.440 に答える