Home > Back-end >  Is there a way to use __str__ in a class but returning a list?
Is there a way to use __str__ in a class but returning a list?

Time:02-20

When I call a class object, I want it to return a list or any other variable other than string

class Vec:
    def __init__(self, data) -> None:
        self.data = data #THIS IS A LIST

    def __str__(self) -> list:
        return self.data

a = Vec([0,1,2,3])
print(a) 

#TypeError: __str__ returned non-string (type list)

Instead I get a TypeError, if I set the "self.data" to a string it will work

Is there any Magic Method for this? Thanks in advance

here is the full code in case you need to see it all

note: I am not done finishing it yet and my problem is that for example if I want to multiply Mat with a Vec I would have to do Mat * Vec.data or Mat * Vec.T (T stands for transposed) which I dont like and I would just like to type Mat * Vec in case I dont need to transpose the vector. Sorry for my messy code and my bad explanation of my problem

class MatrixMatchError(Exception):
    def __init__(self):
        self.message = "Matrix sizes do not match"
        super().__init__(self.message)
    pass

class MatrixRowColError(Exception):
    def __init__(self):
        self.message = "The number of columns in the first matrix do not equal to the number of rows in the second."
        super().__init__(self.message)
    pass

class Vec:
    def __init__(self, x, identity=1) -> None:
        self.x = x
        self.data = self.Create(x, 1, identity)
        self.T = self.Transpose()

    def Create(self, x, y, identity) -> list:
        vec = []
        for i in range(y):
            vec.append([])
            for j in range(x):
                vec[i].append(identity)

        return vec

    def Transpose(self) -> list:
        vec = self.Create(len(self.data), len(self.data[0]), 0)
        for i in range(len(self.data)):
            for j in range(len(self.data[0])):
                vec[j][i] = self.data[i][j]

        return vec

class Mat:
    def __init__(self, x, y=0, identity=1) -> None:
        self.x = x
        if y == 0:
            self.y = x
        else:
            self.y = y

        self.data = self.Create(self.x, self.y, identity)
        self.T = self.Transpose()

    def __add__(self, data):
        if not self.CheckSize(data):
            raise MatrixMatchError

        mat3 = self.data.copy()
        for i in range(len(self.data)):
            for j in range(len(self.data[0])):
                mat3[i][j] = self.data[i][j]   data[i][j]

        return mat3

    def __sub__(self, data):
        if not self.CheckSize(data):
            raise MatrixMatchError

        mat3 = self.data.copy()
        for i in range(len(self.data)):
            for j in range(len(self.data[0])):
                mat3[i][j] = self.data[i][j] - data[i][j]

        return mat3

    def __mul__(self, data):
        if not self.CheckRowCol(data):
            raise MatrixRowColError

        mat3 = self.Create(len(data[0]), len(self.data),  0)

        for i in range(len(self.data)):
            for j in range(len(data[0])):
                for k in range(len(self.data[0])):
                    mat3[i][j]  = self.data[i][k] * data[k][j]

        return mat3

    def Create(self, x, y, identity) -> list:
        mat = []
        for i in range(y):
            mat.append([])
            for j in range(x):
                mat[i].append(0)

        if x == y:
            for i in range(x):
                mat[i][i] = identity

        return mat

    def Transpose(self) -> list:
        mat = self.Create(len(self.data), len(self.data[0]), 0)
        for i in range(len(self.data)):
            for j in range(len(self.data[0])):
                mat[j][i] = self.data[i][j]

        return mat

    def CheckSize(self, data):
        if len(self.data) != len(data) or len(self.data[0]) != len(data[0]):
            return False
        return True

    def CheckRowCol(self, data):
        if len(data) != len(self.data[0]):
            return False
        return True

    def Transform(self):
        pass

    def Scale(self):
        pass

    def Rotate(self):
        pass
        

mat1 = Mat(2)
mat1.data = [[4,5, 4],
            [9,3, 7],
            [7,1, 3]]

vec = Vec(3)
print(mat1 * vec.T)

CodePudding user response:

The builtin str() function will raise a TypeError if it calls your __str__ and gets something that isn't a string, and you won't convince it to do otherwise.

What you probably want is for Vec.__str__ to return the string representation of data, rather than data itself:

class Vec:
    def __init__(self, data: list) -> None:
        self.data = data

    def __str__(self) -> str:
        return str(self.data)

The __str__ method has no bearing on your other use case:

for example if I want to multiply Mat with a Vec I would have to do Mat * Vec.data or Mat * Vec.T (T stands for transposed) which I dont like and I would just like to type Mat * Vec in case I dont need to transpose the vector.

This should instead be addressed in Mat.__mul__ (which should operate on a Vec, not the underlying list) and Vec.T/Vec.Transpose (which should return a Vec, not a list):

class Vec:

    ...

    def Transpose(self) -> 'Vec':
        vec = self.Create(len(self.data), len(self.data[0]), 0)
        for i in range(len(self.data)):
            for j in range(len(self.data[0])):
                vec[j][i] = self.data[i][j]
        return Vec(vec)


class Mat:
    ...

    def __mul__(self, vec: Vec) -> list[list[int]]:
        if not self.CheckRowCol(vec.data):
            raise MatrixRowColError

        mat3 = self.Create(len(vec.data[0]), len(self.data),  0)

        for i in range(len(self.data)):
            for j in range(len(vec.data[0])):
                for k in range(len(self.data[0])):
                    mat3[i][j]  = self.data[i][k] * vec.data[k][j]

        return mat3

Now you can indeed do Mat * Vec or Mat * Vec.T and it will do the right thing.

Note that it would probably be better for Mat.__mul__ to return a Mat, but you haven't provided a constructor that makes it easy for me to implement that part -- nevertheless I hope this gives you a better way to think about your class interfaces and how they can make it easier (or harder) to use the class! Ideally the user of the class shouldn't even be able to access the internals, much less be forced to.

Unrelated to this, I'd suggest spending a bit of time learning about Python's list comprehension syntax, because it'll save you a lot of list allocation boilerplate (i.e. your various Create methods). :)

  • Related