2014-07-22 27 views
7

Genel durumda iç içe geçmiş dikdörtgensel girişlere nasıl yardımcı olabiliriz? Yinelemeli iç içe geçme kurallarına erişmenin pythonik yolu

from collections import defaultdict 

D = lambda: defaultdict(D) 
d = D() 

keys = ['k1', 'k2', 'k3'] 
value = 'v' 

if len(keys) == 3: 
    k1, k2, k3 = keys 
    d[k1][k2][k3] = value 
else: 
    ??? 

Ben reduce, d.__getitem__ ve d.__setitem__ bazı korkunç yolda gidiyor ama kesinlikle daha zarif bir yolu olmalı hissetti ...

+0

İyi bulma @Dave. Aramanızdaki algo, SO'nun İlgili soruları aramasından daha iyidir. – Gerrat

+0

Gerçekten iyi buldum. Kendi sorumu yinelemeli olarak kapatıyorum ... :) – wim

cevap

3

Bu oldukça çirkin ama bu işleri edildi:

def set_val(d, keys, val): 
    reduce(lambda x,y: x[y], keys[:-1], d)[keys[-1]] = val 

Biraz daha okunabilir versiyonu:

def set_val(d, keys, val): 
    last = keys[-1] # Key we want to set val on 
    search_keys = keys[:-1] # Keys we need to traverse 
    reduce(lambda x,y: x[y], search_keys, d)[last] = val 

Kullanım:

>>> from collections import defaultdict 
>>> D = lambda: defaultdict(D) 
>>> d = D() 
>>> set_val(d, ['k1', 'k2', 'k3'], "hi") 
>>> d 
defaultdict(<function <lambda> at 0x7fbd365ac7d0>, {'k1': defaultdict(<function <lambda> at 0x7fbd365ac7d0>, {'k2': defaultdict(<function <lambda> at 0x7fbd365ac7d0>, {'k3': 'hi'})})}) 
>>> d['k1']['k2']['k3'] 
'hi' 

İstenilen en iç dicti (keys[:-1]) ulaşması reduce kullanır, daha sonra istenen değere (output_of_reduce[keys[-1]] = val) için listede son anahtar ayarlar.

Python 3'te bunu kullanmak için functools import reduce'a ihtiyacınız olduğunu unutmayın. Sadece Özyinelemeyi kullanabilirsiniz

def set_val(d, keys, val): 
    out = d 
    for k in keys[:-1]: 
     out = out[k] 
    out[keys[-1]] = val 
+0

bu işe yarıyor, ama umarım daha şık bir fikir vardır .. – wim

2

:

İşte netlik için genişletilmiş kod. Döngüden daha zarif ya da daha pythonik olduğunu söyleyemem ya da azaltmayı kullanamıyorum.

def assign(dct, keylist, value): 
    if not keylist: 
     dct = value 
    else: 
     dct[keylist[0]] = assign(dct[keylist[0]], keylist[1:], value) 
    return dct 


if __name__ == '__main__': 
    from collections import defaultdict 
    D = lambda: defaultdict(D) 
    d = D() 
    keys = ['k1', 'k2', 'k3'] 
    value = 'v' 
    assign(d, keys, value) 
    print d['k1']['k2']['k3'] 

[prints] 'v' 
İlgili konular