I have several functions for merging some dictionaries but over time I created a more general function that would make all these others obsolete if it weren't slower. I have the specialized (and several like it) functions that look like this:def merge_keep_lowest(dict1, dict2, *dicts):        Merge an arbitary number of :py:class:`dict`-like objects and keeps the    **lowest** encountered value for each key.    Parameters    ----------    *dicts : dict-like        The dictionaries to be merged. At least two must be given.    Returns    -------    result : any type        The merged dictionaries.        # Copy the first dict so the result's class and its properties are defined    result = dict1.copy()    # We only want to iterate once so combine the second and the other dicts.    dicts = (dict2,) + dicts    # Now iterate over each dictionary since there is no directly useable    # dict-method for this kind of operation    for d in dicts:        # Now iterate over each key of this dict.        # This way is faster than for kw in d.keys().        for kw in d:            # One could also use try ... except KeyError ... here instead of            # the if kw in result. That would be a bit faster if all dicts            # contained mostly the same keys ... but since contain checks with            # dictionaries are relativly cheap - so it doesn't make a huge            # difference            if kw in result:                # The key was already present in the result so compare it and                # replace it if it is smaller.                if d[kw] < result[kw]:                    result[kw] = d[kw]            else:                # If the key was not yet in the result dict just initialize it                result[kw] = d[kw]    return resultThere are some more for replacing the key if it is higher/shorter/longer/... all look exactly the same except for the if d[kw] < result[kw]: line.Then I thought since < is also defined as method on many data types I could generalize and allow arbitary method calls of that nature:def merge_keep_value_by_method(dict1, dict2, *dicts, method=None):        Merge an arbitary number of :py:class:`dict`-like objects and replaces the    temporary value for each key if it satisfies    ``new_value.method(tmp_value)``.    Parameters    ----------    *dicts : dict-like        The dictionaries to be merged. At least two must be given.    method : str        If a key is encountered during the merge that already is inserted in        the temporary result this value is replaced by the new value if        it satisfies the condition ``new_value.method(tmp_value)``.        Default is ``None`` but **must** be overridden to not produce an Error.    Returns    -------    result : any type        The merged dictionaries.        result = dict1.copy()    dicts = (dict2,) + dicts    for d in dicts:        for kw in d:            if kw in result:                # Here is the difference!                if getattr(d[kw], method)(result[kw]):                    result[kw] = d[kw]            else:                result[kw] = d[kw]    return resultBut this could even be more generalized by allowing arbitary functions to be executed. This has the downside that one must wrap these if they would be avaiable as methods but allows much more freedom:def merge_keep_value_by_func(dict1, dict2, *dicts, func=None):        Merge an arbitary number of :py:class:`dict`-like objects and replaces the    temporary value for each key if it satisfies    ``func(new_value, tmp_value)``.    Parameters    ----------    *dicts : dict-like        The dictionaries to be merged. At least two must be given.    func : callable        If a key is encountered during the merge that already is inserted in        the temporary result this value is replaced by the new value if        it satisfies the condition ``func(new_value, tmp_value)``.        Default is ``None`` but **must** be overridden to not produce an Error.    Returns    -------    result : any type        The merged dictionaries.        result = dict1.copy()    dicts = (dict2,) + dicts    for d in dicts:        for kw in d:            if kw in result:                # Here is the difference!                if func(d[kw], result[kw]):                    result[kw] = d[kw]            else:                result[kw] = d[kw]    return resultso one achieves the same result with some (stripped down) exemplaric dictionaries:a = {'a':1, 'b': 1}b = {'a':1, 'b': 2}and all three function calls do the same:merge_keep_lowest(a, b)merge_keep_value_by_method(a, b, method='__lt__')merge_keep_value_by_func(a, b, func=lambda x, y: True if x < y else False)but the difference here is the time it takes to evaluate the functions (with some bigger input dicts and more of them):merge_keep_lowest(*lotsofdicts)1000 loops, best of 3: 483 s per loopmerge_keep_value_by_method(*lotsofdicts, method='__lt__')1000 loops, best of 3: 1.29 ms per loopmerge_keep_value_by_func(*lotsofdicts, func=lambda x, y: True if x < y else False)1000 loops, best of 3: 940 s per loopSo if you have any comments or recommendations on the code let me know but my primarily question is:Should I just wrap the more generalized in def merge_keep_lowest? Like this:def merge_keep_lowest(dict1, dict2, *dicts):    return merge_keep_value_by_func(*((dict1, dict2)+dicts), func=lambda x, y: True if x < y else False)and don't care that it is slower or does it make sense to let very similar code exist in parallel and just keep all of them like they are? Since I'm sometimes using really big JSON or big normal dicts speed does sometimes make a difference but almost every operation is below one second even with the wrapper.
Functions to merge dictionaries with a comparison
python;performance;dictionary
The version taking func is clearly the nicest; the version which always chooses the lowest is only really better if speed is actually needed.There's no reason to force at least two parameters. Just use *dicts and default to {}. It's simpler and nicer.*_by_func is just *_by. *_by_method is horrible and should be avoided - it's not even more general and it's all stringy.Note that lambda x, y: True if x < y else False is just lambda x, y: x < y is just operator.lt.Further, you should really have a fold function, not a comparator, so you can do stuff likedef merge_keep_lowest(*dicts):    return merge_dicts_by(*dicts, fold=min)but then alsodef merge_counts(*dicts):    return merge_dicts_by(dicts, fold=sum)A fold would look likeresult[kw] = fold(result[kw], d[kw])and any comparator comp(new, old) can be turned into a fold withlambda old, new: new if comp(new, old) else oldFor example, for a comparator oflambda new, old: new <= oldone haslambda old, new: new if new <= old else oldor, simply stated,min