Accessing python dict using nested key lookup string

前端 未结 3 820
傲寒
傲寒 2020-12-30 05:12

I am looking to create a simple nested \"lookup\" mechanism in python, and wanted to make sure there wasn\'t already something somewhere hidden in the vast libraries in pyth

相关标签:
3条回答
  • 2020-12-30 05:38

    Recursion still works.

    def walk_into( dict, key ):
        head, _, tail = key.partition('.')
        if tail:
            return walk_into( dict[head], tail )
        return dict, key
    d, k = walk_into( my_dict, "root.secondary.user2" )
    

    d[k] can be used for getting or putting a new value.

    0 讨论(0)
  • 2020-12-30 05:44

    There's nothing in the standard library for this purpose, but it is rather easy to code this yourself:

    >>> key = "root.secondary.user2"
    >>> reduce(dict.get, key.split("."), my_dict)
    {'age': 25, 'name': 'fred'}
    

    This exploits the fact that the look-up for the key k in the dictionary d can be written as dict.get(d, k). Applying this iteratively using reduce() leads to the desired result.

    Edit: For completeness three functions to get, set or delete dictionary keys using this method:

    def get_key(my_dict, key):
        return reduce(dict.get, key.split("."), my_dict)
    
    def set_key(my_dict, key, value):
        key = key.split(".")
        my_dict = reduce(dict.get, key[:-1], my_dict)
        my_dict[key[-1]] = value
    
    def del_key(my_dict, key):
        key = key.split(".")
        my_dict = reduce(dict.get, key[:-1], my_dict)
        del my_dict[key[-1]]
    
    0 讨论(0)
  • 2020-12-30 05:50

    You can have that. You can subclass dict, add the key lookup (and even retain the name dict) by using code similar to the one below. The {...} form however will still use the builtin dict class (now called orig_dict), so you have to enclose it, like so: Dict({...}). This implementation recursively converts dictionaries to the new form, so you don't have to use the method above for any dictionary entries that are plain dictionaries themselves.

    orig_dict = dict
    class Dict(orig_dict):
        def __init__(self, *args, **kwargs):
            super(Dict, self).__init__(*args, **kwargs)
            for k, v in self.iteritems():
                if type(v) == orig_dict and not isinstance(v, Dict):
                    super(Dict, self).__setitem__(k, Dict(v))
        def __getattribute__(self, k):
            try: return super(Dict, self).__getattribute__(k)
            except: return self.__getitem__(k)
        def __setattr__(self, k, v):
            if self.has_key(k): self.__setitem__(k, v)
            else: return super(Dict, self).__setattr__(k, v)
        def __delattr__(self, k):
            try: self.__delitem__(k)
            except: super(Dict, self).__delattr__(k)
        def __setitem__(self, k, v):
            toconvert = type(v) == orig_dict and not isinstance(v, Dict)
            super(Dict, self).__setitem__(k, Dict(v) if toconvert else v)
    
    # dict = Dict  <-- you can even do this but I advise against it
    
    # testing:
    b = Dict(a=1, b=Dict(c=2, d=3))
    c = Dict({'a': 1, 'b': {'c': 2, 'd': 3}})
    d = Dict(a=1, b={'c': 2, 'd': {'e': 3, 'f': {'g': 4}}})
    
    b.a = b.b
    b.b = 1
    d.b.d.f.g = 40
    del d.b.d.e
    d.b.c += d.b.d.f.g
    c.b.c += c.a
    del c.a
    print b
    print c
    print d
    
    0 讨论(0)
提交回复
热议问题