def dict_map_leaves(nested_dicts, func_to_call):
    """
    Applies a given callable to the leaves of a given tree / set of nested dictionaries, returning the result (without
    modifying the original dictionary)

    :param nested_dicts: Nested dictionaries to traverse
    :param func_to_call: Function to call on the 'leaves' of those nested dictionaries
    :return: Result of the transformation (dict)

    Note that this and all the functions above use the exact same algorithm/pattern for tree traversal. We could reduce
    duplication by refactoring it into a function, but that might make the code less readable.
    """
    to_update = dict(nested_dicts)

    for k, v in nested_dicts.items():
        if isinstance(v, dict):
            to_update[k] = dict_map_leaves(v, func_to_call)
        else:
            to_update[k] = func_to_call(v)

    return to_update