class DataFrame:
    def __init__(self, data: Optional[Any] = None, columns: Optional[List[str]] = None):
        if not data and not columns:
            self.data = {}
            self.columns = []
        elif columns:
            self.columns = columns
            self.data = {col: ListV2([]) for col in columns}
        else:
            raise ValueError("Columns cannot be empty if data is provided")
        
        if data:
            if isinstance(data, dict):
                for col, values in data.items():
                    if col not in self.columns:
                        self.columns.append(col)
                    self.data[col].lst = values
            elif isinstance(data, list):
                if not columns:
                    raise ValueError("Columns must be provided when data is a list")
                for row in data:
                    for col_name, value in zip(self.columns, row):
                        self.data[col_name].append(value)
            else:
                raise ValueError("Invalid data type. Must be either list or dict.")
        
        self.index = {}

    def set_index(self, index: str):
        if index in self.data.keys():
            col = self.data.pop(index)
            self.index = {name: idx for idx, name in enumerate(col)}

    def setitem(self, col_name: str, values: List[Any]):
        if col_name not in self.columns:
            self.columns.append(col_name)
            self.data[col_name] = ListV2([])
            self.data[col_name] = values

    def __getitem__(self, col_name: Any) -> List[Any]:
        if isinstance(col_name, list):
            return self._get_rows(col_name)
        else:
            return self.data[col_name]

    def _get_rows(self, col_names: List[str]) -> 'DataFrame':
        result_data = {col_name: [] for col_name in col_names}
        for row in zip(*(self.__getitem__(name) for name in col_names)):
            for i, col_name in enumerate(col_names):
                result_data[col_name].append(row[i])
        return DataFrame(result_data, columns=col_name)

    def loc(self, row_name: str) -> Dict[str, Any]:
        idx = self.index[row_name]
        return {col: self.data[col][idx] for col in self.columns}

    def iteritems(self):
        return self.data.items()

    def iterrows(self):
        for name, idx in self.index.items():
            row = self.loc(idx)
            yield name, row

    def astype(self, dtype: type, col_name: Optional[str] = None):
        if col_name:
            self.data[col_name] = ListV2([dtype(x) for x in self.data[col_name]])
        else:
            for col in self.columns:
                self.data[col] = ListV2([dtype(x) for x in self.data[col]])

    def drop(self, col_name: str):
        if col_name in self.columns:
            self.columns.remove(col_name)
            self.data.pop(col_name)

    def mean(self) -> Dict[str, float]:
        result = {}
        for col in self.columns:
            total = sum(self.data[col])
            mean = total / len(self.data[col])
            result[col] = mean
        return result

    def __repr__(self):
        table = [("",) + tuple(self.columns)]
        for idx, row in enumerate(zip(*self.data.values())):
            table.append([idx] + list(row))
        return "\n".join([",".join([str(el) for el in row]) for row in table])

given above is my code

Now for the following command

df[['StudentName', 'E1']]

I want the following output

expected_output = """,StudentName,E1
0,student1,92
1,student2,74
2,student3,83
3,student4,100
4,student5,77
5,student6,100
6,student7,66
7,student8,97
8,student9,95
9,student10,78"""