96

I want to create a list that will contain the last 5 values entered into it.

Here is an example:

>>> l = []
>>> l.append('apple')
>>> l.append('orange')
>>> l.append('grape')
>>> l.append('banana')
>>> l.append('mango')
>>> print(l)
['apple', 'orange', 'grape', 'banana', 'mango']
>>> l.append('kiwi')
>>> print(l)  # only 5 items in list
['orange', 'grape', 'banana', 'mango', 'kiwi']

So, in Python, is there any way to achieve what is demonstrated above? The variable does not need to be a list, I just used it as an example.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
lanrat
  • 4,344
  • 10
  • 35
  • 41

7 Answers7

170

You might want to use a collections.deque object with the maxlen constructor argument instead:

>>> l = collections.deque(maxlen=5)
>>> l.append('apple')
>>> l.append('orange')
>>> l.append('grape')
>>> l.append('banana')
>>> l.append('mango')
>>> print(l)
deque(['apple', 'orange', 'grape', 'banana', 'mango'], maxlen=5)
>>> l.append('kiwi')
>>> print(l)  # only 5 items in list
deque(['orange', 'grape', 'banana', 'mango', 'kiwi'], maxlen=5)
wjandrea
  • 28,235
  • 9
  • 60
  • 81
lambacck
  • 9,768
  • 3
  • 34
  • 46
  • +1, nice -- I was about to suggest subclassing list ala [gnibbler](http://stackoverflow.com/questions/5944708/python-forcing-a-list-to-a-fixed-size/5944763#5944763) but I suspected there might be a pre-built solution. – senderle May 10 '11 at 03:18
  • How python implement the solution? Does deque pop out left element when new element is added? – xiao 啸 May 10 '11 at 05:57
  • 2
    @xiao it is a double ended queue which means you can efficiently add to either end. In fact there is a appendleft method to append to the front of the deque. If a maxlen is present and append/appendleft will go over one item is removed from the other end. – lambacck May 10 '11 at 13:47
  • is there any way to easily remove the 'deque' and 'maxlen=5' from the resulting list so that only the list of values is printed? – treetop Jan 18 '18 at 20:18
  • 1
    print(list(l)) will just print the contents – dxander Jun 22 '19 at 18:20
  • 1
    Please notice this solution is slow for copies of large chunks, as it is a doubly linked list, as opposed to a simple `list` which is a c array. – Gulzar Apr 07 '20 at 19:55
  • @Gulzar What do you mean by "copies of large chunks"? At first I thought you meant slicing a large deque, but they don't seem to support slicing. – wjandrea Oct 24 '22 at 21:34
  • @wjandrea I meant [likely, it was 2 years ago] slicing large chunks as you assumed, and this doesn't have to happen via actual slicing. Since a c array doesn't actually slice, it is o(slice-size) vs o(1). – Gulzar Oct 25 '22 at 09:11
17

I ran into this same issue... maxlen=5 from deque was NOT a supported option due to access speed / reliability issues.

SIMPLE Solution:

l = []
l.append(x)                         # add 'x' to right side of list
l = l[-5:]                          # maxlen=5

After you append, just redefine 'l' as the most recent five elements of 'l'.

print(l)

Call it Done.

For your purposes you could stop right there... but I needed a popleft(). Whereas pop() removes an item from the right where it was just appended... pop(0) removes it from the left:

if len(l) == 5:                     # if the length of list 'l' has reached 5 
    right_in_left_out = l.pop(0)    # l.popleft()
else:                               #
    right_in_left_out = None        # return 'None' if not fully populated

Hat tip to James at Tradewave.net

No need for class functions or deque.

Further... to append left and pop right:

l = []
l.insert(0, x)                      # l.appendleft(x)
l = l[-5:]                          # maxlen=5

Would be your appendleft() equivalent should you want to front load your list without using deque

Finally, if you choose to append from the left...

if len(l) == 5:                     # if the length of list 'l' has reached 5 
    left_in_right_out = l.pop()     # pop() from right side
else:                               #
    left_in_right_out = None        # return 'None' if not fully populated
litepresence
  • 3,109
  • 1
  • 27
  • 35
  • 1
    Would be interesting to see the performance differences between the solutions mentioned on this page. I like this answer a lot, but I wonder whether I'm missing something here. Why isn't everybody doing it this way? – oelna Nov 18 '22 at 12:18
15

You could subclass list

>>> class L(list):
...     def append(self, item):
...         list.append(self, item)
...         if len(self) > 5: del self[0]
... 
>>> l = L()
>>> l.append('apple')
>>> l.append('orange')
>>> l.append('grape')
>>> l.append('banana')
>>> l.append('mango')
>>> print(l)
['apple', 'orange', 'grape', 'banana', 'mango']
>>> l.append('kiwi')
>>> print(l)
['orange', 'grape', 'banana', 'mango', 'kiwi']
>>> 
Gulzar
  • 23,452
  • 27
  • 113
  • 201
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
9

deque is slow for random access and does not support slicing. Following on gnibbler's suggestion, I put together a complete list subclass.

However, it is designed to "roll" right-to-left only. For example, insert() on a "full" list will have no effect.

class LimitedList(list):

    # Read-only
    @property
    def maxLen(self):
        return self._maxLen

    def __init__(self, *args, **kwargs):
        self._maxLen = kwargs.pop("maxLen")
        list.__init__(self, *args, **kwargs)

    def _truncate(self):
        """Called by various methods to reinforce the maximum length."""
        dif = len(self)-self._maxLen
        if dif > 0:
            self[:dif]=[]

    def append(self, x):
        list.append(self, x)
        self._truncate()

    def insert(self, *args):
        list.insert(self, *args)
        self._truncate()

    def extend(self, x):
        list.extend(self, x)
        self._truncate()

    def __setitem__(self, *args):
        list.__setitem__(self, *args)
        self._truncate()

    def __setslice__(self, *args):
        list.__setslice__(self, *args)
        self._truncate()
trvjbr
  • 111
  • 1
  • 2
1

You could use a capped collection in PyMongo - it's overkill, but it does the job nicely:

import pymongo

#create collection
db.createCollection("my_capped_list",{capped:True, max:5})

#do inserts ...

#Read list
l = list(db.my_capped_list.find())

Hence any time you call my_capped_list, you will retrieve the last 5 elements inserted.

ajsp
  • 2,512
  • 22
  • 34
0

Most often when you need such a kind of facility, you would write a function which takes the list and then returns the last five elements.

>>> l = range(10)
>>> l[-5:]

But if you really want a custom list, having a cap on five elements, you can override the built-in list and it's methods, you would do something like this, for all it's methods.

class fivelist(list):
    def __init__(self, items):
        list.__init__(self, items[-5:])

    def insert(self, i, x):
        list.insert(self, i, x)
        return self[-5:]

    def __getitem__(self, i):
        if i > 4:
           raise IndexError
        return list.__getitem__(self, i)

    def __setitem__(self, i, x):
        if 0<= i <= 4:
          return list.__setitem__(self, i, x)
        else:
          raise IndexError
Senthil Kumaran
  • 54,681
  • 14
  • 94
  • 131
  • The reason why I can't use a function that returns part of the list is because the list will over time get VERY large, and it will be holding lots of useless data that will never be used again. – lanrat May 10 '11 at 03:34
  • That can again be controlled by the function. if the grows large, shed the ones at the beginning. – Senthil Kumaran May 10 '11 at 03:55
  • The `return` in `insert()` is pointless, because `list.insert` is intended to operate in-place. – glglgl Oct 22 '12 at 11:30
-3

It can be as simple as the below solution

lst = []
arr_size = int(input("Enter the array size "))
while len(lst) != arr_size:
    arr_elem= int(input("Enter the array element "))
    lst.append(arr_elem)

sum_of_elements = sum(lst)

print("Sum is {0}".format(sum_of_elements))
ajknzhol
  • 6,322
  • 13
  • 45
  • 72