Python: модуль управления матрицей

Я сделал модуль под названием «Matrix Manipulation» на Python для сложения, вычитания, умножения и транспонирования матриц.

Я хочу знать, как улучшить качество кода, и если что-то не так с моим кодом

вот код на github:
https://github.com/FaresAhmedb/matrixmanp

#!/usr/bin/python3
"""Matrix Manipulation module to add, substract, multiply matrices.
Copyright (C) 2021 Fares Ahmed
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
"""

# pylint: disable=C0103 # Variable name "m" is "iNvAlId-nAmE"

import argparse
import json


name="matrixmanp"
__version__ = '0.1'
__all__     = ['Matrix']


class MatrixError(Exception):

    """Error for the Matrix Object invalid operations"""


class Matrix:

    """Matrix Object: add, sub, mul, and a lot more"""

    # Object Creation: START
    def __init__(self, matrix) -> None:
        """Initialize matrix object."""
        self.matrix = matrix

    def __str__(self, dims=True):
        """Return the Matrix, the size of it (Activated when using print)"""
        for row in self.matrix:
            print(' '.join(map(str,row)))

        if dims:
            return self.__repr__()

        return ''

    def __repr__(self):
        """Return the matrix size (string representation of the object)"""
        return '({}x{})'.format(len(self.matrix), len(self.matrix[0]))
    # Object Creation: END

    # Object Expressions: START
    def __pos__(self):
        """Positive operator: +A | Return the matrix * 1 (copy)"""
        result = list()

        for i in range(len(self.matrix)):
            result.append([])
            for m in range(len(self.matrix[0])):
                result[i].append(+self.matrix[i][m])

        return Matrix(result)

    def __neg__(self):
        """Negative operator: -A. | Returns the matrix * -1"""
        result = list()

        for i in range(len(self.matrix)):
            result.append([])
            for m in range(len(self.matrix[0])):
                result[i].append(-self.matrix[i][m])

        return Matrix(result)
    # Object Expressions: END

    # Object Math operations: START
    def __add__(self, other):
        """Matrix Addition: A + B or A + INT."""
        if isinstance(other, Matrix):
            # A + B
            result = list()

            if (len(self.matrix)    != len(other.matrix) or
                len(self.matrix[0]) != len(other.matrix[0])):
                raise MatrixError('To add matrices, the matrices must have'
                ' the same dimensions')

            for m in range(len(self.matrix)):
                result.append([])
                for j in range(len(self.matrix[0])):
                    result[m].append(self.matrix[m][j] + other.matrix[m][j])

        else:
            # A + INT
            result = list()

            for m in range(len(self.matrix)):
                result.append([])
                for i in range(len(self.matrix[0])):
                    result[m].append(self.matrix[m][i] + other)

        return Matrix(result)

    def __sub__(self, other):
        """Matrix Subtraction: A - B or A - INT."""
        if isinstance(other, Matrix):
            # A + B
            result = list()

            if (len(self.matrix)    != len(other.matrix) or
                len(self.matrix[0]) != len(other.matrix[0])):
                raise MatrixError('To sub matrices, the matrices must have'
                ' the same dimensions')

            for m in range(len(self.matrix)):
                result.append([])
                for j in range(len(self.matrix[0])):
                    result[m].append(self.matrix[m][j] - other.matrix[m][j])
        else:
            # A + INT
            result = list()

            for m in range(len(self.matrix)):
                result.append([])
                for i in range(len(self.matrix[0])):
                    result[m].append(self.matrix[m][i] - other)

        return Matrix(result)

    def __mul__(self, other):
        """Matrix Multiplication: A * B or A * INT."""
        if isinstance(other, Matrix):
            # A * B
            if len(self.matrix[0]) != len(other.matrix):
                raise MatrixError('The number of rows in matrix A must be'
                ' equal to the number of columns in B matrix')

            # References:
            # https://www.geeksforgeeks.org/python-program-multiply-two-matrices
            result = [[sum(a * b for a, b in zip(A_row, B_col))
                            for B_col in zip(*other.matrix)]
                                    for A_row in self.matrix]
        else:
            # A * INT
            result = list()

            for m in range(len(self.matrix)):
                result.append([])
                for i in range(len(self.matrix[0])):
                    result[m].append(self.matrix[m][i] * other)

        return Matrix(result)
    # Object Math opertaions: END

    # Object Manpulation: START
    def transpose(self: list):
        """Return a new matrix transposed"""
        result = [list(i) for i in zip(*self.matrix)]
        return Matrix(result)


    def to_list(self):
        """Convert Matrix object to a list"""
        return self.matrix
    # Object Manpulation: END

def main():
    """The CLI for the module"""
    parser=argparse.ArgumentParser(
        description = 'Matrix Minuplation module to add, substract, multiply'
        'matrices.',
        epilog = 'Usage: .. -ma "[[1, 2, 3], [4, 5, 6]]" -op "+" -mb'
        ' "[[7, 8, 9], [10, 11, 12]]"')

    parser.add_argument('-v', '--version',
        action="version",
        version=__version__,
    )

    parser.add_argument('-s', '--size',
        type=json.loads,
        metavar="",
        help='Size of A Matrix'
    )

    parser.add_argument('-t', '--transpose',
        type=json.loads,
        metavar="",
        help='Transpose of A Matrix (-t "[[1, 2, 3], [4, 5, 6]]")'
    )

    parser.add_argument('-ma', '--matrixa',
        type=json.loads,
        metavar="",
        help='Matrix A (.. -ma "[[1, 2, 3], [4, 5, 6]]")'
    )

    parser.add_argument('-op', '--operator',
        type=str,
        metavar="",
        help='Operator (.. -op "+", "-", "*")'
    )

    parser.add_argument('-mb', '--matrixb',
        type=json.loads,
        metavar="",
        help='Matrix B (.. -mb "[[1, 2, 3], [4, 5, 6]]")'
    )

    parser.add_argument('-i', '--int',
        type=int,
        metavar="",
        help='Integer (.. -i 69)'
    )

    args = parser.parse_args()

    if args.size:
        return Matrix(args.size)

    elif args.transpose:
        return Matrix(args.transpose).transpose()

    elif args.matrixa:
        if args.operator == '+':
            print(Matrix(args.matrixa) + Matrix(args.matrixb)) 
            if args.matrixb else 
            print(Matrix(args.matrixa) + args.int)
        elif args.operator == '-':
            print(Matrix(args.matrixa) - Matrix(args.matrixb)) 
            if args.matrixb else 
            print(Matrix(args.matrixa) - args.int)
        elif args.operator == '*':
            print(Matrix(args.matrixa) * Matrix(args.matrixb)) 
            if args.matrixb else 
            print(Matrix(args.matrixa) * args.int)
        else:
            raise SyntaxError('The avillable operations are +, -, *')
    else:
        return parser.print_help()


if __name__ == '__main__':
    main()

1 ответ
1

Интересный проект, написание его на python имеет некоторые ограничения. Numpy в основном написан на C, и для этого есть веские причины. Управление памятью намного лучше, и большинство процедур манипулирования массивами можно выполнять на месте, а не копировать туда и обратно. AFAIK, в Python нет эквивалента для чистых c-массивов. А tuple вероятно, самое близкое, что вы можете найти.

Конструктор является недействительной функцией и имеет один аргумент. Вы указываете тип возвращаемого значения, но, НАСКОЛЬКО, __init__ функция должна быть пустым, поэтому в действительности нет смысла указывать ее. Напротив, аргумент не имеет подсказки типа. Матрица является 2-мерный массив, который должен быть списком или любым другим типом последовательности. Вы строите логику, которая предполагает, что self.matrix это list введите другие методы. Значит, должно быть наоборот — указать тип аргумента, но не возвращаемый тип.

С использованием print внутри __str__ выглядит немного странно, потому что python будет искать этот метод при вызове print(self).

В __repr__ вы могли бы использовать ф-струны для форматирования строки. Но это может быть дело вкуса.

В __pos__ на самом деле ничего не делает из того, что я вижу. Если вы хотите скопировать список, для этого есть встроенные методы.

__neg__ Просто делает унарный - оператор для всех элементов в self.matrix. Вы могли бы установить result при выполнении:

result = [[-x for x in y] for y in self.matrix]

Бинарные операции наиболее интересны. Как правило, я стараюсь избегать построения какой-либо логики на основе статического вывода типов, особенно при написании программ на высоком уровне. С учетом сказанного, есть разумные способы сделать это в python — взгляните на functools.singledispatch. Но я часто чувствую, что проверка статического типа просто сворачивает стек вызовов, что затрудняет обратную трассировку для целей отладки. Я предпочитаю использовать полиморфизм и переносить любой тип вывода во внутренний диспетчер. Единственное отличие между сложением двух списков (или матриц) и добавлением list с int это то list необходимо использовать индексацию. Вы можете убедиться, что аргумент other к __add__ это Matrix введите и всегда используйте такую ​​индексацию:

self.matrix[m,j] + other.matrix[m,j]

Это означает, что матрица также может быть целым числом, и конструктор примет list или же int введите в качестве аргумента. Чтобы обрабатывать случаи, когда вы индексируете целое число (что на самом деле не имеет смысла), вам нужно перегрузить __getitem__ метод для Matrix базовый класс. Это может выглядеть примерно так:

def __getitem__(self, idx):
    i, j = idx
    return isinstance(self.matrix, int) and self.matrix or self.matrix[i][j]

Но могут быть лучшие способы сделать это. Например, вы можете использовать собственный итератор.

В заключение, вычитание может быть реализовано как композиция умножения и сложения, потому что a - b <=> a + (b * -1). Он немного менее эффективен, но чтобы делать какие-то выводы, придется посмотреть на сборку.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *