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