Hello experienced python programmers! I come here in humble request of your guidance on my latest python endeavor.

I'm writing my first program to make heavy use of lists within lists, and i've run into a rather unexpected problem. just for practice, I'm writing a program that simulates a simple ecosystem with three animals: plants, prey and predators. The section of code I have here says that when a plant reaches a certain size (the size is specified in the list of values that defines the plant) it splits in two, and its offspring's attributes are modified slightly, like natural mutations in biology.

Nplants.append(plant)
        if plant[2]>plant[3]:#this path spawns another plant
            Nplants.append(plant)#the baby is spawned
            Nplants[-2][2]=Nplants[-2][2]/2#halves size of old plant
            Nplants[-1][2]=Nplants[-1][2]/2#halves size of new plant
            Nplants[-1][0]= Nplants[-1][0]+random.gauss(0.0,Nplants[-1][4])#changes x coordinate
            Nplants[-1][1]= Nplants[-1][1]+random.gauss(0.0,Nplants[-1][4])#changes y coordinate
            Nplants[-1][3]=Nplants[-1][3]+random.gauss(0,10)#changes split size
            Nplants[-1][4]=Nplants[-1][4]+random.gauss(0.0,0.5)#changes spawn radius
        else:#this path just grows the plant by ten size units
            Nplants[-1][2]=Nplants[-1][2]+10
        if Nplants[-1][2]<1:#this checks to see if the plant's size is less than one
            del Nplants[-1]#kills the plant

My problem is this: all modifications made to the new plant Nplants[-1] also apply to the old plant and vice versa. Why is this and how can I fix it?

Recommended Answers

All 4 Replies

Usually that means that two items point to the same memory address, but without knowing more about "plant" (whether it is a string, list, etc.), and the same for Nplant (a list or list of lists?), there is no way to tell. Insert this code after appending, which prints the id, and see if 2 or more id's are the same.

for pl in Nplants:
    print id(pl)

For more help, post a complete code snippet, that will run as is, and some sample data to use for appending.

Yeah, that's the problem; the ids in Nplants are enormously redundant.

Plant is a list of five values [x coordinate, y coordinate, size, &c.], and Nplants is a list of plants. I've attached the full code below and its riddled with errors, so any further help to clean it up would be greatly appreciated. Sorry for the long post.

'''
Created on Jan 31, 2011

@author: Jeremy Vonderfecht
'''
###Evolution program with constrained mutations###
#Plants have 5 stats: [x,y,size,split size, spawn radius]
#Prey have 9 stats: [x,y,size,split size, spawn radius, metabolism, Predator repulsion, prey repultion, plant attraction]
#Predators have 8 stats: [[x,y,size,split size, spawn radius, metabolism,Predator repultion, Prey attraction]

import random
import pylab
import math

Plants=[]
Preys=[]
Predators=[]

Nplants=[]
Npreys=[]
Npredators=[]


def ADD(number,Type,stats):
    while number > 0:
        Type.append(stats[:])
        Type[-1].insert(0,random.randrange(1,100))
        Type[-1].insert(0,random.randrange(1,100))
        number = number-1

ADD(20,Plants,[100,200])
ADD(10,Preys,[500,1000,5,5,5,5,5,5,5])
ADD(5,Predators,[1000,2000,5,10,5,5,5])

print(Plants)
print(Preys)
print(Predators)

iteration=1000

while iteration>0:
    
    ###PLANTS###
    for plant in Plants:
        Nplants.append(plant)
        if plant[2]>plant[3]:#this path spawns another plant
            Nplants.append(plant)#the baby is spawned
            Nplants[-2][2]=Nplants[-2][2]/2#halves size of old plant
            Nplants[-1][2]=Nplants[-1][2]/2#halves size of new plant
            Nplants[-1][0]= Nplants[-1][0]+random.gauss(0.0,Nplants[-1][4])#changes x coordinate
            Nplants[-1][1]= Nplants[-1][1]+random.gauss(0.0,Nplants[-1][4])#changes y coordinate
            Nplants[-1][3]=Nplants[-1][3]+random.gauss(0,10)#changes split size
            Nplants[-1][4]=Nplants[-1][4]+random.gauss(0.0,0.5)#changes spawn radius
        else:#this path just grows the plant again
            Nplants[-1][2]=Nplants[-1][2]+10
        if Nplants[-1][2]<1:
            del Nplants[-1]
        
    ###END PLANTS###
            
    ###PREYS###
    for prey in Preys:
        Npreys.append(prey)
        sumx=0
        sumy=0
       
        ##PREY MOVEMENT##
        #CHASING PLANTS#
        for plant in Plants:
            difx=plant[0]-prey[0]
            dify=plant[1]-prey[1]
            sumx = sumx + float(prey[8]*difx)/math.sqrt(difx**2+dify**2)
            sumy = sumy + float(prey[8]*dify)/math.sqrt(difx**2+dify**2)
        #FLEEING PREDATORS
        for predator in Predators:
            difx=prey[0]-predator[0]
            dify=prey[1]-predator[1]
            sumx = sumx + (prey[6]*difx)/math.sqrt(difx**2+dify**2)
            sumy = sumy + (prey[6]*dify)/math.sqrt(difx**2+dify**2)
        #AVOIDING PREY
        for oprey in Preys:
            if Preys.index(oprey)==Preys.index(prey): #this makes sure it doesn't affect itself.
                donothing=1
            else:
                difx=prey[0]-oprey[0]
                dify=prey[1]-oprey[1]
                sumx = sumx + (prey[7]*difx)/math.sqrt(difx**2+dify**2)
                sumy = sumy + (prey[7]*dify)/math.sqrt(difx**2+dify**2)
        magdiff=math.sqrt(sumx**2+sumy**2)
        sumx=sumx/magdiff
        sumy=sumy/magdiff
        Npreys[-1][0]=Npreys[-1][0]+Npreys[-1][5]*sumx
        Npreys[-1][1]=Npreys[-1][0]+Npreys[-1][5]*sumy
        ##END PREY MOVEMENT
        
        ##PREY EATING##
        for plant in Plants:
            if math.sqrt(plant[0]**2+plant[1]**2)<=(prey[5]*2):
                Npreys[-1][2]=Npreys[-1][2]+plant[2]
                del Plants[Plants.index(plant)]
        ##END PREY EATING##
        
        ##PREY SPLITTING##
        if prey[2]>prey[3]:#this path spawns another prey
            Npreys.append(prey)#the baby is spawned
            Npreys[-2][2]=Npreys[-2][2]/2#halves size of old prey
            Npreys[-1][2]=Npreys[-1][2]/2#halves size of new prey
            Npreys[-1][0]=Npreys[-1][0]+random.gauss(0.0,Npreys[-1][4])#changes x coordinate
            Npreys[-1][1]=Npreys[-1][1]+random.gauss(0.0,Npreys[-1][4])#changes y coordinate
            Npreys[-1][3]=Npreys[-1][3]+random.gauss(0,10)#changes split size
            Npreys[-1][4]=Npreys[-1][4]+random.gauss(0.0,0.5)#changes spawn radius
            Npreys[-1][5]=Npreys[-1][5]+random.gauss(0.0,0.5)#changes metabolism
            Npreys[-1][6]=Npreys[-1][6]+random.gauss(0.0,0.5)#changes predator repulsion
            Npreys[-1][7]=Npreys[-1][7]+random.gauss(0.0,0.5)#changes prey repulsion
            Npreys[-1][8]=Npreys[-1][8]+random.gauss(0.0,0.5)#changes plant attraction
        Npreys[-1][2]=Npreys[-1][2]-Npreys[-1][5]**2 #this lowers the predator's health
        if Npreys[-1][2]<1:
            del Npreys[-1]
        ###END PREYS###
        
        ###PREDATORS###
    for predator in Predators:
        Npredators.append(predator)
        sumx=0
        sumy=0
       
        ##PREDATOR MOVEMENT##
        #CHASING PREY#
        for prey in Preys:
            difx=prey[0]-predator[0]
            dify=prey[1]-predator[1]
            sumx = sumx + (predator[7]*difx)/math.sqrt(difx**2+dify**2)
            sumy = sumy + (predator[7]*dify)/math.sqrt(difx**2+dify**2)
        #AVOIDING PREDATORS#
        for opredator in Predators:
            if Predators.index(opredator)==Predators.index(predator): #this makes sure it doesn't affect itself.
                donothing=1
            else:
                difx=predator[0]-opredator[0]
                dify=predator[1]-opredator[1]
                sumx = sumx + (predator[6]*difx)/math.sqrt(difx**2+dify**2)
                sumy = sumy + (predator[6]*dify)/math.sqrt(difx**2+dify**2)
        magdiff=math.sqrt(sumx**2+sumy**2)
        sumx=sumx/magdiff
        sumy=sumy/magdiff
        Npredators[-1][0]=Npredators[-1][0]+Npredators[-1][5]*sumx
        Npredators[-1][1]=Npredators[-1][0]+Npredators[-1][5]*sumy
        ##END PREDATOR MOVEMENT
        
        #PREDATOR EATING#
        for prey in Preys:
            if math.sqrt(prey[0]**2+prey[1]**2)<=(predator[5]*2):
                Npredators[-1][2]=Npredators[-1][2]+prey[2]
                del Preys[Preys.index(prey)]
        #END PREDATOR EATING#
        
        #PREDATOR SPLITTING#
        if predator[2]>predator[3]:#this path spawns another prey
            Npredators.append(prey)#the baby is spawned
            Npredators[-2][2]=Npredators[-2][2]/2#halves size of old prey
            Npredators[-1][2]=Npredators[-1][2]/2#halves size of new prey
            Npredators[-1][0]=Npredators[-1][0]+random.gauss(0.0,Npreys[-1][4])#changes x coordinate
            Npredators[-1][1]=Npredators[-1][1]+random.gauss(0.0,Npreys[-1][4])#changes y coordinate
            Npredators[-1][3]=Npredators[-1][3]+random.gauss(0,10)#changes split size
            Npredators[-1][4]=Npredators[-1][4]+random.gauss(0.0,0.5)#changes spawn radius
            Npredators[-1][5]=Npredators[-1][5]+random.gauss(0.0,0.5)#changes metabolism
            Npredators[-1][6]=Npredators[-1][6]+random.gauss(0.0,0.5)#changes predator repulsion
            Npredators[-1][7]=Npredators[-1][7]+random.gauss(0.0,0.5)#changes prey attraction
        #END PREDATOR SPLITTING
        Npredators[-1][2]=Npredators[-1][2]-Npredators[-1][5]**2 #this lowers the predator's health
        if Npredators[-1][2]<1:
            del Npredators[-1]    
        ###END PREDATORS

    ###UPDATE MATRICIES
    Plants=Nplants
    Preys=Npreys
    Predators=Npredators
    Nplants=[]
    Npreys=[]
    Npredators=[]
    if iteration%100==0:
        pylab.title('Evolving program')
        pylab.scatter(Plants[:][0],Plants[:][1],s=1,c='g',marker='o')
        pylab.scatter(Preys[:][0],Preys[:][1],s=2,c='b',marker='o')
        pylab.scatter(Predators[:][0],Predators[:][1],s=3,c='r',marker='o')
        pylab.show()
    iteration=iteration-1

prove putting copy slice everywhere where there is append i.e.

Npray.append(pray[:])

please take out capitalzed names and use python_style_names ;) Are you sure about repeating lines 45 vs 47? I would also check namedtuple type for making nice named access to attributes.

Use a function for the redundant code. An example only to replace plant, predator, and prey append redundancy:

def append_next(item, list_in, max_depth):

    ## first 3 values not used
    gauss_list = [0, 0, 0, 10, 0.5, 0.5, 0.5, 0.5]
    if item[2]>item[3]:#this path spawns another
            next_item = deepcopy(item)
            list_in.append(next_item)#the baby is spawned
            list_in[-2][2]=list_in[-2][2]/2#halves size of old plant
            list_in[-1][2]=list_in[-1][2]/2#halves size of new plant

            list_in[-1][0]= list_in[-1][0]+random.gauss(0.0,list_in[-1][4])#changes x coordinate
            list_in[-1][1]= list_in[-1][1]+random.gauss(0.0,list_in[-1][4])#changes y coordinate

            for x in range(max_depth+1):
                amt = gauss_list[x]:
                list_in[-1][x] += random.gauss(0,amt)
    return list_in
#
#------------------------------------------------------------------------------------
        if plant[2]>plant[3]:#this path spawns another plant
            Nplants = append_next(plant, Nplants, 4):
#
#------------------------------------------------------------------------------------
        if prey[2]>prey[3]:#this path spawns another prey
            Nprey = append_next(prey, Nprey, 8): 
#
# etc. for predators
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.