from functools import reduce


def value_for_key_path(dictionary, key_path, ignore_non_existing=True):
    """
    Returns the value of a dictionary given the corresponding key(s). Nested keys can be specified in a single
    string separated by dots, allowing access to nested dictionary values. If a list of dictionaries is passed,
    then a list of corresponding values is returned.
    :param dictionary: data dictionary (or list of dictionaries)
    :param key_path: key(s) to access (e.g. key_path="key1.subkey1.subsubkey1")
    :param ignore_non_existing: whether to raise an exception or not when a key does not exist in dictionary.
    :return: corresponding value
    """
    if type(dictionary) == dict:
        if key_path is None:
            return None
        keys = key_path.split(".")
        try:
            return reduce(lambda d, k: d[k], keys, dictionary)
        except KeyError as e:
            if ignore_non_existing:
                return None
            else:
                raise e
    else:
        output = list()
        if key_path is None:
            return output
        keys = key_path.split(".")
        for item in dictionary:
            try:
                output.append(reduce(lambda d, k: d[k], keys, item))
            except KeyError as e:
                if ignore_non_existing:
                    continue
                else:
                    raise e
        return output