1

Let's say

>>> a = [1,2,3,4,5]

And I want an output like

>>> b
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]

Here is my code:

class Child(object):
    def get_lines(self):
        a = [1,2,3,4,5]              
        b=[]
        c=[]
        j=0
        for i in a:
            print i
            b.append(i)
            print b
            c.insert(j,b)

            j=j+1
        print c


son= Child()
son.get_lines()

When I print list b in loop, it gives:

1 
[1] 
2 
[1, 2]
3  
[1, 2, 3]  
4  
[1, 2, 3, 4] 
5 
[1, 2, 3, 4, 5]

and the output is:

[[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]

Where do I make wrong in the code?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Jay Venkat
  • 397
  • 2
  • 5
  • 18

5 Answers5

5

b is always the same list object (note I've changed print c to return c):

>>> map(id, Child().get_lines())
...
[49021616, 49021616, 49021616, 49021616, 49021616]

c contains five references to the same list. I think what you want is:

class Child(object):

    def get_lines(self):
        a = [1, 2, 3, 4, 5]              
        return [a[:x+1] for x in range(len(a))]

This makes a shallow copy of part (or all) of a for each step:

>>> Child().get_lines()
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
2

Replace:

c.insert(j,b)

with:

 c.append(b[:])

and try again.

You need to make a copy of b. Otherwise you add the same b again and again resulting in the full list at all indices. 'b[:]' copies the list.

This solution does the same but is a bit shorter:

a = [1, 2, 3, 4, 5]
c = []
for i in range(1, len(a) + 1):
    c.append(a[:i])

now c is:

[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]

a[:i] slices the list a from the beginning to excluding the index i. In this case it does a[:1], a[:2] and so on. a[:1] makes a new list [1], a[:2] a new list [1, 2,] and so on. Using append() is simpler and insert().

Another alternative is a list comprehension:

a = [1, 2, 3, 4, 5]
[a[:i] for i in range(1, len(a) + 1)]

also results in:

[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
Mike Müller
  • 82,630
  • 20
  • 166
  • 161
2

Here in this case you are not supposed to append value to the list object (b). since list is mutable, it will refer to the exact same object in memory until a reassign occurs.

>>> b=[]
>>> b.append(1)
>>> id(b)
4337935424
>>> b.append(2)
>>> id(b)
4337935424
>>> b.append(3)
>>> id(b)
4337935424
>>> b = [1, 2, 3, 4]
>>> id(b)
4337942608

so that in your code c will make five references to the same list.

It will instruct a new object and then (to the extent possible) inserts references into it to the objects found in the original.

>>> class Child(object):
...
...     def get_lines(self):
...         a = [1, 2, 3, 4, 5]
...         return map(lambda x: a[:x+1], range(len(a)))
...
>>> Child().get_lines()
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
V Manikandan
  • 370
  • 6
  • 24
  • hey your explanation made perfect on my doubts. Also, I've a doubt on **map**. Why do you use this structure and also lambda... In jonrsharpe's answer, he didn't use kind of that. Which is less time complexity? – Jay Venkat May 15 '15 at 06:42
  • Yes you are right Jay, It is nothing but I use lambda in simple iteration. – V Manikandan May 15 '15 at 07:15
  • @JayVenkat in terms of time complexity `timeit` suggests that the list comprehension (mine) is fastest, then the copy (Mike/Serge) and `map` (Manikandan) is slowest, but this isn't by very much (2.04s, 2.43s and 2.85s respectively) – jonrsharpe May 15 '15 at 12:57
  • @jonrsharpe, I was about to mention that. python -m timeit "a = [1, 2, 3, 4, 5]; map(lambda x: a[:x+1], range(len(a)))" -->100000 loops, best of 3: 3.4 usec per loop. python -m timeit "a = [1, 2, 3, 4, 5]; [a[:x+1] for x in range(len(a))]"-->100000 loops, best of 3: 2.67 usec per loop – V Manikandan May 15 '15 at 15:19
1

You insert same b five times in list c. As a list actually contains references to objects and not copies, you have 5 times a reference to a same b list in which you have added successively 1, 2, 3, 4, 5. So the result.

You must instead add copies of list b :

def get_lines(self):
    a = [1,2,3,4,5]              
    b=[]
    c=[]
    j=0
    for i in a:
        print i
        b.append(i)
        print b
        c.insert(j,b[:]) # forces insertion of a copy

        j=j+1
    print c
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

In the loop, value of b persists since it's a mutable object. Hence when you print c, the last value of b is shown.

Instead of using b as temporary variable, you can directly use a as follows:

class Child(object):
    def get_lines(self):
        a = [1,2,3,4,5]
        b = []
        for index, element in enumerate(a, 1):
            b.append(x[:index])
        return b


son= Child()
son.get_lines()
JRajan
  • 672
  • 4
  • 19