21

Is there a straightforward way to get the index of an item I just appended to a list? I need to keep track of the last added item.

I came up with two possible solutions:

# Workaround 1
# The last added is the one at index len(li) - 1
>> li = ['a', 'b', 'c',]
>> li.append('d')
>> last_index = len(li) - 1
>> last_item = li[len(li) - 1]

# Workaround 2
# Use of insert at index 0 so I know index of last added
>> li = ['a', 'b', 'c',]
>> li.insert(0, 'd')
>> last_item = li[0]

Is there a trick to get the index of an appended item?

If there's not, which of the above would you use and why? Any different workaround you suggest?

Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
romeroqj
  • 829
  • 3
  • 10
  • 21

3 Answers3

33

li[-1] is the last item in the list, and hence the one that was most recently appended to its end:

>>> li = [1, 2, 3]
>>> li.append(4)
>>> li[-1]
4

If you need the index, not the item, then len(li) - 1 is just fine, and very efficient (since len(li) is computed in constant time - see below)


In the source of CPython, len for lists is mapped to function list_length in Objects/listobject.c:

static Py_ssize_t
list_length(PyListObject *a)
{
    return Py_SIZE(a);
}

Py_SIZE is just a macro for accessing the size attribute of all Python objects, defined in Include/object.h:

#define Py_SIZE(ob)     (((PyVarObject*)(ob))->ob_size)

Hence, len(lst) is essentially a single pointer dereference.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
4

A third possible solution would be to subclass list and override the append method, so that it automatically stores in a property like mylist.last_added whenever you call it.

This approach - if extended to other list methods - offers the advantage that you could potentially create a class where it will keep track of the index of the last added element regardless of the method used (insert, append, or simple assignment of mylist[some_index] = some_value).

Another advantage of embedding this info in the list object is that you will pass around it without having to worry about namespaces (so you will be able to retrieve it even if your list is passed by a return or yield, for example).

mac
  • 42,153
  • 26
  • 121
  • 131
  • 1
    I suggest subclassing the [collections.MutableSequence](http://docs.python.org/library/collections.html#abcs-abstract-base-classes) [ABC](http://en.wikipedia.org/wiki/Abstract_base_class) instead of list. This makes it easier to "catch" every way your extended list might be used, and you only need to implement 5 methods. See [this](http://stackoverflow.com/questions/241141/python-lazy-list/5104787#5104787) answer (shameless plug). – Lauritz V. Thaulow Jul 01 '11 at 08:49
3

You can index lists from either side. The index of the last element is always -1, you don't need to call len. Repeatedly inserting at the beginning is very inefficient (requires all elements in the list to be moved one place down).

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
  • 5
    My understanding is that the OP wanted a "reusable/absolute" index. In other words an index that could be used to track the value even once other elements will be appended to the list (-1 is "non-reusable/relative"... but maybe I'm thinking to far? – mac Jul 01 '11 at 08:39
  • 1
    @mac you're not going to far, actually I think your suggestion is a great one but in the long run, I potentially will have to deal with the limitations you mentioned about namespaces and the preservation of that index through returns. By now @Eli Bendersky clarification about efficiency of len(li) is O.K. Appreciate your help! – romeroqj Jul 01 '11 at 08:50