This is the continuation of yesterday's practice, but now with Matrix operations.

from typing import List, Tuple, Callable
Vector = List[float]
Matrix = List[List[float]]
A = [[1, 2, 3],
     [4, 5, 6]]

B = [[1, 2],
     [3, 4],
     [5, 6]]

Shape

def shape(A: Matrix) -> Tuple[int, int]:
    number_of_rows = len(A)
    number_of_columns = len(A[0]) if A else 0
    return number_of_rows, number_of_columns

assert shape([[1, 2, 3],[4, 5, 6]]) == (2, 3)

Get rows and columns

def get_row(A: Matrix, i: int) -> Vector:
    return A[i]

def get_columns(A: Matrix, i: int) -> Vector:
    return [row[i] for row in A] 

assert get_columns([[1, 2, 3],[4, 5, 6]], 1) == [2, 5]

Create a new matrix from a function of $(i, j)$

def make_matrix(
    number_of_rows: int,
    number_of_columns: int,
    entry_function: Callable[[int, int], float]
) -> Matrix:
    return [
        [entry_function(i, j) for j in range(number_of_columns)]
        for i in range(number_of_rows)
    ]

assert make_matrix(3, 2, lambda i, j: i + j) == [[0, 1], [1, 2], [2, 3]]
def identity_matrix(n: int) -> Matrix:
    return make_matrix(n, n, lambda i, j: 1 if i == j else 0)

assert identity_matrix(3) == [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
identity_matrix(5)
[[1, 0, 0, 0, 0],
 [0, 1, 0, 0, 0],
 [0, 0, 1, 0, 0],
 [0, 0, 0, 1, 0],
 [0, 0, 0, 0, 1]]