code: def copy_func(f, name=None):
      
      '''
      return a function with same code, globals, defaults, closure, and 
      name (or provide a new name)
      '''
      fn = types.FunctionType(f.__code__, f.__globals__, name or f.__name__,
                              f.__defaults__, f.__closure__)
      # in case f was given attrs (note this dict is a shallow copy):
      fn.__dict__.update(f.__dict__)
      return fn
docs: Based on https://stackoverflow.com/a/30714299 (Aaron Hall)