[Tutor] Standard way to append to a list?

Magnus Lyckå magnus@thinkware.se
Wed Apr 16 19:02:02 2003


At Tue, 15 Apr 2003 01:44:38 -0400, Brian Christopher Robinson wrote:
>Say I have:
>
>list = [1, 2, 3]
>
>What is the standard way to add 4 to the end of the list?  I know:
>
>list = list + [4]
>
>would work.  What else is there?

You said it yourself in the subject line. Please read
http://www.python.org/doc/current/lib/typesseq-mutable.html
etc.

First of all, don't use list as a variable name. It's
allowed, but bad practice to use builtin type names as
varible names. See below:

 >>> l = list((1,32,5))
 >>> l
[1, 32, 5]
 >>> list = [1,2,3]
 >>> l = list((1,32,5))
Traceback (most recent call last):
   File "<interactive input>", line 1, in ?
TypeError: 'list' object is not callable

:( Since you have reassigned the name list to refer to a
particular list object. instead of the list type, you cqan
no longer cast tuples to lists... (Well, you can type
"__builtins__.list((1,32,5))", but that seems a bit
awkward...)

Secondly, using "l = l + [4]" or the shorter form "l += [4]",
you are doing the following:

1. To start with, you have a list object containing [1,2,3]
2. Then you create a new list object containing [4].
3. With your assignment you create a new list object, and
    place the sum of the two lists, i.e. [1,2,3,4] in that.
4. As you rebind the variable name "l" from the list
    containing [1,2,3] to the one containing [1,2,3,4], you
    will reduce the reference count on the list containing
    [1,2,3]. If no other variables etc references that list,
    it will be garbage collected now (or perhaps a little
    later if you are running Jython.)
5. The list containing [4] will also be garbage collected as
    soon as the assignment is done.

All this juggling with objects take space and time.

Using "l.append(4)" you will just mutate the already existing
list object, and append is very fast. Lets test it with the
profiler:

 >>> import profile
 >>> def add():
...     l = []
...     for i in xrange(10000):
...             l = l + [i]
...
 >>> profile.run('add()')
          3 function calls in 4.799 CPU seconds
    Ordered by: standard name
    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         1    4.664    4.664    4.664    4.664 <interactive input>:1(add)
         1    0.002    0.002    4.666    4.666 <string>:1(?)
         1    0.133    0.133    4.799    4.799 profile:0(add())
         0    0.000             0.000          profile:0(profiler)

 >>> def add():
...     l = []
...     for i in xrange(10000):
...             l.append(i)
...
 >>> profile.run('add()')
          3 function calls in 0.023 CPU seconds
    Ordered by: standard name
    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         1    0.022    0.022    0.022    0.022 <interactive input>:1(add)
         1    0.001    0.001    0.023    0.023 <string>:1(?)
         1    0.000    0.000    0.023    0.023 profile:0(add())
         0    0.000             0.000          profile:0(profiler)

It seems append is more than 200 times faster in this case. With a
shorter list, I'm sure the difference will be smaller though. Adding
(+) large lists is much slower than appending to large lists.

 >>> def add():
...     for i in xrange(1000):
...             l = []
...             for j in range(10):
...                     l = l + [j]
...
 >>> profile.run('add()')
          3 function calls in 0.041 CPU seconds
    Ordered by: standard name
    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         1    0.040    0.040    0.040    0.040 <interactive input>:1(add)
         1    0.000    0.000    0.040    0.040 <string>:1(?)
         1    0.000    0.000    0.041    0.041 profile:0(add())
         0    0.000             0.000          profile:0(profiler)

 >>> def add():
...     for i in xrange(1000):
...             l = []
...             for j in range(10):
...                     l.append(j)
...
 >>>
 >>> profile.run('add()')
          3 function calls in 0.025 CPU seconds
    Ordered by: standard name
    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         1    0.025    0.025    0.025    0.025 <interactive input>:1(add)
         1    0.000    0.000    0.025    0.025 <string>:1(?)
         1    0.000    0.000    0.025    0.025 profile:0(add())
         0    0.000             0.000          profile:0(profiler)

Now "l = l + [j]" only takes 60% more time, but append is still a better
choice.