""" https://stepik.org/lesson/701994/step/12?unit=702095 Объявите класс Matrix (матрица) для операций с матрицами. Объекты этого класса должны создаваться командой: m1 = Matrix(rows, cols, fill_value) где rows, cols - число строк и столбцов матрицы; fill_value - заполняемое начальное значение элементов матрицы (должно быть число: целое или вещественное). >>> rows, cols, fill_value = 4, 2, 0 >>> m1 = Matrix(rows, cols, fill_value) Если в качестве аргументов передаются не числа, то генерировать исключение: raise TypeError('аргументы rows, cols - целые числа; fill_value - произвольное число') >>> m1 = Matrix("тест", cols, fill_value) Traceback (most recent call last): ... TypeError: аргументы rows, cols - целые числа; fill_value - произвольное число Также объекты можно создавать командой: m2 = Matrix(list2D) где list2D - двумерный список (прямоугольный), состоящий из чисел (целых или вещественных). >>> list2D = [[1, 2], [3, 4], [5, 6], [7, 8]] >>> m2 = Matrix(list2D) Если список list2D не прямоугольный, или хотя бы один из его элементов не число, то генерировать исключение командой: raise TypeError('список должен быть прямоугольным, состоящим из чисел') >>> m2 = Matrix([[1, 2], [3], [4, 5], [6, 7, 8]]) Traceback (most recent call last): ... TypeError: список должен быть прямоугольным, состоящим из чисел Для объектов класса Matrix должны выполняться следующие команды: matrix = Matrix(4, 5, 0) res = matrix[0, 0] # возвращается первый элемент матрицы matrix[indx1, indx2] = value # элементу матрицы с индексами (indx1, indx2) присваивается новое значение >>> matrix = Matrix(4, 5, 0) >>> matrix[0, 0] = 42 >>> matrix[0, 0] 42 Если в результате присвоения тип данных не соответствует числу, то генерировать исключение командой: raise TypeError('значения матрицы должны быть числами') Если указываются недопустимые индексы матрицы (должны быть целыми числами от 0 и до размеров матрицы), то генерировать исключение: raise IndexError('недопустимые значения индексов') Также с объектами класса Matrix должны выполняться операторы: matrix = m1 + m2 # сложение соответствующих значений элементов матриц m1 и m2 matrix = m1 + 10 # прибавление числа ко всем элементам матрицы m1 matrix = m1 - m2 # вычитание соответствующих значений элементов матриц m1 и m2 matrix = m1 - 10 # вычитание числа из всех элементов матрицы m1 Во всех этих операция должна формироваться новая матрица с соответствующими значениями. Если размеры матриц не совпадают (разные хотя бы по одной оси), то генерировать исключение командой: raise ValueError('операции возможны только с матрицами равных размеров') Пример для понимания использования индексов (эти строчки в программе писать не нужно): mt = Matrix([[1, 2], [3, 4]]) res = mt[0, 0] # 1 res = mt[0, 1] # 2 res = mt[1, 0] # 3 res = mt[1, 1] # 4 P.S. В программе нужно объявить только класс. Выводить на экран ничего не нужно. --- >>> Matrix(2, 2) - 2 Matrix([[-2, -2], [-2, -2]]) >>> Matrix(2, 2) == Matrix([[0, 0], [0, 0]]) True >>> Matrix(2, 2, 2) == [[2, 2], [2, 2]] True >>> Matrix(2, 2, 2) == [[2, -2], [2, 2]] False """ from functools import singledispatchmethod from operator import add, floordiv, mod, mul, sub, truediv, xor def add_ops(*ops): def decorator(cls): def make_methods(op): def method_new(self, other): return self.__class__(self.mapped(op, other)) def method_ip(self, other): self.data = self.mapped(op, other) return self def method_r(self, other): if isinstance(other, (int, float)): return self.__class__( [ *map( lambda row: [*map(lambda x: op(other, x), row)], self.data, ) ] ) return NotImplemented return { f"__{op.__name__}__": method_new, f"__i{op.__name__}__": method_ip, f"__r{op.__name__}__": method_r, } for op in ops: for name, method in make_methods(op).items(): setattr(cls, name, method) return cls return decorator @add_ops(add, sub, mul, truediv, floordiv, xor, mod, pow) class Matrix: @singledispatchmethod def __init__(self, rows, cols, fill_value=0): args_valid = ( isinstance(rows, int) and isinstance(cols, int) and isinstance(fill_value, (int, float)) ) if not args_valid: raise TypeError( "аргументы rows, cols - целые числа; fill_value - произвольное число" ) self.rows, self.cols = rows, cols self.data = [[fill_value for _ in range(cols)] for _ in range(rows)] @__init__.register(list) @__init__.register(tuple) def _from_list_tuple(self, data): data = data or [[]] if not isinstance(data[0], (list, tuple)): raise TypeError("список должен быть прямоугольным, состоящим из чисел") self.rows, self.cols = len(data), len(data[0]) self.data = [] for row in data: if len(row) != self.cols: raise TypeError("список должен быть прямоугольным, состоящим из чисел") new_row = [] for value in row: if not isinstance(value, (int, float)): raise TypeError( "список должен быть прямоугольным, состоящим из чисел" ) new_row.append(value) self.data.append(new_row) 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.data[row][col] def __setitem__(self, index, new_value): row, col = self.validate_index(index) if not isinstance(new_value, (int, float)): raise TypeError("значения матрицы должны быть числами") self.data[row][col] = new_value def validate_size(self, other): if not isinstance(other, self.__class__): raise NotImplementedError if (self.rows, self.cols) != (other.rows, other.cols): raise ValueError("операции возможны только с матрицами равных размеров") def mapped(self, op, other): if isinstance(other, (int, float)): return [*map(lambda row: [*map(lambda x: op(x, other), row)], self.data)] self.validate_size(other) return [ *map( lambda self_row, other_row: [ *map( lambda self_x, other_x: op(self_x, other_x), self_row, other_row ) ], self.data, other.data, ) ] def __len__(self): return self.rows def __iter__(self): return ( (self.cells[row][col].data for col in range(self.cols)) for row in range(self.rows) ) def __eq__(self, other): if isinstance(other, self.__class__): return self.data == other.data if isinstance(other, list): return self.data == other return NotImplemented def __ne__(self, other): return not self == other def __repr__(self): return f"{self.__class__.__name__}({self.data!r})" def tests(): # 10x = https://stepik.org/lesson/701994/step/12?discussion=5990560&unit=702095 code = ( b"Y-w|JGDILfAX{58EFdynEFfDmEFd&pEFfDoEFd;4AU9oI3UqRLItm~lARu#eAUz;WVRUk7cqnX" + b"Xb96F9DGFtHV`Xr3AXIs9WkqswZ*n>cARr(hVRLh3a&#b6a&=`2Wo&b0Itm~lARu9Lb7gXLAVy(q" + b"b7d?bBGA3iwII=f(6i9B(7n*L(SXpn(SXpt(6Z35(7VvS(Sgy7AketcfzZ0pyU~u(htRdqz0kPOw" + b"IEb^aAieua&K}V(6rFI(T^a|z0kEF(7(}u(T~u)(7w@w(6i9K(7Vx$(7n*U(6i9KAkl%)ztFhRfz" + b"Z0puprQ~AkezdzR)WkL?AsNTU#+KAX{BsEFf" + b"DmEFd&pEFfDoEFd;rT?%w^c{&OpARr)fbRaz-O<{C$X?Q4XX>)WkL@5eocw=R7bRblDaAieua&K}" + b"v3LqdLAYpTJWpZ>NRC0A?3T13_WjYEVARr)Nb8}^KbRb4yY;$ESAR^Gc(6u1ZfzY$iwa~rLwb6jk" + b"xY2;nzRNRC0A?3T13_WjYEVARr)Nb8}^KbRb4yY;$ESAR^Gc(6u1ZfzY$iwa~rLwb6jkxY2;nzRo" + b"yU@PSveAptg&@$d(SXpi(Sy*u(6!LL(Sp#v(6S)VvLMj9(7w>U(Sgx|(SXr|(7Mrr(7w@t(6t~O<{C" + b"$X?Q3!EFd*3ATcm03So0|WpZ>NZDDkBX?R;REFd&pAU!=GF)%D3BGA3iwa~KAwb6jkz0kfO(7w@v" + b"(SXpf(6P|I(Sp#h(7Pbey3oGRz0rZug3*A{gV4Isg3!LvfFRJg(7VvMAYWf+WprtDWo=(yA_@w0a" + b"(OxmARr(hb|5_NRC0A?3T" + b"13_WjYEVARr)Nb8}^KbRb4yY;$ESAR^Gc(6u1ZfzY$iwa~rLwb6jkxY2;nzRNMqzAoWh@{f" + b"(7n*LAkl%)v(UBBz0kGMfY7+nfY83sve2;5yU@PTfzga0(74fo(7MpO(T>rF(6!LL(74dGAW3dyW" + b"q3t$a&K}X3JPsubaH8UTQDpjFkK)$AU6tOb8}^KbRcbEbaH8UTQDpjFkK)$Js>wMAR^Gc(6!LA(6" + b"!Nk(7n*UAke`c{&OpARr)ZVRUk7cv~AxCkkbFV`Xr3AXIs9WkqswZ*n>cARr(hVRLh3a&#b6a&=`2Wo&b0Itm~lARu9Lb7gXLAVy(" + b"qb7d?bBGA3iwII=f(6i9B(7n*L(SXpn(SXpt(6Z35(7VvS(Sgy7AketcfzZ0pyU~u(htRdqz0kPO" + b"wIEb^aAieua&K}V(6S(3Uvp)2X>?_6Utb~$3T-hUJs?eCbaH8UC|g@GEFdynEFfDmEFd&pT`3A}G" + b"9W!5O<{C$X?Q4GTQMvkF`c{&OpARr)ZVRUk7cpyC>Z80D#AZ" + b";=VWq4y{aC9J6VQh6}MRIa)aykkiARr)Nb8}^KbRbl6b!7@=Y;$Eg3LqdLAYpTJWpZ>NMqzAoWh@" + b"{f(7n*LAkl%)v(UBBz0kGMfY7+nfY83sve2;5yU@PTfzga0(74fo(7MpO(T>rF(6!LL(74dGAXZ^" + b")b!A0za&K}V(7(}u(6}JcfzZ3qzRAz0r%&g&@&@" + b"(6G?A(7e#K(SXpt(6S;53T-hUJs?eCbaH8UC|g@GEFdynEFfDmEFd&pT`3A}G9W!5O<{C$X?Q4GT" + b"QMvkFO(6Z5h(6G^o(6G^h(TpI`jnKN#x6rlFywJbUyU~x)fFRJi(7VvE(Sgx{(6As" + b")VRUk7cp?g6b8}^KbRcbEbaH8UTQMvkFqKAR^Gc(6!LA(6!Nk(7n*UAkeBFRF)Sc4T_8O@ATc0Z3LqdLAR" + b"r(hVQyp~Z8BRhEFdslAU!=GF)Sb=(74fo(S^{y(6rFK(TmWvAke(fu+f6ifY7+nhS7^4(7n*LAke" + b"hXzRN(6!LL(74f$AkehXzRO(6rF7(6Z35(Sp&8(SgyAAkdxAp3suetI(Ms(T&" + b"i$(6`XF(7e#U(7Vx((SRV(y3o7Ou+f3hfzYrZO<{C$X?P+E3TeKEFdD#z0kGLve32BfY80rz97)P(Sp%{(6G?4(7w@v(6G?E(6A" + b"uTzRN(6!LL(74f$Ake(fu+f6ifY7+nhS7^4(Sab*htRmufzZ3" + b"qzRS(6-RM(6AuTztFzWyU~NuhtRmug3*l8fzgj3(" + b"6!LC(74dO(74fu(7n*G(T^a|ywI@Gg3*A`xY35tunJ*wb7gXLAZ;;QFf1T2T_8O@ATc0eZe$>BF<" + b"UV#ATeDaJv|^XAYpD~AZ;>RFf1T2T_8O@ATS_Y3LqdLARr(hVQyp~Z8BRhEFdvmAU!=GF)Sb=(74" + b"fo(S^{y(6rFK(TmWvAke(fu+f6ifY7+nhS7^4(7n*LAkehXzReGAR^Gc(6!LA(" + b"6!Nk(7n*UAkeNZDDkBX?R;OEFdslAU!=GZDDkBX?R;PEFdvmAU!=GF)Sb=(74fo(S^{y(6rFK(TmWvAke(" + b"fu+f6ifY7+nhG8Jkz0kEF(6rFL(7VvK(7n*GAke(fwa~rMkI{nBjM0J7k08*$(SXpnAkeNZCfxbATV7ZJ" + b"v|_8TQMvkFbFAR^Gc(6!LA(6!Nk(7n*UAke