How to force a list to a fixed size?

Question:

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.

Asked By: lanrat

||

Answers:

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)
Answered By: lambacck

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']
>>> 
Answered By: John La Rooy

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
Answered By: Senthil Kumaran

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()
Answered By: Julio

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
Answered By: litepresence

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))
Answered By: ajknzhol

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.

Answered By: ajsp
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.