Given the arguments contained in `args` and `kwargs`, returns the best match
    from the list of implementations registered on `dispatcher`.

def find(dispatcher, args, kwargs):
    """
    Given the arguments contained in `args` and `kwargs`, returns the best match
    from the list of implementations registered on `dispatcher`.
    """
    matches = []
    full_args = args
    full_kwargs = kwargs
    for func, sig in dispatcher.__functions:
        params = sig.parameters
        param_count = len(params)
        # Filter out arguments that will be consumed by catch-all parameters
        # or by keyword-only parameters.
        if sig.has_varargs:
            args = full_args[:param_count]
        else:
            args = full_args
        if sig.has_varkw or sig.has_kwonly:
            kwargs = {kw: full_kwargs[kw] for kw in params if kw in full_kwargs}
        else:
            kwargs = full_kwargs
        kwarg_set = set(kwargs)
        arg_count = len(args) + len(kwargs)
        optional_count = len(sig.defaults)
        required_count = param_count - optional_count
        # Consider candidate functions that satisfy basic conditions:
        # - argument count matches signature
        # - all keyword arguments are recognized.
        if not 0 <= param_count - arg_count <= optional_count:
            continue
        if kwargs and not kwarg_set <= set(params):
            continue
        if kwargs and args and kwarg_set & set(params[:len(args)]):
            raise TypeError("%s() got multiple values for the same parameter"
                            % dispatcher.__name__)
        arg_score = arg_count # >= 0
        type_score = 0
        specificity_score = [None] * dispatcher.__maxlen
        sig_score = required_count
        var_score = -sig.has_varargs
        indexed_kwargs = ((params.index(k), v) for k, v in kwargs.items()) if kwargs else ()
        for param_pos, value in chain(enumerate(args), indexed_kwargs):
            param_name = params[param_pos]
            if value is None and sig.defaults.get(param_name, _empty) is None:
                expected_type = type(None)
            else:
                expected_type = sig.types[param_pos]
            specificity = compare(value, expected_type)
            if specificity[0] == -1:
                break
            specificity_score[param_pos] = specificity
            type_score += 1
        else:
            score = (arg_score, type_score, specificity_score, sig_score, var_score)
            matches.append(Match(score, func, sig))
    if matches:
        if len(matches) > 1:
            matches.sort(key=lambda m: m.score, reverse=True)
            if DEBUG:
                assert matches[0].score > matches[1].score
        return matches[0].func
    else:
        return None