from typing import Union
from typing import Dict
from typing import List
from typing import Callable
from typing import Sequence


def walk_tree_and_modify(
    obj: Union[Dict, List], func: Callable, ignore: Sequence[str] = ()
) -> Union[Dict, List]:
    """
    Traverse tree of dicts and lists, and modify each node.

    :param obj: the object to traverse

    :param func:
        the function to call on each dict node, returning a dict

    :param ignore: the names of keys that should not be explored

    :returns: a version of the tree with every node modified by `func`
    """
    if isinstance(obj, List):
        return [walk_tree_and_modify(item, func, ignore) for item in obj]
    if isinstance(obj, Dict):

        obj_dict: Dict = func(obj)

        for key, value in obj_dict.items():
            if isinstance(value, (Dict, List)) and key not in ignore:
                obj_dict[key] = walk_tree_and_modify(value, func, ignore)

        return obj_dict

    return obj