Tips and Tricks
Path Navigation
Path navigation is one that comes up a lot when given a list of keys, To quickly and efficiently get a value given a list of keys making up a path try using the following pattern.
>>> from functools import reduce
>>> from operator import getitem
>>> path = ['a', 1]
>>> d = {'a':{1: 1, 2: 2}, 'b': {'1':{}, '2':{'2':2}}}
>>> reduce(getitem, path, d)
1
Deep Updates
Note: As of objdiff 1.2, a function that is simmilar to the one described
below is avalible as objdiff.deep_update
If you have a deep data structure made up of dictonaries (eg a config file) and
wish to apply an 'overlay' of values from a diffrent source (eg an included
file or a profile) then the naive way with dict.update()
may not work well
for you eg:
>>> base = {'a': {1:1, 2:2, 3:3}, 'b': None}
>>> updates = {'a': {1:None}}
>>> base.update(updates)
>>> base == {'a': {1: None}, 'b': None}
True
In the above we only wanted to update obj['a'][1] to be None, however the other keys where also overwritten.
ObjDiff will give you the full path of keys that need updating and the values
through the 'added' command that is returned via objdiff.obj_diff()
. These
can then be used to deeply recurse into the data and update the correct key
directly with the new value.
This can be demonstrated as below:
>>> from pprint import pprint
>>> import objdiff
>>> base = {'a': {1:1, 2:2, 3:3}, 'b': None}
>>> updates = {'a': {1:None}}
>>> for cmd in objdiff.obj_diff(base, updates):
... if isinstance(cmd, (objdiff.added, objdiff.modified)):
... ptr = base
... # we need the last key for making the update
... for key in cmd.path[:-1]:
... ptr = ptr[key]
... ptr[cmd.path[-1]] = cmd.new
...
>>> base == {'a': {1: None, 2: 2, 3: 3}, 'b': None}
True
In the above you can see we ignore all commands except add and modify. Deleted is discarded as the update dictonary has no way to specify key deletion.