Nested list object does not support indexing - python

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).

Related

Creating and declaring list in class

I am trying to create a list within a class and then declaring elements in that list.
I don't even know if this is the right way for python. I have some java background.
I didn't add 10 elements in the list manually because I feel creating a dynamic list will be more useful.
class Levels:
def __init__(self):
self.type = "A"
self.size = [] # trying to create a list for 10 elements.
self.makespace(self.size) # running this method to create 10 spaces and then declare them.
def makespace(self, size):
for i in range(0,10):
if(size[i] == None):
size[i] = "free"
print(i)
else:
print("Didn't work")
print(i)
test = Levels()
Your problem lies in here.
if(size[i] == None):
size[i] = "free"
print(i)
At this moment, size is empty, it doesn't contain any elements so why are you
checking size[i] == None?
You probably think that a python list behaves like an array in Java where it initializes everything with null? Even though here you are not declaring the size of the list inside the init constructor, so I'm curious how you thought of that.
Your code should look like this:
class Levels:
def __init__(self):
self.type = "A"
self.size = [] # trying to create a list for 10 elements.
self.makespace(self.size) # running this method to create 10 spaces and then declare them.
def makespace(self, size):
#This will fill the emty list with 10 None(null) values
for i in range(0,10):
size.append(None)
test = Levels()
Also a bonus:
class Levels:
def __init__(self):
self.type = "A"
self.size = []
#Notice that I'm not passing self as an argument when I call makespace()
self.makespace()
def makespace(self):
#This will fill the emty list with 10 None(null) values
for i in range(0,10):
self.size.append(None)
test = Levels()
Self is the this keyword in python, the difference is that in Python you need to pass it as an argument only when declaring methods, not when you call them and also you can name it whatever you want!
Hope this helps!
This code won't work because size[i] does not exist. Instead use size.append(...). Also you should pass a number to makespace so that you can make space for an arbitrary number of items.
class Levels:
def __init__(self, kind='A', size=10):
self.kind = kind
self.size = [ 0 for _ in range(10) ]
These slight changes make your code more robust and more pythonic.
First but the least important is that type is a builtin-method (also a class and also a type) so kind is often substituted.
Second You can pass default arguments to the constructor (or any function) as you should generally avoid having constants inside functions like that. Here you can arbitrarily set a Level's kind, as well as the initial space required.
Third Using list-comprehension you can create a list of arbitrary size (or elements). The syntax is
[ expression for args in iterable ]
which allows for any expression to be generated based on arguments passed from an iterable. Read more about list comprehension and other datastructure here.
As for your makespace you shouldnt really need it, however you could change the implementation so you can allocate more space (using self.size.append(...)) or overwriting currently used space.
Best of luck!
If you want to have 10 free spaces in the list upon initializing, change
self.size = []
to
self.size = [“free”] * 10
If you want to start with an empty list and add 10 free spaces in your makespace loop, simply use
self.size.append(“free”)
Also, you really don’t need to pass size to makespace. Since you’re already passing self, I would just reference self.size from inside the makespace function.

Quick way to convert all instance variables in a class, to a list (Python)

I have created a class with around 100+ instance variables (as it will be used in a function to do something else).
Is there a way to translate all the instance variables; into an array list. Without manually appending each instance variable.
For instance:
class CreateHouse(object):
self.name = "Foobar"
self.title = "FooBarTest"
self.value = "FooBarValue"
# ...
# ...
# (100 more instance variables)
Is there a quicker way to append all these items to a list:
Quicker than:
theList = []
theList.append(self.name)
theList.append(self.title)
theList.append(self.value)
# ... (x100 elements)
The list would be used to perform another task, in another class/method.
The only solution (without totally rethinking your whole design - which FWIW might be an option to consider, cf my comments on your question) is to have a list of the attribute names (in the order you want them in the final list) and use getattr
class MonstruousGodClass(object):
_fields_list = ["name", "title", "value", ] #etc...
def as_list(self):
return [getattr(self, fieldname) for fieldname in self._fields_list]
Now since, as I mentionned in a comment, a list is NOT the right datatype here (from a semantical POV at least), you may want to use a dict instead - which makes the code much simpler:
import copy
def as_dict(self):
# we return a deepcopy to avoid unexpected side-effects
return copy.deepcopy(self.__dict__)

How to access the __setitem__ definition | Syntax & Examples?

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.

Why does .append() not work on this list?

I have an object scene which is an instance of class Scene and has a list children which returns:
[<pythreejs.pythreejs.Mesh object at 0x000000002E836A90>, <pythreejs.pythreejs.SurfaceGrid object at 0x000000002DBF9F60>, <pythreejs.pythreejs.Mesh object at 0x000000002E8362E8>, <pythreejs.pythreejs.AmbientLight object at 0x000000002E8366D8>, <pythreejs.pythreejs.DirectionalLight object at 0x000000002E836630>]
If i want to update this list with a point which has type:
<class 'pythreejs.pythreejs.Mesh'>
I need to execute:
scene.children = list(scene.children) + [point]
Usually, I would execute:
scene.children.append(point)
However, while these two approaches both append point, only the first actually updates the list and produce the expected output (that is; voxels on a grid). Why?
The full code can be found here.
I am guessing your issue is due to children being a property (or other descriptor) rather than a simple attribute of the Scene instance you're interacting with. You can get a list of the children, or assign a new list of children to the attribute, but the lists you're dealing with are not really how the class keeps track of its children internally. If you modify the list you get from scene.children, the modifications are not reflected in the class.
One way to test this would be to save the list from scene.children several times in different variables and see if they are all the same list or not. Try:
a = scene.children
b = scene.children
c = scene.children
print(id(a), id(b), id(c))
I suspect you'll get different ids for each list.
Here's a class that demonstrates the same issue you are seeing:
class Test(object):
def __init__(self, values=()):
self._values = list(values)
#property
def values(self):
return list(self._values)
#values.setter
def values(self, new_values):
self._values = list(new_values)
Each time you check the values property, you'll get a new (copied) list.
I don't think there's a fix that is fundamentally different than what you've found to work. You might streamline things a little by by using:
scene.children += [point]
Because of how the += operator in Python works, this extends the list and then reassigns it back to scene.children (a += b is equivalent to a = a.__iadd__(b) if the __iadd__ method exists).
Per this issue, it turns out this is a traitlets issue. Modifying elements of self.children does not trigger an event notification unless a new list is defined.

How do I downcast in python

I have two classes - one which inherits from the other. I want to know how to cast to (or create a new variable of) the sub class. I have searched around a bit and mostly 'downcasting' like this seems to be frowned upon, and there are some slightly dodgy workarounds like setting instance.class - though this doesn't seem like a nice way to go.
eg.
http://www.gossamer-threads.com/lists/python/python/871571
http://code.activestate.com/lists/python-list/311043/
sub question - is downcasting really that bad? If so why?
I have simplified code example below - basically i have some code that creates a Peak object after having done some analysis of x, y data. outside this code I know that the data is 'PSD' data power spectral density - so it has some extra attributes. How do i down cast from Peak, to Psd_Peak?
"""
Two classes
"""
import numpy as np
class Peak(object) :
"""
Object for holding information about a peak
"""
def __init__(self,
index,
xlowerbound = None,
xupperbound = None,
xvalue= None,
yvalue= None
):
self.index = index # peak index is index of x and y value in psd_array
self.xlowerbound = xlowerbound
self.xupperbound = xupperbound
self.xvalue = xvalue
self.yvalue = yvalue
class Psd_Peak(Peak) :
"""
Object for holding information about a peak in psd spectrum
Holds a few other values over and above the Peak object.
"""
def __init__(self,
index,
xlowerbound = None,
xupperbound = None,
xvalue= None,
yvalue= None,
depth = None,
ampest = None
):
super(Psd_Peak, self).__init__(index,
xlowerbound,
xupperbound,
xvalue,
yvalue)
self.depth = depth
self.ampest = ampest
self.depthresidual = None
self.depthrsquared = None
def peakfind(xdata,ydata) :
'''
Does some stuff.... returns a peak.
'''
return Peak(1,
0,
1,
.5,
10)
# Find a peak in the data.
p = peakfind(np.random.rand(10),np.random.rand(10))
# Actually the data i used was PSD -
# so I want to add some more values tot he object
p_psd = ????????????
edit
Thanks for the contributions.... I'm afraid I was feeling rather downcast(geddit?) since the answers thus far seem to suggest I spend time hard coding converters from one class type to another. I have come up with a more automatic way of doing this - basically looping through the attributes of the class and transfering them one to another. how does this smell to people - is it a reasonable thing to do - or does it spell trouble ahead?
def downcast_convert(ancestor, descendent):
"""
automatic downcast conversion.....
(NOTE - not type-safe -
if ancestor isn't a super class of descendent, it may well break)
"""
for name, value in vars(ancestor).iteritems():
#print "setting descendent", name, ": ", value, "ancestor", name
setattr(descendent, name, value)
return descendent
You don't actually "cast" objects in Python. Instead you generally convert them -- take the old object, create a new one, throw the old one away. For this to work, the class of the new object must be designed to take an instance of the old object in its __init__ method and do the appropriate thing (sometimes, if a class can accept more than one kind of object when creating it, it will have alternate constructors for that purpose).
You can indeed change the class of an instance by pointing its __class__ attribute to a different class, but that class may not work properly with the instance. Furthermore, this practice is IMHO a "smell" indicating that you should probably be taking a different approach.
In practice, you almost never need to worry about types in Python. (With obvious exceptions: for example, trying to add two objects. Even in such cases, the checks are as broad as possible; here, Python would check for a numeric type, or a type that can be converted to a number, rather than a specific type.) Thus it rarely matters what the actual class of an object is, as long as it has the attributes and methods that whatever code is using it needs.
See following example. Also, be sure to obey the LSP (Liskov Substitution Principle)
class ToBeCastedObj:
def __init__(self, *args, **kwargs):
pass # whatever you want to state
# original methods
# ...
class CastedObj(ToBeCastedObj):
def __init__(self, *args, **kwargs):
pass # whatever you want to state
#classmethod
def cast(cls, to_be_casted_obj):
casted_obj = cls()
casted_obj.__dict__ = to_be_casted_obj.__dict__
return casted_obj
# new methods you want to add
# ...
This isn't a downcasting problem (IMHO). peekfind() creates a Peak object - it can't be downcast because its not a Psd_Peak object - and later you want to create a Psd_Peak object from it. In something like C++, you'd likely rely on the default copy constructor - but that's not going to work, even in C++, because your Psd_Peak class requires more parameters in its constructor. In any case, python doesn't have a copy constructor, so you end up with the rather verbose (fred=fred, jane=jane) stuff.
A good solution may be to create an object factory and pass the type of Peak object you want to peekfind() and let it create the right one for you.
def peak_factory(peak_type, index, *args, **kw):
"""Create Peak objects
peak_type Type of peak object wanted
(you could list types)
index index
(you could list params for the various types)
"""
# optionally sanity check parameters here
# create object of desired type and return
return peak_type(index, *args, **kw)
def peakfind(peak_type, xdata, ydata, **kw) :
# do some stuff...
return peak_factory(peak_type,
1,
0,
1,
.5,
10,
**kw)
# Find a peak in the data.
p = peakfind(Psd_Peak, np.random.rand(10), np.random.rand(10), depth=111, ampest=222)

Categories

Resources