Python, принуждение списка к фиксированному размеру

83

В Python (3) я хочу создать список, который будет содержать последние 5 введенных в него переменных. Вот пример:

>>>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'] #only 5 items in list

Итак, в Python есть ли способ добиться того, что показано выше? Переменная не обязательно должна быть списком, я просто использовал ее в качестве примера.

Благодаря!

ланрат
источник

Ответы:

145

Вместо этого вы можете использовать объект collections.deque с аргументом конструктора maxlen:

>>>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)
deque(['orange','grape','banana','mango','kiwi'], maxlen=5) #only 5 items in list
ламбак
источник
+1, хорошо - я собирался предложить список подклассов ala gnibbler, но подозревал, что может быть готовое решение.
senderle
Как Python реализует решение? Выскакивает ли deque левый элемент при добавлении нового элемента?
сяо 啸
Python имеет множество структур данных-списков, которые можно превратить в список, когда они вам понадобятся, с помощью list (). Например, сделайте диктатор и попробуйте список (MyDict).
Майкл Диллон
1
@xiao - это двусторонняя очередь, что означает, что вы можете эффективно добавлять в любой конец. На самом деле существует метод appendleft для добавления к началу двухсторонней очереди. Если присутствует maxlen, и append / appendleft переместятся, один элемент будет удален с другого конца.
ламбак
1
Обратите внимание, что это решение работает медленно для копий больших фрагментов, поскольку это двусвязный список, в отличие от простого listмассива ac.
Гульзар
14

Я столкнулся с этой же проблемой ... maxlen = 5 from deque НЕ поддерживался из-за проблем со скоростью / надежностью доступа.

ПРОСТОЕ РЕШЕНИЕ:

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

После добавления просто переопределите «l» как последние пять элементов «l».

print(l)

Назовите это Готово.

Для ваших целей вы могли бы остановиться прямо здесь ... но мне нужен был popleft (). В то время как pop () удаляет элемент справа, где он был только что добавлен ... pop (0) удаляет его слева:

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

Совет от Джеймса на Tradewave.net

Нет необходимости в функциях класса или двухсторонней очереди.

Далее ... добавить влево и открыть вправо:

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

Будет вашим эквивалентом appendleft (), если вы захотите загрузить свой список без использования deque

Наконец, если вы выберете добавление слева ...

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
легкое присутствие
источник
14

Вы можете создать подкласс 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']
>>> 
Джон Ла Рой
источник
2
Вы бы также необходимо продлить insert, extendи setitemметоды ( l[1:1] = range(100)) для этого , чтобы быть надежными.
Лауриц В. Таулов,
1
Посмотрим del self[0].
Alfe
1
и , возможно , необходимо переопределить __add__также
Ли
7

dequeработает медленно для произвольного доступа и не поддерживает нарезку. Следуя предложению Гниблера, я собрал полный listподкласс.

Однако он предназначен для "прокрутки" только справа налево. Например, insert()на «полный» список не повлияет.

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()
Хулио
источник
1

Вы можете использовать ограниченную коллекцию в PyMongo - это излишне, но она отлично справляется со своей задачей:

import pymongo

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

#do inserts ...

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

Следовательно, каждый раз, когда вы звоните my_capped_list, вы получаете последние 5 вставленных элементов.

ajsp
источник
0

Чаще всего, когда вам нужно такое средство, вы должны написать функцию, которая берет список, а затем возвращает последние пять элементов.

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

Но если вам действительно нужен настраиваемый список, имеющий ограничение на пять элементов, вы можете переопределить встроенный список и его методы, вы бы сделали что-то вроде этого для всех его методов.

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
Сентил Кумаран
источник
Причина, по которой я не могу использовать функцию, возвращающую часть списка, заключается в том, что список со временем станет ОЧЕНЬ большим и будет содержать много бесполезных данных, которые никогда не будут использоваться снова.
lanrat
Это снова можно контролировать с помощью функции. если вырастет, избавьтесь от тех, что были в начале.
Сентил Кумаран
returnВ insert()бессмысленна, так как list.insertпредназначен для работы на месте.
glglgl
-3

Это может быть так же просто, как решение ниже

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))
Айкнжол
источник