Given an object that can be a nested structure of dict-like or array-like objects. What is the pythonic way to set a value on that object given a dotted path as a string?
Example:
obj = [
{'a': [1, 2]},
{'b': {
'c': [3, 4],
}},
]
path = '1.b.c.0'
# operation that sets a value at the given path, e.g.
obj[path] = 5
# or
set_value(obj, path, 5)
The above call/assignment should replace the 3
in the example with a 5
.
Note: The path can contain list indices as well as keys. Every level of the object can be an array
or a dict
or something that behaves like that.
The solution should be roughly like what the npm package object-path
does in javascript.
The Python "AttributeError: 'list' object has no attribute" occurs when we access an attribute that doesn't exist on a list. To solve the error, access the list element at a specific index or correct the assignment.
Adding attributes to a Python class is very straight forward, you just use the '. ' operator after an instance of the class with whatever arbitrary name you want the attribute to be called, followed by its value.
A non-recursive version, which also works for numeric dictionary keys:
from collections.abc import MutableMapping
def set_value_at_path(obj, path, value):
*parts, last = path.split('.')
for part in parts:
if isinstance(obj, MutableMapping):
obj = obj[part]
else:
obj = obj[int(part)]
if isinstance(obj, MutableMapping):
obj[last] = value
else:
obj[int(last)] = value
Sounds like a job for recursion and str.partition
:
def set_value(obj, path, val):
first, sep, rest = path.partition(".")
if first.isnumeric():
first = int(first)
if rest:
new_obj = obj[first]
set_value(new_obj, rest, val)
else:
obj[first] = val
Because 1
is not the same as "1"
, I'm making a compromise here: If something looks like a number, it's treated as a number.
You could get this to behave on custom objects, but not really on builtin types without awful hackery, because Python doesn't have an equivalent to object.prototype
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With