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.