code: def _get_required_columns(
      self, data: TileableData
  ) -> Optional[Set[Any]]:
      required_columns = set()
      successor_required_columns = self._get_successor_required_columns(
          data
      )
      if successor_required_columns is None:
          return None
      required_columns.update(successor_required_columns)
      self_required_columns = self._get_self_required_columns(data)
      required_columns.update(self_required_columns)
      return required_columns
  
imports: from typing import List, Dict, Set, Any, Type, Union, Optional
  import pandas
  from .input_column_selector import InputColumnSelector
  from .self_column_selector import SelfColumnSelector
  from ..core import register_optimization_rule
  from ...core import OptimizationRecord, OptimizationRecordType, OptimizationRule, OptimizationRecords, Optimizer
  from .....core import TileableData
  from .....core.graph import EntityGraph
  from .....dataframe.core import parse_index, BaseSeriesData, BaseDataFrameData
  from .....dataframe.datasource.core import ColumnPruneSupportedDataSourceMixin
  from .....dataframe.groupby.aggregation import DataFrameGroupByAgg
  from .....dataframe.indexing.getitem import DataFrameIndex
  from .....dataframe.merge import DataFrameMerge
  from .....utils import implements
label_desc: Use image node_id 4 for calling the ColumnPruningRule obj's underlying member method code with example usage: obj._get_required_columns(data) and returns: required_columns, None