Storing dictionary path in Python

橙三吉。 提交于 2021-02-07 11:52:14

问题


Brand new to python, Let's say I have a dict:

kidshair = {'allkids':{'child1':{'hair':'blonde'},
                      'child2':{'hair':'black'},
                      'child3':{'hair':'red'},
                      'child4':{'hair':'brown'}}}

If child3 changes their hair colour regularly, I might want to write an application to speed up the data maintenance. In this example i'd use:

kidshair['allkids']['child3']['hair'] = ...

Is there any way to store this path as a variable so I can access it at my leasure? Obviously

mypath = kidshair['allkids']['child3']['hair']

results in mypath = 'red'. Is there any possible way to hard code the path itself so I could use:

mypath = 'blue' 

to reperesent

kidshair['allkids']['child3']['hair'] = 'blue'

Kind thanks, ATfPT


回答1:


Depending on what you need, the easiest option may be to use tuples as dictionary keys instead of nested dictionaries:

kidshair['allkids', 'child3', 'hair']
mypath = ('allkids', 'child3', 'hair')
kidshair[mypath]

The only issue with this is that you can't get a portion of the dictionary, so, for example, you can't (easily/efficiently) access everything to do with 'child3'. This may or may not be an appropriate solution for you depending on your usage.

An alternative with your current structure is to do something like this:

>>> from functools import partial
>>> test = {"a": {"b": {"c": 1}}}
>>> def itemsetter(item):
...     def f(name, value):
...         item[name] = value
...     return f
...
>>> mypath = partial(itemsetter(test["a"]["b"]), "c")
>>> mypath(2)
>>> test
{'a': {'b': {'c': 2}}}

Here we make a function itemsetter(), which (in the vein of operator.itemgetter()) gives us a function that that sets the relevant key in the given dictionary. We then use functools.partial to generate a version of this function with the key we want pre-filled. It's not quite mypath = blue either, but it's not bad.

If you don't want to bother with making something consistent to the operator module, you could simply do:

def dictsetter(item, name):
     def f(value):
         item[name] = value
     return f

mypath = dictsetter(test["a"]["b"], "c")

mypath(2)



回答2:


You could create a set of functions that accesses a 'path' for a given dictionary:

def pathGet(dictionary, path):
    for item in path.split("/"):
        dictionary = dictionary[item]
    return dictionary

def pathSet(dictionary, path, setItem):
    path = path.split("/")
    key = path[-1]
    dictionary = pathGet(dictionary, "/".join(path[:-1]))
    dictionary[key] = setItem

Usage:

>>> pathGet(kidshair, "allkids/child1/hair")
'blonde'
>>> pathSet(kidshair, "allkids/child1/hair", "blue")
>>> kidshair['allkids']['child1']
{'hair': 'blue'}



回答3:


You can save a reference to kidshair['allkids']['child3'] easily:

thiskid = kidshair['allkids']['child3']
thiskid['hair'] = 'blue'

You can't save the entire path, because the assignment changes the hair key in the kidshair['allkids']['child3'] dictionary.
If you want to change a dictionary, you must have a reference to it, not to something it contains.




回答4:


The closest you can do with your current data structure is to get the last dict and use it directly:

child = kidshair['allkids']['child3']

child['hair'] = 'blue'

This is because in Python, assignment of the style

name = value

simply binds a new object to the "variable" name. There is no way to overload this and the binding can't affect any other object (apart from freeing the object that was bound to this name until now).

On the other hand this:

name[key] = value

is entirely different because it performs an action (sets an item) on the object currently in name (dict in your case).




回答5:


try to use dpath lib https://pypi.org/project/dpath/

>>> help(dpath.util.new)
Help on function new in module dpath.util:

new(obj, path, value)
Set the element at the terminus of path to value, and create
it if it does not exist (as opposed to 'set' that can only
change existing keys).

path will NOT be treated like a glob. If it has globbing
characters in it, they will become part of the resulting
keys

>>> dpath.util.new(x, 'a/b/e/f/g', "Roffle")
>>> print json.dumps(x, indent=4, sort_keys=True)
{
    "a": {
        "b": {
            "3": 2,
            "43": 30,
            "c": "Waffles",
            "d": "Waffles",
            "e": {
                "f": {
                    "g": "Roffle"
                }
            }
        }
    }
}


来源:https://stackoverflow.com/questions/10371732/storing-dictionary-path-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!