diff --git a/mod_oop/3.9_12_matrix.py b/mod_oop/3.9_12_matrix.py new file mode 100644 index 0000000..1d0cc63 --- /dev/null +++ b/mod_oop/3.9_12_matrix.py @@ -0,0 +1,284 @@ +""" + 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]]) +""" + +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 __repr__(self): + return f"{self.__class__.__name__}({self.data!r})" + + 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 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 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