Fairly new to python, very new to python classes. Question is a bit involved. Most appreciative of your patience:
I have a class “Star”. It is simple. Attributes x, v, and mass. Another class, Galaxy, has an attribute “stars” which is just a list of star objects:
class Galaxy:
numstars=0.
stars=[]
def __init__(self,numin,xes,vees,masses):
self.numstars=numin
for n in range(numin):
self.stars.append(Star(xes[n],vees[n],masses[n]))
Galaxy also has an attribute function called time_stepper. Suffice it to say time_stepper just updates all the elements of “stars” and then returns “stars”:
def time_stepper(self,dt):
self.velstep(dt)
self.xstep(dt)
self.velstep(dt)
return(self.stars)
Now, I’m trying to drive this thing and store the various updates of “stars” in a list called “history”:
gal=Galaxy(#stuff#)
history=[]
for n in range(100):
history.append(gal.time_stepper(.1))
Finally, my question: In each iteration of this loop, the new element of “stars” is added to “history”, but … and here it is … all the previous elements of history are over-written and given the same values as the newest element of history! So what is going on? I’ve run into things about python lists that I didn’t understand before, but I thought I finally had it nailed down. Apparently not. Thanks for your help.
Addendum:
Thanks to everyone for your help. Didn’t expect that many helpful replies and especially so soon. My problem was that I was assuming these two pieces of code were essentially the same. First:
>>> a=[]
>>> b=[1,2,3]
>>> a.append(b)
>>> b=[4,5,6]
>>> a.append(b)
>>> a
[[1, 2, 3], [4, 5, 6]]
Second:
>>> a=[]
>>> b=[1,2,3]
>>> a.append(b)
>>> b[:]=(4,5,6)
>>> b
[4, 5, 6]
>>> a.append(b)
>>> a
[[4, 5, 6], [4, 5, 6]]
And whoops! They aren’t. So in code 1, I guess, b is “re-pointed” to a completely new memory location while a[0] continues to point the the old b. In the second, the memory at b is “edited” and a[0] is still pointing to that location. After the append, a[1] is also pointing to that location. Do I have it now?
I’m very new to python and am still figuring out the “pythonic” philosophy. But to me, having a reassignment of pointers done straightforwardly, but a “deep copy” done in a more complicated way is sort of backwards from the way I usually want to do things. Can anyone enlighten me? Thanks again.
I think it is worth noting that if you have more than 1 galaxy, they’ll share the same stars. This could lead you to believe that you’re overwriting your stars when you really aren’t…
I’m guessing you would probably be better with an
__init__that looks like:Here I’ve shifted the class attribute
starsto be an instance attribute. Now each instance will have it’s ownstarslist instead of sharing onestarslist with all of the other galaxies in the universe.As others have noted, your
historylist is going to suffer from a similar problem (You have multiple references to the same list). However, the fix really depends on what you do inself.velstepandself.xstep. If you modify theStarobjects in place, then a simple (shallow) list copy won’t do you any good (e.g.gal.time_stepper(0.1)[:]). (You’ll create a new list, but it will hold the same stars which are constantly being updated). In that case, you’ll wantcopy.deepcopywhen you append to your history list: