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 aVec
I would have to doMat * Vec.data
orMat * Vec.T
(T stands for transposed) which I dont like and I would just like to typeMat * 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). :)