list of objects containing lists of objects - python

I'm trying to create a list of objects (Elements), where each element contains a list of objects (GaussPoints). The number of GaussPoints per Element can be variable, although in the example below each element has only one GaussPoint for simplicity.
class Element:
gp = []
class GaussPoint:
argument = 0
k = 0
elements = []
for ele in xrange(4):
k = k+1
anEle = Element()
aGP = GaussPoint()
aGP.argument = k
print k
elements.append(anEle)
elements[ele].gp.append(aGP)
print "testing the data structure:"
for ele in xrange(4):
print elements[ele].gp[0].argument
The output is:
1
2
3
4
testing the data structure:
1
1
1
1
What I'm expecting would be:
1
2
3
4
testing the data structure:
1
2
3
4
Somehow the code doesn't create a new list of GaussPoints for each Element, but appends to the same one.
I'm obviously misunderstanding something quite fundamental about Python. Could anyone please help?

You need to make Element.gp an instance variable, like so:
class Element:
def __init__(self):
self.gp = []
Currently, gp is a member of the class itself and is in effect shared by all instances of the class. You change it in one, and it appears to change in all.
It is arguably good style to make the same change to GaussPoint.argument:
class GaussPoint:
def __init__(self):
self.argument = 0
Finally, I'd recommend reading the tutorial on classes.

You're initializing Element.gp as a class variable, rather than an instance variable, so it's shared across all your Element objects. You end up with 1 gp being an array of length 4, rather than 4 gps of length 1.
Instead, initialize it in an __init__ method like so:
class Element:
def __init__(self):
self.gp = []
You're making argument a class variable, but since you reassign it for each instance, rather than modify it as you do element, it ends up holding the value you'd expect.

Related

Python class method appends item several times in .ipynb environment

Imagine we have a Class of foo with a certain structure defined in one cell:
class foo:
def __init__(self, lst = []):
self.lst = lst
def appendlst(self, item):
self.lst.append(item)
def display(self):
for item in self.lst:
print(item)
And in the next cell we have an object initialization with method calls:
dummy = foo()
dummy.appendlst(0)
dummy.appendlst(5)
dummy.display()
When I run the cell one time, output is as expected to be:
0
5
But when I run the cell twice, I expect the object to be reinitialized and to print the previous output once, instead I have:
0
5
0
5
On the opposite, if I reinitialize in the cell in the following manner:
dummy = foo()
dummy.appendlst(0)
dummy.appendlst(5)
dummy = foo()
dummy.appendlst(0)
dummy.appendlst(5)
dummy.display()
Output is as expected to be:
0
5
Also, changing the self.lst = lst to be self.lst = [], resolves the first case, however, making class incapable for initializing with anything, except the null list.
What is the source of that strange behavior with list being not null, when running the cell twice?
This is because you have used the anti-pattern of using a mutable default value as an argument.
The fix is
def __init__(self, lst = None):
if lst is None:
lst = []
self.lst = lst
In short, by setting the default value to [] you are creating a new list and setting the default value to a reference to that list. So every time you call that init function, instead of creating a new list as you were expecting, instead you are getting a reference to a list that is now shared between all your foo objects. (Incidentally by pep8 naming conventions you should have called it Foo).
This is compounded by Jupyter's design which encourages bad practices like running cells out of sequence leading to unreproducible program states. i.e. be very careful about running or rerunning any cell in Jupyter without running all the cells in order from the start.

Append/Write with values of an instance

So I'm new to working with classes in python, but this project Euler(q 81) I have is done using classes just to be a bit more tricky? I guess?
I can get the values of a (2n+1 * 2n+1) grid, but I can't work with them to append to another list or even write to a file.
def minSum(matrix):
file = open("pleasedeargodwork.txt", "w")
newList = []
for x in maxtrix.grids:
for y in x:
newList.append(y)
print y,
file.write(y,)
print newList
>>> 1 7 2 5 6 2 9 2 5
>>> TypeError: must be string or read-only character buffer, not instance
>>> <matrix.Supplies instance at 0x0240E9B8>
^^ I would like this last line to give me the values, rather than the instance, but how?
My matrix class looks something like this:
class Matrix:
def __init__(self, grids):
self.size = len(grids) / 2
self.grids = [[Supplies(s) for s in row] for row in grids]
class Supplies:
def __init__(self, supp):
if isinstance(supp, list):
self.value = supp[0]
"Matrix" is the class name, "matrix" is the filename and an argument given to my class minSum to be able to access this file.
If you need to see any more of the matrix file let me know.
Thanks.
Looks like you have another error when you try to write an instance to the text file but here's a way to print the values instead of the instance:
The __repr__ method lets you define what the object looks like when it is printed.
Add a __repr__ method to the Supplies class like so:
class Supplies:
def __init__(self, supp):
if isinstance(supp, list):
self.value = supp[0]
def __repr__(self):
return str(self.value)
Whenever you print a Supplies instance, Python will print its value attribute instead. Note that value is not guaranteed to be defined in the Supplies class so you might want to either initialize it or check before you attempt to convert it to a string in the __repr__ method.
Edit
If you want newList to contain the values of each Supplies instance you could just append the value instead of the instance:
newList.append(y.value)
instead of:
newList.append(y)

How to build a new list from a list in Python?

I have a python list that contains a set of objects (a class that has it's own
properties and functions and variables). I would like to extract some of the list elements and create a new list.
My question is: Are the new list elments going to remain same or they will be considered new instances of the class when I extract them from list?
For example: List layer = [myclass1, myclass2, myclass3]
I want new list layernew = [myclass1] or any such combination. How can I extract the items. When I print directly layer and new layer (by using newlayer=layer[0:1]) I see that it gives me the same instance reference of the object.
Are the new list elments going to remain same or they will be considered new instances of the class when I extract them from list.
They'll be the same. Demo:
class Widget:
def __init__(self, value):
self.value = value
a = [Widget(4), Widget(8), Widget(15)]
b = a[0:1]
print a[0] is b[0]
The output is True, so a[0] and b[0] are references to the same object.
One way to change this behavior is to use the copy module's deepcopy method. This will attempt to duplicate the object you give it and return a referentially distinct object with identical values.
import copy
class Widget:
def __init__(self, value):
self.value = value
a = [Widget(4), Widget(8), Widget(15)]
b = copy.deepcopy(a[0:1])
print a[0] is b[0]
#result: False
They are exactly the same objects. You can do identity tests with is in python.
layernew[0] is layer[0] # True

What does this class do?-Python

I am trying to understand what does this class
class undo:
def __init__(self, ss):
self.ss = ss
In my head it should simply put the value of ss uniquely in the variables i decide to use,but when I'm using it it rewrites all the variables, as if it was shared.
sum_expenses[100][0] = 100
sum_expenses[99][2] = 99
s = 1
a = [0 for i in range(100)]
a[s] = undo(sum_expenses)
output(a[1].ss)
sum_expenses[100][0] = 0
b = undo(sum_expenses)
print " "
print b.ss
print " "
sum_expenses[99][2] = 1
a[2] = undo(sum_expenses)
print a[2].ss
I do not understand why it overwrites all the variables with the current values of sum_expense, when I try to put it individually so that I can use the past values of sum_expenses.
Thank you and have a good day!
It happens because you're giving __init__ a reference to the list. If you change the list somewhere else, the reference in .ss still points to the same list, so the changes are visible there, too.
You could copy the list, since it's 2D try deepcopy.
Everytime you call this function it overrides reference to thr whole array ss, changing it to the new one you just passed to the constructor.
You can also create this array within the class and pass indexes and value to it like so:
undo.add(index1,index2,value)
Or you can make another variable ss_old and have make the function return that variable before you set it to a new value.

Store and Retrieve Objects from Python Lists

I am new to python and have a difficulty getting an object to be stored and access in an array or a list in Python.
I've tried doing something like this:
class NodeInfo:
def __init__(self, left, value, right):
self.l = left
self.r = right
self.v = value
tree[0] = NodeInfo(0,1,2)
tree[0].l = 5
tree[0].r = 6
tree[0].v = 7
When I try to assign values to or try to read from the variable, I get the following error:
tree[0] = NodeInfo(0,1,2)
NameError: name 'tree' is not defined
What am I doing wrong, or is there a different way to assign and read objects from arrays or lists in Python.
You need to create list first and use append method to add an element to the end of it.
tree = []
tree.append(NodeInfo(0,1,2))
# or
tree = [NodeInfo(0,1,2)]

Categories

Resources