This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Provide list prepend method (even though it's not efficient)
Type: enhancement Stage:
Components: Library (Lib) Versions: Python 2.6
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: andybuckley, exarkun, ezio.melotti, georg.brandl, mark.dickinson, rhettinger, ssbarnea
Priority: normal Keywords:

Created on 2010-06-25 12:00 by andybuckley, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Messages (11)
msg108587 - (view) Author: Andy Buckley (andybuckley) Date: 2010-06-25 12:00
I know that Python lists aren't designed for efficient prepending, but sometimes when working with small lists it's exactly what needs to be done (search path lists being a common example). For a programmer aware of the performance issue and having convinced themself that it's not a problem for their use-case, it's a niggle that there is no prepend()  function for lists by direct analogy to the commonly-used append(). 

Writing l = ["foo"] + l, or something mucky based on l.insert(0, ...) or reverse/append/reverse is annoyingly asymmetric and no more performant. So I suggest that l.append(x) be added to the list interface, with a prominent warning in the documentation that it's not an efficient operation on that type (possibly mention the complexity scaling with list length). I think the role of the interface is to make simple things simple, not to make it difficult to do simple-but-inefficient things that people will do anyway ;)
msg108588 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2010-06-25 12:10
Thanks for bringing this up.

I think you have more work to do to successfully make the case that L.insert(0, x) is "difficult" enough to merit the addition of a new list method.  There are already at least two in-place insert-at-front list APIs (the second being L[:0] = [x]).  There should be a very compelling reason for adding a third, and I don't think that being able to skip the extra 0 in the L.insert version is going to suffice.
msg108590 - (view) Author: Andy Buckley (andybuckley) Date: 2010-06-25 12:23
Maybe I just value method symmetry/equivalence higher than the designers when it comes to interface expectations. I've seen several "I expected there to be a prepend() method like append() on lists, but there isn't -- what do I do?" emails on list archives when searching for info on this, which I think is in itself a hint that the current situation is counter-intuitive.

The argument that "there are already two ways to do it, so why add a third?", is not bad, but if applied to appending, it would ban the append() method... except that it's already there. Personally, the behaviour of append() is very familiar, while insert(0, ...) is less-used and I always have to explicitly confirm to myself that it isn't going to overwrite the zeroth existing element. Adding a prepend(x) method which just calls insert(0, x) (or calls the same underlying C function) would change nothing in terms of code efficiency -- bad programmers will always write inefficient code, one way or another -- but would make the list API more intuitive and improve code readability: seems to me like the sort of thing that Python is philosophically into ;)

Sorry to bring up this issue that I guess has been raised (many times?) before, but I thought I'd have a stab at a convincing case!
msg108592 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2010-06-25 12:35
If we add a .prepend() method, people will see it and start using it. Since now there's no 'prepend' method people ask why, and the answer they usually get is "because it not a good idea to add elements on lists (but if you really have to use the 'insert' method)".

The symmetry should be in the use case, not (only) in the names, otherwise we should also have a method to pop from the beginning to complete the symmetry with append/pop.

If you are not convinced yet you could write to the python-ideas mailing list.
msg108593 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-06-25 12:36
Andy, the bug-tracker isn't really the best place for this sort of discussion;  if you want to take this further I'd suggest mailing the python-ideas mailing list (bigger audience).

FWIW, the bar for adding new methods to builtins should be pretty high; I agree with Jean-Paul that 'list.prepend' seems unnecessary.
msg108594 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2010-06-25 12:36
> The argument that "there are already two ways to do it, so why add a third?", is not bad, but if applied to appending, it would ban the append() method... except that it's already there.

Not quite.  First let's consider the insert approach.  Unlike with prepending, you first have to find the length of the list: L.insert(len(L), x).  So it's no longer just a matter of an extra constant (although it /is/ still pretty simple).  Next, the slice assignment solution: L[-1:] = [x] doesn't work, because it actually includes the last element of L in the slice, so it replaces the last value instead of appending the new element.

So there's really just two ways, L.insert(len(L), x) and L.append(x).  To me, it seems like the extra cognitive load of figuring out whether the parameter to L.insert should be len(L) or len(L) - 1 or len(L) + 1 makes L.append worthwhile.  Maybe the same argument could be applied to L.insert(0, x), but it seems like a simpler case ("of _course_ inserting at 0 prepends") to me.

The other cost of adding a new list method is updating all of the list-like interfaces out there to also provide this, although that would probably be a cost worth accepting for a really compelling new method.

> Sorry to bring up this issue that I guess has been raised (many times?) before, but I thought I'd have a stab at a convincing case!

No need to apologize!  Thanks for the reasoned argument.  At least this can serve as a reference when the issue comes up again in the future, and perhaps someone else will be more convinced than overrule me. :)
msg108595 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2010-06-25 12:50
[insert the usual reference to collections.deque here]
msg108596 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2010-06-25 12:52
Here: http://docs.python.org/py3k/tutorial/datastructures.html#using-lists-as-queues
msg108597 - (view) Author: Andy Buckley (andybuckley) Date: 2010-06-25 13:22
Still not convinced with the reasoning, I'm afraid, but I certainly agree that modifications to built-ins are not to be made lightly. Using deques, which are far less familiar, is not a particularly natural thing to do for a search path, and of course can't be used with a built-in list to which you might well wish to prepend: sys.path.

Personally, I would have no problem with a list pop_front method ;) I understand the connection of API availability and misuse of algorithmically inefficient methods -- for large lists -- but I don't find "be inconveniently absent so that users search for an answer and find out about complexity issues" a very compelling or Pythonic design aim. But I admit that l.insert(0, x) is not such a hard idiom to learn -- just a bit less syntactically elegant and self-explanatory than I would prefer for a simple task, especially where I've already conciously made the efficiency decision in my choice of container type.

But thanks for listening... as mentioned, this can stay here as a reference for further enquiries (I searched for a pre-existing prepend() request before submitting this ticket, of course!) and I'll go visit python-ideas. Cheers!
msg115778 - (view) Author: Sorin Sbarnea (ssbarnea) * Date: 2010-09-07 15:28
+1 for adding it because it will make the code easier to read/understand.
Bad performance could be documented and it's related about internal representation.
msg115786 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2010-09-07 16:01
Sorry, this needs to stay closed.
It has no chance.
History
Date User Action Args
2022-04-11 14:57:02adminsetgithub: 53326
2010-09-07 16:01:22rhettingersetnosy: + rhettinger
messages: + msg115786
2010-09-07 15:28:27ssbarneasetnosy: + ssbarnea
messages: + msg115778
2010-06-25 13:22:50andybuckleysetmessages: + msg108597
2010-06-25 12:52:27ezio.melottisetmessages: + msg108596
2010-06-25 12:50:41georg.brandlsetnosy: + georg.brandl
messages: + msg108595
2010-06-25 12:36:42exarkunsetmessages: + msg108594
2010-06-25 12:36:15mark.dickinsonsetnosy: + mark.dickinson
messages: + msg108593
2010-06-25 12:35:30ezio.melottisetnosy: + ezio.melotti
messages: + msg108592
2010-06-25 12:23:50andybuckleysetmessages: + msg108590
2010-06-25 12:10:35exarkunsetstatus: open -> closed

nosy: + exarkun
messages: + msg108588

resolution: wont fix
2010-06-25 12:00:47andybuckleycreate