def get_param(params, name, default_val=None):
    """
    Retrieves a param from a parameter object/dict. If the parameter does not exist, will return default_val.
    In case the default_val is of type dictionary, and a value is found in the params - the function
    will return the default value dictionary with internal values overridden by the found value

    i.e.
    default_opt_params = {'lr':0.1, 'momentum':0.99, 'alpha':0.001}
    training_params = {'optimizer_params': {'lr':0.0001}, 'batch': 32 .... }
    get_param(training_params, name='optimizer_params', default_val=default_opt_params)
    will return {'lr':0.0001, 'momentum':0.99, 'alpha':0.001}

    :param params:      an object (typically HpmStruct) or a dict holding the params
    :param name:        name of the searched parameter
    :param default_val: assumed to be the same type as the value searched in the params
    :return:            the found value, or default if not found
    """
    if isinstance(params, dict):
        if name in params:
            if isinstance(default_val, dict):
                return {**default_val, **params[name]}
            else:
                return params[name]
        else:
            return default_val
    elif hasattr(params, name):
        if isinstance(default_val, dict):
            return {**default_val, **getattr(params, name)}
        else:
            return getattr(params, name)
    else:
        return default_val