code: def iterrows(self):
          
          columns = self.columns
          klass = self._constructor_sliced
          for k, v in zip(self.index, self.values):
              s = klass(v, index=columns, name=k)
              yield k, s
docs: Iterate over DataFrame rows as (index, Series) pairs.
  
          Yields
          ------
          index : label or tuple of label
              The index of the row. A tuple for a `MultiIndex`.
          data : Series
              The data of the row as a Series.
  
          it : generator
              A generator that iterates over the rows of the frame.
  
          See Also
          --------
          itertuples : Iterate over DataFrame rows as namedtuples of the values.
          iteritems : Iterate over (column name, Series) pairs.
  
          Notes
          -----
  
          1. Because ``iterrows`` returns a Series for each row,
             it does **not** preserve dtypes across the rows (dtypes are
             preserved across columns for DataFrames). For example,
  
             >>> df = pd.DataFrame([[1, 1.5]], columns=['int', 'float'])
             >>> row = next(df.iterrows())[1]
             >>> row
             int      1.0
             float    1.5
             Name: 0, dtype: float64
             >>> print(row['int'].dtype)
             float64
             >>> print(df['int'].dtype)
             int64
  
             To preserve dtypes while iterating over the rows, it is better
             to use :meth:`itertuples` which returns namedtuples of the values
             and which is generally faster than ``iterrows``.
  
          2. You should **never modify** something you are iterating over.
             This is not guaranteed to work in all cases. Depending on the
             data types, the iterator returns a copy and not a view, and writing
             to it will have no effect.