code: def memoize(limit=0, thread_local=False, thread_safe=True):
      assert limit >= 0
      assert (
          limit <= 0 or thread_safe
      ), "memoize() it not thread safe enough to work in limiting and non-thread safe mode"
  
      def decorator(func):
          memory = {}
  
          if six.PY3:
              lock = contextlib.nullcontext()
          else:
              lock = nullcontext()
          lock = threading.Lock() if thread_safe else lock
  
          if limit:
              keys = collections.deque()
  
              def get(args):
                  if args not in memory:
                      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.setdefault(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
  
      return decorator
  
imports: import functools
  import threading
  import collections
  import contextlib
  import six
label_desc: Use image node_id 5 for calling a global function with example usage: memoize(limit, thread_local, thread_safe) and returns: decorator, wrapper, memory, memory, get, get