You could make a nestedDict class based on dict and override its get, getitem, and setitem methods to support indexing by paths. This would make the indexing and manipulation more seamless:
class nestedDict(dict):
def __getDictKey(self,index):
parent,path = self,[index]
while True:
key = path.pop(0)
if isinstance(key,(list,tuple)): path[:0] = key
elif path: parent = parent[key]
else: break
return parent,key
def __getitem__(self,index):
d,k = self.__getDictKey(index)
return dict.__getitem__(d,k)
def get(self,index,default=None):
try: return self[index]
except KeyError: return default
def __setitem__(self,index,value):
d,k = self.__getDictKey(index)
dict.__setitem__(d,k,value)
Example usage:
data = nestedDict(data)
# Indexing by path:
data['top_banner', 'image','global_image','src']
'path/to/file/5473cd34fadbb6a3-dine-1.jpg'
data['top_banner', 'image','global_image','background'] = False
# Indexing by dynamic paths:
path = ['top_banner', 'image','global_image']
data[path]
{'alt':' ', 'src':'path/to/file/5473cd34fadbb6a3-dine-1.jpg', 'sizes': ... }
data[path,"types"]
['webp ', 'jpg']
data[path].update({'intrinsicwidth': 200, 'intrinsicheight': 300})
data[path,"types"].append('pdf')
data[path,'background'] = False
# Indexing by composite paths:
pathParts = [['top_banner', 'image'],['global_image','types']]
data[pathParts]
['webp ', 'jpg']
To perform the same update on a list of different paths, you should use a loop:
paths = [path1,path2,path3]
newSize = {'intrinsicwidth': 200, 'intrinsicheight': 300}
for path in paths:
data[path,"global_image"].update(newSize)
Eventually, you may also want to override the keys, values and items method to allow iteration over paths of various lengths:
def keys(self,depth=1):
if depth ==1 :
yield from ([k] for k in dict.keys(self))
else:
for k,d in dict.items(self):
if not isinstance(d,dict): continue
yield from ([k]+p for p in nestedDict.keys(d,depth-1))
def values(self,depth=1):
yield from (self[k] for k in self.keys(depth))
def items(self,depth=1):
return zip(self.keys(depth),self.values(depth)))
Which will allow things such as:
# get all 2-part paths
for p in data.keys(2): print(p)
['top_banner', 'image']
['top_banner', 'title']
['top_banner', 'description']
# all level 4 paths and values
for k,v in data.items(4):print(k,":",v)
['top_banner', 'image', 'global_image', 'alt'] :
['top_banner', 'image', 'global_image', 'src'] : path/to/file/5473cd34fadbb6a3-dine-1.jpg
['top_banner', 'image', 'global_image', 'sizes'] : [{'src': '', 'view': '100vw', 'media': '(min-width:100px)', 'intrinsicwidth': 0, 'intrinsicheight': 0}]
['top_banner', 'image', 'global_image', 'types'] : ['webp ', 'jpg']
['top_banner', 'image', 'global_image', 'background'] : True
['top_banner', 'image', 'global_image', 'intrinsicWidth'] : 2400
['top_banner', 'image', 'global_image', 'intrinsicHeight'] : 2400
# All 'types' values (at level 3)
for v in data.values(3):print(v['types'])
['webp ', 'jpg']