50

Running the code

listoflists = []
list = []
for i in range(0,10):
    list.append(i)
    if len(list)>3:
        list.remove(list[0])
        listoflists.append((list, list[0]))
print listoflists

returns

[([7, 8, 9], 0), ([7, 8, 9], 0), ([7, 8, 9], 0), ([7, 8, 9], 1), ([7, 8, 9], 2), ([7, 8, 9], 3), ([7, 8, 9], 4), ([7, 8, 9], 5), ([7, 8, 9], 6), ([7, 8, 9], 7)]

so somehow the first argument of each tuple (list) is being updated each time in the list of lists, but the second argument list[0] is not. Can someone explain what's going on here and suggest a way to fix this? I'd like to output

[([0],0), ([0,1],0), ...
Carl
  • 603
  • 1
  • 5
  • 5

7 Answers7

55

Lists are a mutable type - in order to create a copy (rather than just passing the same list around), you need to do so explicitly:

listoflists.append((list[:], list[0]))

However, list is already the name of a Python built-in - it'd be better not to use that name for your variable. Here's a version that doesn't use list as a variable name, and makes a copy:

listoflists = []
a_list = []
for i in range(0,10):
    a_list.append(i)
    if len(a_list)>3:
        a_list.remove(a_list[0])
        listoflists.append((list(a_list), a_list[0]))
print listoflists

Note that I demonstrated two different ways to make a copy of a list above: [:] and list().

The first, [:], is creating a slice (normally often used for getting just part of a list), which happens to contain the entire list, and thus is effectively a copy of the list.

The second, list(), is using the actual list type constructor to create a new list which has contents equal to the first list. (I didn't use it in the first example because you were overwriting that name in your code - which is a good example of why you don't want to do that!)

Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
Amber
  • 507,862
  • 82
  • 626
  • 550
25

I came here because I'm new with python and lazy so I was searching an example to create a list of 2 lists, after a while a realized the topic here could be wrong... This is a code to create a list of lists:

listoflists = []
for i in range(0,2):
    sublist = []
    for j in range(0,10)
        sublist.append((i,j))
    listoflists.append(sublist)
print listoflists

this is the output:

[
    [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (0, 9)],
    [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9)]
]

The problem with your code seems to be you are creating a tuple with your list and you get the reference to the list instead of a copy. That I guess should fall under a tuple topic...

sRavioli
  • 27
  • 5
user110954
  • 751
  • 8
  • 9
  • This doesn't give the output the OP asked for. – nekomatic Nov 25 '15 at 15:51
  • 21
    right, but I thought this better matches the title of the question than the question itself and normally people land here because they want just create a list of list and not because they have problems updating a list of tuples... probably because I use this site as a learning tool and not as a debugger tool :-) – user110954 Dec 01 '15 at 17:35
13

First, I strongly recommend that you rename your variable list to something else. list is the name of the built-in list constructor, and you're hiding its normal function. I will rename list to a in the following.

Python names are references that are bound to objects. That means that unless you create more than one list, whenever you use a it's referring to the same actual list object as last time. So when you call

listoflists.append((a, a[0]))

you can later change a and it changes what the first element of that tuple points to. This does not happen with a[0] because the object (which is an integer) pointed to by a[0] doesn't change (although a[0] points to different objects over the run of your code).

You can create a copy of the whole list a using the list constructor:

listoflists.append((list(a), a[0]))

Or, you can use the slice notation to make a copy:

listoflists.append((a[:], a[0]))
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
3

When you run the code

listoflists.append((list, list[0]))

You are not (as I think you expect) adding a copy of list to the end of listoflists. What you are doing is adding a reference to list to the end of listoflists. Thus, every time you update list, it updates every reference to list, which in this case, is every item in listoflists

What you could do instead is something like this:

listoflists = []
for i in range(1, 10):
    listoflists.append((range(i), 0))
Strigoides
  • 4,329
  • 5
  • 23
  • 25
3

The list variable (which I would recommend to rename to something more sensible) is a reference to a list object, which can be changed.

On the line

listoflists.append((list, list[0]))

You actually are only adding a reference to the object reference by the list variable. You've got multiple possibilities to create a copy of the list, so listoflists contains the values as you seem to expect:

Use the copy library

import copy
listoflists.append((copy.copy(list), list[0]))

use the slice notation

listoflists.append((list[:], list[0]))
kajk
  • 126
  • 12
2

You're also not going to get the output you're hoping for as long as you append to listoflists only inside the if-clause.

Try something like this instead:

import copy

listoflists = []
list = []
for i in range(0,10):
    list.append(i)
    if len(list)>3:
        list.remove(list[0])
    listoflists.append((copy.copy(list), copy.copy(list[0])))
print(listoflists)
Michael
  • 57,169
  • 9
  • 80
  • 125
2

Time traveller here

List_of_list =[([z for z in range(x-2,x+1) if z >= 0],y) for y in range(10) for x in range(10)]

This should do the trick. And the output is this:

[([0], 0), ([0, 1], 0), ([0, 1, 2], 0), ([1, 2, 3], 0), ([2, 3, 4], 0),  ([3, 4, 5], 0), ([4, 5, 6], 0), ([5, 6, 7], 0), ([6, 7, 8], 0), ([7, 8, 9], 0), ([0], 1), ([0, 1], 1), ([0, 1, 2], 1), ([1, 2, 3], 1), ([2, 3, 4], 1), ([3, 4, 5], 1), ([4, 5, 6], 1), ([5, 6, 7], 1), ([6, 7, 8], 1), ([7, 8, 9], 1), ([0], 2), ([0, 1], 2), ([0, 1, 2], 2), ([1, 2, 3], 2), ([2, 3, 4], 2), ([3, 4, 5], 2), ([4, 5, 6], 2), ([5, 6, 7], 2), ([6, 7, 8], 2), ([7, 8, 9], 2), ([0], 3), ([0, 1], 3), ([0, 1, 2], 3), ([1, 2, 3], 3), ([2, 3, 4], 3), ([3, 4, 5], 3), ([4, 5, 6], 3), ([5, 6, 7], 3), ([6, 7, 8], 3), ([7, 8, 9], 3), ([0], 4), ([0, 1], 4), ([0, 1, 2], 4), ([1, 2, 3], 4), ([2, 3, 4], 4), ([3, 4, 5], 4), ([4, 5, 6], 4), ([5, 6, 7], 4), ([6, 7, 8], 4), ([7, 8, 9], 4), ([0], 5), ([0, 1], 5), ([0, 1, 2], 5), ([1, 2, 3], 5), ([2, 3, 4], 5), ([3, 4, 5], 5), ([4, 5, 6], 5), ([5, 6, 7], 5), ([6, 7, 8], 5), ([7, 8, 9], 5), ([0], 6), ([0, 1], 6), ([0, 1, 2], 6), ([1, 2, 3], 6), ([2, 3, 4], 6), ([3, 4, 5], 6), ([4, 5, 6], 6), ([5, 6, 7], 6), ([6, 7, 8], 6), ([7, 8, 9], 6), ([0], 7), ([0, 1], 7), ([0, 1, 2], 7), ([1, 2, 3], 7), ([2, 3, 4], 7), ([3, 4, 5], 7), ([4, 5, 6], 7), ([5, 6, 7], 7), ([6, 7, 8], 7), ([7, 8, 9], 7), ([0], 8), ([0, 1], 8), ([0, 1, 2], 8), ([1, 2, 3], 8), ([2, 3, 4], 8), ([3, 4, 5], 8), ([4, 5, 6], 8), ([5, 6, 7], 8), ([6, 7, 8], 8), ([7, 8, 9], 8), ([0], 9), ([0, 1], 9), ([0, 1, 2], 9), ([1, 2, 3], 9), ([2, 3, 4], 9), ([3, 4, 5], 9), ([4, 5, 6], 9), ([5, 6, 7], 9), ([6, 7, 8], 9), ([7, 8, 9], 9)]    

This is done by list comprehension(which makes looping elements in a list via one line code possible). The logic behind this one-line code is the following:

(1) for x in range(10) and for y in range(10) are employed for two independent loops inside a list

(2) (a list, y) is the general term of the loop, which is why it is placed before two for's in (1)

(3) the length of the list in (2) cannot exceed 3, and the list depends on x, so

[z for z in range(x-2,x+1)] 

is used

(4) because z starts from zero but range(x-2,x+1) starts from -2 which isn't what we want, so a conditional statement if z >= 0 is placed at the end of the list in (2)

[z for z in range(x-2,x+1) if z >= 0] 
mathguy
  • 1,450
  • 1
  • 16
  • 33