code: def objSearchReplace(obj: Any,
                       toFrom: Dict[Any, Any],
                       checked: Set[Any]=None,
                       logMsg: str=None,
                       deepLevel: int=None) -> None:
      
  
      if checked is None:
          checked = set()
  
      checked.add(id(obj))
      pairs = [(i, getattr(obj, i)) for i in dir(obj) if not i.startswith("__")]
  
      if isinstance(obj, Mapping):
          pairs += [x for x in iteritems(obj)]
      elif isinstance(obj, (Sequence, Set)) and not isinstance(obj, string_types):
          pairs += [x for x in enumerate(obj)]
  
      for nm, o in pairs:
          if id(o) not in checked:
              mutated = False
              for old, new in toFrom.items():
                  if id(o) == id(old):
                      logging.debug(
                          "{}in object {}, attribute {} changed from {} to {}". format(
                              logMsg + ": " if logMsg else "", obj, nm, old, new))
                      if isinstance(obj, dict):
                          obj[nm] = new
                      else:
                          setattr(obj, nm, new)
                      mutated = True
              if not mutated:
                  if deepLevel is not None and deepLevel == 0:
                      continue
                  objSearchReplace(o, toFrom, checked, logMsg, deepLevel -
                                   1 if deepLevel is not None else deepLevel)
      checked.remove(id(obj))
docs: Search for an attribute in an object and replace it with another.
  
      :param obj: the object to search for the attribute
      :param toFrom: dictionary of the attribute name before and after search and replace i.e. search for the key and replace with the value
      :param checked: set of attributes of the object for recursion. optional. defaults to `set()`
      :param logMsg: a custom log message