How to access the __setitem__ definition | Syntax & Examples? - python

I have a matrix class similar to here:
class Matrix(object):
def __init__(self, m, n, init=True):
if init:
self.rows = [[0]*n for x in range(m)]
else:
self.rows = []
self.m = m
self.n = n
def __setitem__(self, idx, item):
self.rows[idx] = item
print("HERE")
...
I would like to set an element to a value of 2:
my_mat = 0000 my_mat = 0000
0000 -> 0200
0000 0000
0000 0000
and in my main() I set the element like this:
from matrix import Matrix
def main():
# Create matrix
my_mat = Matrix(4,3)
# Set element
my_mat[1][1] = 2
print(my_mat)
if __name__ == "__main__":
main()
The __setitem__ definition requires 3 args, (one which is self, that is provided automatically). So, id and item are needed. I have tried a number of different combinations to set an element of the matrix. When I try to set the element (above), "HERE" isn't printed. It appears that I'm not accessing the __setitem__ method at all.
How do I set an element using the __setitem__ def? Syntax and examples would be appreciated.
I have tried variations like:
my_mat(1,1) = 2
my_mat(1,1,2)
my_mat([1,1],2)
.... but all fail.

The __setitem__() method in the Matrix class in the linked ActiveState recipe is for accessing entire rows (likewise for the __getitem__() method).
So, to invoke its __setitem__() method would require something like the following, which first retrieves the entire row, changes a single element of it, and then stores the entire row back into the matrix at the same row index:
def main():
# Create matrix
my_mat = Matrix(4,3)
# Set single element, logically equivalent to my_mat[1][1] = 2
row = my_mat[1] # Uses Matrix.__getitem__()
row[1] = 2 # Change single element of row.
my_mat[1] = row # Uses Matrix.__setitem__() to replace row
print(my_mat)
As written, the recipe does not provide a way to do this all with a single line of code. If it's going to be done frequently, you're will need to modify the definition of the the Matrix class itself.
If you can't figure out how to do that, find a different recipe that supports it (or ask another question here. I suppose).
Parting thought: I suggest you look into getting and installing the numpy add-on module (it's not built-in to Python). It also has a number of other useful features and is very fast.
Minor Update
For fun, I figured-out a very hacky and unreadable way to do it in a single line of code:
# Logically equivalent to my_mat[1][1] = 2
my_mat[1] = (lambda r, i, v: (r.__setitem__(i, v), r)[1])(my_mat[1], 1, 2)
This defines an inline anonymous function that accepts 3 arguments, r, i, and v:
r: row to modify
i: index of item within the row
v: new value for item
This function calls row r's __setitem__() method (each row being a sublist in the recipe) to modify the item's value within the row, and then returns the modified row, which is assigned back to same row position it was retrieved from (overwriting its original value).
The Matrix class' __setitem__() will get called to perform the final step of replacing the entire row.
(I'm not recommending you do it this way...but hopefully this will give you the an idea about what a new method to the class would have o o since that would need to do something similar.)

I think in this situation you would want to define __getitem__ and have it return the proper row:
def __getitem__(self, item):
if isinstance(item, int):
return self.rows[item]
return super().__getitem__(item)
Or, alternatively, define a more complex __setitem__ as described here
Also, unrelated to your question, note that the else branch in your constructor will initialize rows as a one-dimensional list instead of two-dimensional, not sure if you meant to do that.

Related

I have a trouble with python objects. It was the same object, but now it different

You can see, that i've created two instances of class A. So, a.d (dict of first instance) and b.d (dict of second instance) have to be different! But they are not, we clearly can see that a.d == b.d = True. Okay, so, it should mean that if i will modivy a.d, then b.d will be modifyed too, right? No. Now they are diffrent. And you will say, okay, they just compare together by value, not by it's link value. But i have another trouble with code:
class cluster(object):
def __init__(self, id, points):
self.id = id
self.points = points
self.distances = dict() # maps ["other_cluster"] = distance_to_other_cluster
def set_distance_to_cluster(self, other_cluster, distance):
"""Sets distance to itself and to other cluster"""
assert self.distances != other_cluster.distances
self.distances[other_cluster] = distance
other_cluster.distances[self] = distance
and at the end i'm getting the same "distances" dict object for all clusters. Am i do sth wrong? :'(
Dictionaries are not regular data types, you have to be very careful when working with them.
a = [5]
b = a
b.append(3)
You'd think that with this code that a=[5] and b=[5,3] but in reality they BOTH equal [5, 3].
And by the way when you assigned the value a.d["A"] = 1 you turned the ARRAY into a DICTIONARY, dictionaries don't have the problem above so it didn't come up again.
Solution is to use dictionaries from the start since it suit your data type anyways.

Python return value prints only one value from the object instead of all the values

I have a Python file with two class A and B, and I am inheriting temp from class A to B. In my class A function temp, I am getting the id value from an XML file which has values like this,
[1,2,3,4,5]
My Class A with temp function
class A:
def temp(self):
id = []
for child in root:
temp_id=child.get('id')
id.append(temp_id)
return id
and I am getting the function value in class B,
class B (A):
fun=A
value = fun.temp()
for x in value:
return x
While printing the return value instead of printing all the values, I am getting only the first value
[1,1,1,1,1]
Can some one please let me know on how to solve this, thanks.
Standard functions can only return a single value. After that, execution continues from where that function was called. So, you're trying to loop through all the values, but return upon seeing the first value, then the function exits and the loop is broken.
In your case, you could simply return fun.temp(). Or, if you want to process each value, then return the new list, you could run something like:
new_list = []
value = fun.temp()
for x in value:
# do something with x here
new_list.append(x)
return new_list
Also, it appears you may be a bit confused about how inheritance works. DigitalOcean has a great article on it, if you want to take a look.

How can I use the value of a variable in the name of another without using a dictionary in python?

The answer people have already given for using the value of a variable in the assignment of another is:
to create a dictionary and,
use dict[oldVariable] instead of defining a new one
I don't think that works in the context of what I'm trying to do...
I'm trying to define a class for a vector which would take a list as an input and assign an entry in the vector for each element of the list.
My code looks something like this right now:
class vector:
def __init__(self, entries):
for dim in range(len(entries)):
for entry in entries:
self.dim = entry #here I want to assign self.1, self.2, etc all the way to however
#many elements are in entries, but I can't replace self.dim with
# dict[dim]
def __str__(self):
string = []
for entry in range(1,4):
string.append(self.entry)
print(string)
How do I do this?
What you are doing here is a bit strange, since you are using a variable named "dim" in a for, but you do not do anything with that variable. It looks like you want to use a class as if it was an array... why don't you define an array within the class and access it from the outside with the index? v.elements[1] ... and so on?
Example:
class Vector:
def __init__(self, entries):
self.elements = []
for e in entries:
self.elements.append(self.process(e))
def __str__(self):
buff = ''
for e in self.elements:
buff += str(e)
return buff
Hope this helps.
If I'm reading your question correctly, I think you're looking for the setattr function (https://docs.python.org/2/library/functions.html#setattr).
If you wanted to name the fields with a particular string value, you could just do this:
class vector:
def __init__(self, entries):
for dim in range(len(entries)):
for entry in entries:
#self.dim = entry
setattr(self, str(dict[dim]), dim)
That will result in your object self having attributes named with whatever the values of dict[dim] are and values equal to the dim.
That being said, be aware that an integer value is generally a poor attribute name. You won't be able to do print obj.1 without error. You'd have to do getattr(obj,'1').
I agree with #Ricardo that you are going about this strangely and you should probably rethink how you're structuring this class, but I wanted to directly answer the question in case others land here looking for how to do dynamic naming.

Nested list object does not support indexing

I have a nested list, named env, created in the constructor and another method to populate an element of the grid defined as below:
class Environment(object):
def __init__(self,rowCount,columnCount):
env = [[ None for i in range(columnCount)] for j in range(rowCount) ]
return env
def addElement(self, row, column):
self[row][column] = 0
Later in the code I create an instance of Environment by running:
myEnv = createEnvironment(6,6)
Then I want to add an element to the environment by running:
myEnv.addElement(2,2)
So what I expected to happen was that I would receive a new Environment object as a 6x6 grid with a 0 in position 2,2 of the grid. But that did not work.
I have two errors:
I am unable to return anything other than None from the init method.
The main issue us when trying to execute addElement(2, 2) I get this error:
"TypeError: 'Environment' object does not support indexing.
I looked at the __getitem__ and __setitem__ methods but was unable to get them working over a multidimensional list. Is there a better data structure I should be using to create a grid?
The problem here is that you can't replace the object with __init__. You could subclass list and do something in __new__, probably, but that would be massive overkill, the better option is just to wrap the list:
class Environment(object):
def __init__(self, rows, columns):
self.env = [[None for column in range(columns)] for row in range(rows) ]
def addElement(self, row, column):
self.env[row][column] = 0
Note that it's a little odd you claim to be calling myEnv = createEnvironment(6,6) - using a function rather than the constructor is a little odd.
If you really want your object to act like the list, you can of course provide a load of extra wrapper functions like __getitem__/__setitem__. E.g:
def __getitem__(self, row, column):
return self.env[row][column]
Which would allow you to do some_environment[5, 6], for example. (You may rather return the column, that depends on your system and what works best for you).

Python OOP __Add__ matrices together (Looping Problem)

class Matrix:
def __init__(self, data):
self.data = data
def __repr__(self):
return repr(self.data)
def __add__(self, other):
data = []
for j in range(len(self.data)):
for k in range(len(self.data[0])):
data.append([self.data[k] + other.data[k]])
data.append([self.data[j] + other.data[j]])
data = []
return Matrix(data)
x = Matrix([[1,2,3],[2,3,4]])
y = Matrix([[10,10,10],[10,10,10]])
print(x + y,x + x + y)
I was able to get Matrices to add for 1 row by n columns, but when I tried to improve it for all n by n matrices by adding in a second loop I got this error.
Traceback (most recent call last):
line 24, in <module>
print(x + y,x + x + y)
line 15, in __add__
data.append([self.data[k] + other.data[k]])
IndexError: list index out of range
How about this:
class Matrix:
def __init__(self, data):
self.data = data
def __repr__(self):
return repr(self.data)
def __add__(self, other):
data = []
for j in range(len(self.data)):
data.append([])
for k in range(len(self.data[0])):
data[j].append(self.data[j][k] + other.data[j][k])
return Matrix(data)
Your code has a few problems... the first is basic logic of the addition algorithm
data.append([self.data[k] + other.data[k]])
this statement is highly suspect... data is a bidimensional matrix but here your are accessing it with a single index. data[k] is therefore a whole row and using + you are concatenating rows (probably not what you wanted, correct?). Probably the solution of highBandWidth is what you were looking for.
The second problem is more subtle, and is about the statement
self.data = data
This may be a problem because Python uses the so-called "reference semantic". Your matrix will use the passed data parameter for the content but without copying it. It will store a reference to the same data list object you passed to the constructor.
May be this is intentional but may be it's not... this is not clear. Is it ok for you that if you build two matrices from the same data and then change the content of a single element in the first also the content of the second changes? If this is not the case then you should copy the elements of data and not just assign the data member for example using
self.data = [row[:] for row in data]
or using copy.deepcopy from the standard copy module.
A third problem is that you are using just two spaces for indenting. This is not smart... when working in python you should use 4 spaces indenting and never use hard tabs chracters. Note that I said that doing this (using two spaces) is not smart, not that you are not smart so please don't take this personally (I even did the very same stupid error myself when starting with python). If you really want to be different then do so by writing amazing bug-free software in python and not by just using a bad indenting or choosing bad names for function or variables. Focus on higher-level beauty.
One last problem is that (once you really understand why your code didn't work) you should really read about python list comprehensions, a tool that can greatly simplify your code if used judiciously. You addition code could for example become
return Matrix([[a + b for a, b in zip(my_row, other_row)]
for my_row, other_row in zip(self.data, other.data)])
To a trained eye this is easier to read than your original code (and it's also faster).

Categories

Resources