docstr: Locate complex types in :attr:`parameters`, create hashes for them, add lookup entries
  linking their original values to their hashes, then update their values in
  :attr:`parameters` to their hashes to facilitate Description saving
docstr_tok: ["Locate","complex","types","in",":","attr",":","`","parameters","`",",","create","hashes","for","them",",","add","lookup","entries","linking","their","original","values","to","their","hashes",",","then","update","their","values","in",":","attr",":","`","parameters","`","to","their","hashes","to","facilitate","Description","saving"]
code: def handle_complex_types(self):
      """Locate complex types in :attr:`parameters`, create hashes for them, add lookup entries
      linking their original values to their hashes, then update their values in
      :attr:`parameters` to their hashes to facilitate Description saving
      """
      dataframe_hashes = {}
  
      def enter(path, key, value):
          """Produce iterable of attributes to remap for instances of :class:`metrics.Metric`"""
          if isinstance(value, Metric):
              metric_attrs = ["name", "metric_function", "direction"]
              return (
                  {},
                  [(_, getattr(value, _)) for _ in metric_attrs],
              )
  
          if isinstance(value, EngineerStep):
              return ({}, list(value.get_key_data().items()))
          if isinstance(value, FeatureEngineer):
              return ({}, list(value.get_key_data().items()))
  
          return default_enter(path, key, value)
  
      def visit(path, key, value):
          """Check whether a parameter is of a complex type. If not, return it unchanged.
          Otherwise, 1) create a hash for its value; 2) save a complex type lookup entry linking
          `key`, `value`, and the hash for `value`; and 3) return the hashed value with `key`,
          instead of the original complex-typed `value`
  
          Parameters
          ----------
          path: Tuple
              The path of keys that leads to `key`
          key: Str
              The parameter name
          value: *
              The value of the parameter `key`
  
          Returns
          -------
          Tuple of (`key`, value), in which value is either unchanged or a hash for the original
          `value`"""
          if isinstance(value, BaseKerasCallback):
              return (key, keras_callback_to_dict(value))
          if isinstance(value, BaseKerasInitializer):
              return (key, keras_initializer_to_dict(value))
          if isinstance(value, Sentinel):
              return (key, value.sentinel)
          elif callable(value) or isinstance(value, pd.DataFrame):
              # FLAG: Look into adding package version number to hashed attributes
              hashed_value = make_hash_sha256(value)
  
              if isinstance(value, pd.DataFrame):
                  dataframe_hashes.setdefault(hashed_value, []).append(
                      key
                  )
  
              if (
                  self.tested_keys_dir is not None
              ):  # Key-making not blacklisted
                  self.add_complex_type_lookup_entry(
                      path, key, value, hashed_value
                  )
              return (key, hashed_value)
          return (key, value)
  
      self.parameters = remap(self.parameters, visit=visit, enter=enter)
  
      #################### Check for Identical DataFrames ####################
      for df_hash, df_names in dataframe_hashes.items():
          if len(df_names) > 1:
              G.warn(
                  f"The dataframes: {df_names} are identical. Scores may be misleading!"
              )
  
imports: from hyperparameter_hunter.feature_engineering import FeatureEngineer, EngineerStep
  from hyperparameter_hunter.i_o.exceptions import EnvironmentInvalidError, EnvironmentInactiveError
  from hyperparameter_hunter.keys.hashing import make_hash_sha256
  from hyperparameter_hunter.compat.keras_helper import keras_callback_to_dict, keras_initializer_to_dict, parameterize_compiled_keras_model
  from hyperparameter_hunter.compat.keras_optimization_helper import initialize_dummy_model
  from hyperparameter_hunter.metrics import Metric
  from hyperparameter_hunter.sentinels import Sentinel
  from hyperparameter_hunter.settings import G
  from hyperparameter_hunter.utils.file_utils import write_json, read_json, add_to_json, make_dirs
  from hyperparameter_hunter.utils.file_utils import RetryMakeDirs
  from hyperparameter_hunter.utils.general_utils import subdict
  from hyperparameter_hunter.utils.boltons_utils import remap, default_enter
  from abc import ABCMeta, abstractmethod
  from copy import deepcopy
  from functools import partial
  from inspect import isclass, getsource
  from os import listdir
  import os.path
  import pandas
  import shelve
label_desc: Use image node_id 7 for calling the KeyMaker obj's underlying member method code with example usage: obj.handle_complex_types() and returns: default_enter, key,  value, dict,  unknown, dict,  list, dict,  list, key,  keras_callback_to_dict, key,  keras_initializer_to_dict, key,  value, key,  hashed_value