py_stepik/mod_oop/3.9_12_matrix.py

302 lines
16 KiB
Python
Raw Normal View History

2024-04-17 12:51:51 +00:00
"""
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]])
2024-04-17 13:02:02 +00:00
>>> 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
2024-04-17 12:51:51 +00:00
"""
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,
)
]
2024-04-17 13:02:02 +00:00
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})"
2024-04-17 12:51:51 +00:00
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()