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

Ви можете замість цього використовувати об'єкт collection.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, але я підозрював, що може бути заздалегідь створене рішення.
Сендерле

Як python реалізує рішення? Чи вискакує лівий елемент лівий елемент при додаванні нового елемента?
xiao 啸

Python має багато структур даних списку, які можна перетворити на список, коли вони вам потрібні, за допомогою list (). Наприклад, складіть дикт і спробуйте список (MyDict).
Майкл Діллон,

1
@xiao - це подвійна черга, що означає, що ви можете ефективно додати до будь-якого кінця. Насправді існує метод appendleft, який додається до передньої частини деке. Якщо присутній maxlen і додаток / додаток ліворуч буде переходити через один елемент, видаляється з іншого кінця.
lambacck

1
Зверніть увагу, що це рішення є повільним для копій великих шматків, оскільки це подвійно пов’язаний список, на відміну від простого listмасиву змінного струму.
Гюльзар

14

Я зіткнувся з цією ж проблемою ... maxlen = 5 від 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

Немає необхідності в функціях класу або deque.

Далі ... додати ліворуч та спливаюче праворуч:

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)) для цього , щоб бути надійними.
Lauritz V. Thaulow

1
Поміркуйте del self[0].
Алфе

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 вставлених елементів.


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

Причина, по якій я не можу використовувати функцію, яка повертає частину списку, полягає в тому, що з часом список стане ДУЖЕ великим, і він буде містити багато марних даних, які більше ніколи не використовуватимуться.
ланрат

Це знову може контролюватися функцією. якщо виростає велике, пролийте спочатку.
Сентіл Кумаран

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))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.