human
I wrote the below code

    if (cache is None):
        cache = {}
    try:
        may_have_kwargs = (has_keywords(func) is not False)
        is_unary = is_arity(1, func)
    except TypeError:
        may_have_kwargs = True
        is_unary = False
    if (key is None):
        if is_unary:
            def key(args, kwargs):
                return args[0]
        elif may_have_kwargs:
            def key(args, kwargs):
                return ((args or None), (frozenset(kwargs.items()) if kwargs else None))
        else:
            def key(args, kwargs):
                return args
    def memof(*args, **kwargs):
        k = key(args, kwargs)
        try:
            return cache[k]
        except TypeError:
            raise TypeError('Arguments    to    memoized    function    must    be    hashable')
        except KeyError:
            cache[k] = result = func(*args, **kwargs)
            return result
    try:
        memof.__name__ = func.__name__
    except AttributeError:
        pass
    memof.__doc__ = func.__doc__
    memof.__wrapped__ = func
    return memof

What's a good function header?
assistant
#Here's a good function header.

@curry
def memoize(func, cache=None, key=None):