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