def METHOD_NAME(func):
    memory = {}
    lock = threading.Lock()
    if limit:
        keys = collections.deque()
        def get(args):
            try:
                return memory[args]
            except KeyError:
                with lock:
                    if args not in memory:
                        fargs = args[-1]
                        memory[args] = func(*fargs)
                        keys.append(args)
                        if len(keys) > limit:
                            del memory[keys.popleft()]
                    return memory[args]
    else:
        def get(args):
            if args not in memory:
                with lock:
                    if args not in memory:
                        fargs = args[-1]
                        memory[args] = func(*fargs)
            return memory[args]
    if thread_local:
        @functools.wraps(func)
        def wrapper(*args):
            th = threading.current_thread()
            return get((th.ident, th.name, args))
    else:
        @functools.wraps(func)
        def wrapper(*args):
            return get(('', '', args))
    return wrapper