class_docstr: Perform a database (SQL) merge operation between two DataFrame objects
  using either columns as keys or their row indexes
class_docstr_tok: ["Perform","a","database","(","SQL",")","merge","operation","between","two","DataFrame","objects","using","either","columns","as","keys","or","their","row","indexes"]
docstr: Note: has side effects (copy/delete key columns)
  
  Parameters
  ----------
  left
  right
  on
  
  Returns
  -------
  left_keys, right_keys
code: def _get_merge_keys(self):
      """
      Note: has side effects (copy/delete key columns)
  
      Parameters
      ----------
      left
      right
      on
  
      Returns
      -------
      left_keys, right_keys
      """
      left_keys = []
      right_keys = []
      join_names = []
      right_drop = []
      left_drop = []
  
      left, right = self.left, self.right
  
      is_lkey = lambda x: is_array_like(x) and len(x) == len(left)
      is_rkey = lambda x: is_array_like(x) and len(x) == len(right)
  
      # Note that pd.merge_asof() has separate 'on' and 'by' parameters. A
      # user could, for example, request 'left_index' and 'left_by'. In a
      # regular pd.merge(), users cannot specify both 'left_index' and
      # 'left_on'. (Instead, users have a MultiIndex). That means the
      # self.left_on in this function is always empty in a pd.merge(), but
      # a pd.merge_asof(left_index=True, left_by=...) will result in a
      # self.left_on array with a None in the middle of it. This requires
      # a work-around as designated in the code below.
      # See _validate_specification() for where this happens.
  
      # ugh, spaghetti re #733
      if _any(self.left_on) and _any(self.right_on):
          for lk, rk in zip(self.left_on, self.right_on):
              if is_lkey(lk):
                  left_keys.append(lk)
                  if is_rkey(rk):
                      right_keys.append(rk)
                      join_names.append(None)  # what to do?
                  else:
                      if rk is not None:
                          right_keys.append(
                              right._get_label_or_level_values(rk)
                          )
                          join_names.append(rk)
                      else:
                          # work-around for merge_asof(right_index=True)
                          right_keys.append(right.index)
                          join_names.append(right.index.name)
              else:
                  if not is_rkey(rk):
                      if rk is not None:
                          right_keys.append(
                              right._get_label_or_level_values(rk)
                          )
                      else:
                          # work-around for merge_asof(right_index=True)
                          right_keys.append(right.index)
                      if lk is not None and lk == rk:
                          # avoid key upcast in corner case (length-0)
                          if len(left) > 0:
                              right_drop.append(rk)
                          else:
                              left_drop.append(lk)
                  else:
                      right_keys.append(rk)
                  if lk is not None:
                      left_keys.append(
                          left._get_label_or_level_values(lk)
                      )
                      join_names.append(lk)
                  else:
                      # work-around for merge_asof(left_index=True)
                      left_keys.append(left.index)
                      join_names.append(left.index.name)
      elif _any(self.left_on):
          for k in self.left_on:
              if is_lkey(k):
                  left_keys.append(k)
                  join_names.append(None)
              else:
                  left_keys.append(left._get_label_or_level_values(k))
                  join_names.append(k)
          if isinstance(self.right.index, MultiIndex):
              right_keys = [
                  lev._values.take(lev_codes)
                  for lev, lev_codes in zip(
                      self.right.index.levels, self.right.index.codes
                  )
              ]
          else:
              right_keys = [self.right.index._values]
      elif _any(self.right_on):
          for k in self.right_on:
              if is_rkey(k):
                  right_keys.append(k)
                  join_names.append(None)
              else:
                  right_keys.append(right._get_label_or_level_values(k))
                  join_names.append(k)
          if isinstance(self.left.index, MultiIndex):
              left_keys = [
                  lev._values.take(lev_codes)
                  for lev, lev_codes in zip(
                      self.left.index.levels, self.left.index.codes
                  )
              ]
          else:
              left_keys = [self.left.index.values]
  
      if left_drop:
          self.left = self.left._drop_labels_or_levels(left_drop)
  
      if right_drop:
          self.right = self.right._drop_labels_or_levels(right_drop)
  
      return left_keys, right_keys, join_names
  
imports: import copy
  import string
  import warnings
  import numpy
  from pandas._libs import hashtable, join, lib
  import pandas.compat
  from pandas.compat import filter, lzip, map, range, zip
  from pandas.errors import MergeError
  from pandas.util._decorators import Appender, Substitution
  from pandas.core.dtypes.common import ensure_float64, ensure_int64, ensure_object, is_array_like, is_bool, is_bool_dtype, is_categorical_dtype, is_datetime64_dtype, is_datetime64tz_dtype, is_datetimelike, is_dtype_equal, is_extension_array_dtype, is_float_dtype, is_int64_dtype, is_integer, is_integer_dtype, is_list_like, is_number, is_numeric_dtype, is_object_dtype, needs_i8_conversion
  from pandas.core.dtypes.missing import isnull, na_value_for_dtype
  from pandas import Categorical, DataFrame, Index, MultiIndex, Series, Timedelta
  import pandas.core.algorithms
  from pandas.core.arrays.categorical import _recode_for_categories
  import pandas.core.common
  from pandas.core.frame import _merge_doc
  from pandas.core.internals import concatenate_block_managers, items_overlap_with_suffix
  import pandas.core.sorting
  from pandas.core.sorting import is_int64_overflow_possible
label_desc: Use image node_id 9 for calling the _MergeOperation obj's underlying member method code with example usage: obj._get_merge_keys() and returns: left_keys,  right_keys,  join_names