0

I have a really weird problem with append in a list.

import random

def CreatePopulation(a, r):
    pippo=range(a)
    print pippo
    i=0
    prova=[]
    while i<r:
        random.shuffle(pippo)
        print pippo
        prova.append(pippo)
        print prova
        i=i+1
    return prova

pop=CreatePopulation(5,10)
print pop

example=["a","b"]
example.append("c")
print example

I can't upload the entire output because the website says that it's bad formatted. But the output it's a list with ten times the same value, the last shuffle of x, like this:

[[1, 0, 4, 2, 3], [1, 0, 4, 2, 3], [1, 0, 4, 2, 3], [1, 0, 4, 2, 3], [1, 0, 4, 2, 3],
 [1, 0, 4, 2, 3], [1, 0, 4, 2, 3], [1, 0, 4, 2, 3], [1, 0, 4, 2, 3], [1, 0, 4, 2, 3],
 [1, 0, 4, 2, 3]]

But the example append out of the loop works properly:

['a', 'b', 'c']

I really can't see why the output it's like this, instead of append at the end, it just create a list with all the same value. But if i use append out of the loop it works.

falsetru
  • 357,413
  • 63
  • 732
  • 636

2 Answers2

1

I was experiencing the same problem and struggling to understand why the issue takes place. Description of 'append' function which I see all over the internet is that it "Appends a new item (object) to the end of the array". According to such description the problem above must not even happen.

It seems to me that 'append' instead of adding an object to the end of the list, adds a reference to that object. So when used in a loop as you tried, it adds reference to the same location in memory again and again (despite values in that location change). So you end up with 2D array each row of which refers to the same location in memory.

You can check this by changing value of your pippo array after the loop is over:

    def CreatePopulation(a, r):
     pippo=range(a)
     print pippo
     i=0
     prova=[]
     while i<r:
      random.shuffle(pippo)
      print pippo
      prova.append(pippo)
      print 'prova=', prova
      i=i+1
     random.shuffle(pippo) #here we change pippo after appending of prova is done
     return prova

    pop=CreatePopulation(5,10)
    print 'pop=', pop

In results there will be something like:

    prova= [[0, 2, 3, 4, 1], [0, 2, 3, 4, 1], [0, 2, 3, 4, 1], [0, 2, 3, 4, 1], [0, 2, 3, 4, 1], [0, 2, 3, 4, 1], [0, 2, 3, 4, 1], [0, 2, 3, 4, 1], [0, 2, 3, 4, 1], [0, 2, 3, 4, 1]]
    pop= [[2, 0, 1, 3, 4], [2, 0, 1, 3, 4], [2, 0, 1, 3, 4], [2, 0, 1, 3, 4], [2, 0, 1, 3, 4], [2, 0, 1, 3, 4], [2, 0, 1, 3, 4], [2, 0, 1, 3, 4], [2, 0, 1, 3, 4], [2, 0, 1, 3, 4]]

pop shows different value than the last prova in the loop, because we've changed pippo which prova is referring to.

I can see now why creating a new pippo list inside the loop resolves the problem.

I haven't found this 'append' reference logic to be explicitly explained anywhere, that is why leaving this comment.

casens
  • 11
  • 2
0

The code is appending the same list (pippo) multiple times.

In other word, at the end of the loop, prova will contain something like:

prova = [pippo, pippo, pippo, pippo, ..., pippo]

Making a new list inside the loop will solve the problem.

def CreatePopulation(a, r):
    prova = []
    for i in range(r):
        pippo = range(a)
        random.shuffle(pippo)
        prova.append(pippo)
    return prova

BTW, using sample.shuffle which returns a new list, you don't need to create a source list every time:

def CreatePopulation(a, r):
    pippo = range(a)
    return [random.sample(pippo, a) for i in range(r)]
falsetru
  • 357,413
  • 63
  • 732
  • 636