Appending to []

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sun Apr 22 01:15:22 EDT 2012


On Sat, 21 Apr 2012 14:48:44 +0200, Bernd Nawothnig wrote:

> On 2012-04-20, Rotwang wrote:
>> since a method doesn't assign the value it returns to the instance on
>> which it is called; what it does to the instance and what it returns
>> are two completely different things.
> 
> Returning a None-value is pretty useless. Why not returning self, which
> would be the resulting list in this case? Returning self would make the
> language a little bit more functional, without any drawback.

It is a deliberate design choice, and there would be a drawback.

A method like append could have three obvious designs:

1) Functional, no side-effects: return a new list with the item appended.

2) Functional, with side-effect: return the same list, after appending 
the item.

3) Procedural, with side-effect: append the item, don't return anything 
(like a procedure in Pascal, or void in C).

Python chooses 3) as the design, as it is the cleanest, most pure choice 
for a method designed to operate by side-effect. Unfortunately, since 
Python doesn't have procedures, that clean design is slightly spoilt due 
to the need for append to return None (instead of not returning anything 
at all).

How about 1), the pure functional design? The downside of that is the 
usual downside of functional programming -- it is inefficient to 
duplicate a list of 100 million items just to add one more item to that 
list. Besides, if you want a pure functional append operation, you can 
simply use mylist + [item] instead.

But what about 2), the mixed (impure) functional design? Unfortunately, 
it too has a failure mode: by returning a list, it encourages the error 
of assuming the list is a copy rather than the original:

mylist = [1, 2, 3, 4]
another_list = mylist.append(5)
# many pages of code later...
do_something_with(mylist)


This is especially a pernicious error because instead of giving an 
exception, your program will silently do the wrong thing. 

    "I find it amusing when novice programmers believe their main
    job is preventing programs from crashing. More experienced
    programmers realize that correct code is great, code that 
    crashes could use improvement, but incorrect code that doesn’t 
    crash is a horrible nightmare."
    -- Chris Smith


Debugging these sorts of bugs can become very difficult, and design 2) is 
an attractive nuisance: it looks good because you can chain appends:

mylist.append(17).append(23).append(42)
# but why not use mylist.extend([17, 23, 42]) instead?

but the disadvantage in practice far outweighs the advantage in theory.

This is the same reason why list.sort, reverse and others also return 
None.



> Then nested calls like
> 
> a = [].append('x').append('y').append('z')
> 
> would be possible with a containing the resulting list
> 
> ['x', 'y', 'z'].
> 
> That is the way I expect any append to behave.

That would be possible, but pointless. Why not use:

a = ['x', 'y', 'z'] 

directly instead of constructing an empty list and then make three 
separate method calls? Methods which operate by side-effect but return 
self are an attractive nuisance: they seem like a good idea but actually 
aren't, because they encourage the user to write inefficient, or worse, 
incorrect, code.



-- 
Steven



More information about the Python-list mailing list