code: def _traverse_obj(
      obj: Any,
      prefix: str,
      action: Callable,
      crit: Callable,
      max_depth: int = 20,
      exception_ids=None,
  ):
      """
      Traverse an object to get/set variables that are accessible through the object.
      The object can be a torch.nn.Module, a class instance, or an iterable object.
      The action is performed on the object that satisfies the criteria.
  
      Examples
      --------
      >>> import torch
      >>> from deepchem.utils.differentiation_utils.editable_module import _traverse_obj, torch_float_type
      >>> class A:
      ...     def __init__(self):
      ...         self.a = 2
      ...         self.b = torch.tensor(3.0)
      ...         self.c = torch.tensor(4.0)
      ...         self.d = torch.tensor(5.0)
      ...
      >>> a = A()
      >>> def action(elmt, name, objdict, key):
      ...     print(name, elmt)
      ...
      >>> def crit(elmt):
      ...     return isinstance(elmt, torch.Tensor) and elmt.dtype in torch_float_type
      ...
      >>> _traverse_obj(a, "", action, crit)
      b tensor(3.)
      c tensor(4.)
      d tensor(5.)
  
      Parameters
      ----------
      obj: Any
          The object user wants to traverse down
      prefix: str
          Prefix of the name of the collected tensors.
      action: Callable
          The action to be performed on the object.
      crit: Callable
          The criteria to be met to perform the action.
      max_depth: int (default=20)
          Maximum recursive depth to avoid infinitely running program.
          If the maximum depth is reached, then raise a RecursionError.
      exception_ids: Set[int] (default=None)
          Set of ids of objects that are already traversed to avoid infinite loop.
  
      Raises
      ------
      RecursionError
          If the maximum depth is reached.
      RuntimeError
          If the object is not iterable or keyable.
  
      """
      if exception_ids is None:
          # None is set as default arg to avoid expanding list for multiple
          # invokes of _get_tensors without exception_ids argument
          exception_ids = set()
  
      if isinstance(obj, torch.nn.Module):
          generators = [obj._parameters.items(), obj._modules.items()]
          name_format = "{prefix}{key}"
          objdicts = [obj._parameters, obj._modules]
      elif hasattr(obj, "__dict__"):
          generators = [obj.__dict__.items()]
          name_format = "{prefix}{key}"
          objdicts = [obj.__dict__]
      elif hasattr(obj, "__iter__"):
          if isinstance(obj, dict):
              generators = [obj.items()]
          else:
              generators = [enumerate(obj)]  # type: ignore[list-item]
          name_format = "{prefix}[{key}]"
          objdicts = [obj]
      else:
          raise RuntimeError("The object must be iterable or keyable")
  
      for generator, objdict in zip(generators, objdicts):
          for key, elmt in generator:
              name = name_format.format(prefix=prefix, key=key)
              if crit(elmt):
                  action(elmt, name, objdict, key)
                  continue
  
              hasdict = hasattr(elmt, "__dict__")
              hasiter = hasattr(elmt, "__iter__")
              if hasdict or hasiter:
                  # add exception to avoid infinite loop if there is a mutual dependant on objects
                  if id(elmt) in exception_ids:
                      continue
                  else:
                      exception_ids.add(id(elmt))
  
                  if max_depth > 0:
                      _traverse_obj(
                          elmt,
                          action=action,
                          crit=crit,
                          prefix=name + "." if hasdict else name,
                          max_depth=max_depth - 1,
                          exception_ids=exception_ids,
                      )
                  else:
                      raise RecursionError(
                          "Maximum number of recursion reached"
                      )
  
imports: import inspect
  import warnings
  from abc import abstractmethod
  import copy
  import torch
  from typing import Sequence, Union, Dict, List, Callable, Any
  from deepchem.utils.attribute_utils import get_attr, set_attr, del_attr
label_desc: Use image node_id 1 for calling a global function with example usage: _traverse_obj(obj, prefix, action, crit, max_depth, exception_ids) without return types