def METHOD_NAME(
        df: pd.DataFrame,
        max_categorical_ratio: float = 0.01,
        max_categories: int = None,
        columns: t.Optional[t.List[Hashable]] = None,
) -> t.List[Hashable]:
    """Infers which features are categorical by checking types and number of unique values.
    Parameters
    ----------
    df : pd.DataFrame
        dataframe for which to infer categorical features
    max_categorical_ratio : float , default: 0.01
    max_categories : int , default: None
    columns : t.Optional[t.List[Hashable]] , default: None
    Returns
    -------
    List[Hashable]
        list of categorical features
    """
    categorical_dtypes = df.select_dtypes(include='category')
    if len(categorical_dtypes.columns) > 0:
        return list(categorical_dtypes.columns)
    if columns is not None:
        dataframe_columns = ensure_hashable_or_mutable_sequence(columns)
    else:
        dataframe_columns = df.columns
    if max_categories is None:
        return [
            column
            for column in dataframe_columns
            if is_categorical(
                t.cast(pd.Series, df[column]),
                max_categorical_ratio)]
    else:
        return [
            column
            for column in dataframe_columns
            if is_categorical(
                t.cast(pd.Series, df[column]),
                max_categorical_ratio,
                max_categories,
                max_categories,
                max_categories)]