parse a dot seperated string into dictionary variable

前端 未结 4 360
北荒
北荒 2021-01-25 08:27

I have string values as,

\"a\"
\"a.b\"
\"b.c.d\"

How to convert them into python dictionary variables as,

a
a[\"b\"]
b[\"c\"][\         


        
相关标签:
4条回答
  • 2021-01-25 08:55

    eval is fairly dangerous here, since this is untrusted input. You could use regex to grab the dict name and key names and look them up using vars and dict.get.

    import re
    
    a = {'b': {'c': True}}
    
    in_ = 'a.b.c'
    match = re.match(
        r"""(?P<dict>      # begin named group 'dict'
              [^.]+        #   one or more non-period characters
            )              # end named group 'dict'
            \.             # a literal dot
            (?P<keys>      # begin named group 'keys'
              .*           #   the rest of the string!
            )              # end named group 'keys'""",
        in_,
        flags=re.X)
    
    d = vars()[match.group('dict')]
    for key in match.group('keys'):
        d = d.get(key, None)
        if d is None:
            # handle the case where the dict doesn't have that (sub)key!
            print("Uh oh!")
            break
    result = d
    
    # result == True
    

    Or even more simply: split on dots.

    in_ = 'a.b.c'
    input_split = in_.split('.')
    d_name, keys = input_split[0], input_split[1:]
    
    d = vars()[d_name]
    for key in keys:
        d = d.get(key, None)
        if d is None:
            # same as above
    result = d
    
    0 讨论(0)
  • 2021-01-25 09:01

    The pyjq library does something quite similar to this, except you have to explicitly provide a dictionary to be the root, and you have to prefix your strings with a . to refer to whatever dictionary was your root.

    python:

    import pyjq
    d = { 'a' : { 'b' : { 'c' : 'd' } } }
    for path in ['.a', '.a.b', '.b.c.d', '.x']:
        print(pyjq.first(path, d))
    

    output:

    {'b': {'c': 'd'}}
    {'c': 'd'}
    None
    None
    
    0 讨论(0)
  • 2021-01-25 09:18

    I ran into this same problem for parsing ini files with dot-delimited keys in different sections. e.g.:

    [app]
    site1.ftp.host = hostname
    site1.ftp.username = username
    site1.database.hostname = db_host
    ; etc..
    

    So I wrote a little function to add "add_branch" to an existing dict tree:

    def add_branch(tree, vector, value):
        """
        Given a dict, a vector, and a value, insert the value into the dict
        at the tree leaf specified by the vector.  Recursive!
    
        Params:
            data (dict): The data structure to insert the vector into.
            vector (list): A list of values representing the path to the leaf node.
            value (object): The object to be inserted at the leaf
    
        Example 1:
        tree = {'a': 'apple'}
        vector = ['b', 'c', 'd']
        value = 'dog'
    
        tree = add_branch(tree, vector, value)
    
        Returns:
            tree = { 'a': 'apple', 'b': { 'c': {'d': 'dog'}}}
    
        Example 2:
        vector2 = ['b', 'c', 'e']
        value2 = 'egg'
    
        tree = add_branch(tree, vector2, value2)    
    
        Returns:
            tree = { 'a': 'apple', 'b': { 'c': {'d': 'dog', 'e': 'egg'}}}
    
        Returns:
            dict: The dict with the value placed at the path specified.
    
        Algorithm:
            If we're at the leaf, add it as key/value to the tree
            Else: If the subtree doesn't exist, create it.
                  Recurse with the subtree and the left shifted vector.
            Return the tree.
    
        """
        key = vector[0]
        tree[key] = value \
            if len(vector) == 1 \
            else add_branch(tree[key] if key in tree else {},
                            vector[1:],
                            value)
        return tree
    
    0 讨论(0)
  • 2021-01-25 09:19
    s = "a.b.c"
    s = s.replace(".", "][")+"]" # 'a][b][c]'
    i = s.find("]") # find the first "]"
    s = s[:i]+s[i+1:] # remove it 'a[b][c]'
    s = s.replace("]", "\"]").replace("[", "[\"") # add quotations 'a["b"]["c"]'
    # you can now execute it:
    v = eval(s)
    
    0 讨论(0)
提交回复
热议问题