Python, wymuszając ustalony rozmiar listy

83

W Pythonie (3) chcę utworzyć listę, która będzie zawierała ostatnie 5 wprowadzonych do niej zmiennych. Oto przykład:

>>>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

Czy w Pythonie jest jakiś sposób na osiągnięcie tego, co pokazano powyżej? Zmienna nie musi być listą, użyłem jej tylko jako przykładu.

Dzięki!

lanrat
źródło

Odpowiedzi:

145

Możesz zamiast tego użyć obiektu collections.deque z argumentem konstruktora 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
lambacck
źródło
+1, fajnie - miałem zamiar zasugerować podklasy listy ala gnibbler, ale podejrzewałem, że może istnieć gotowe rozwiązanie.
senderle
Jak Python implementuje to rozwiązanie? Czy deque wyskakuje lewy element po dodaniu nowego elementu?
xiao 啸
Python ma wiele struktur danych listowych, które można przekształcić w listę, gdy ich potrzebujesz, używając list (). Na przykład stwórz dyktando i wypróbuj listę (MyDict).
Michael Dillon
1
@xiao jest to kolejka z podwójnym zakończeniem, co oznacza, że ​​możesz efektywnie dodawać do dowolnego końca. W rzeczywistości istnieje metoda appendleft, która ma być dołączona na początku deque. Jeśli maxlen jest obecny i append / appendleft przejdzie przez jeden element jest usuwany z drugiego końca.
lambacck
1
Proszę zauważyć, że to rozwiązanie jest powolne w przypadku kopii dużych fragmentów, ponieważ jest to lista podwójnie połączona, w przeciwieństwie do prostej listtablicy ac.
Gulzar,
14

Napotkałem ten sam problem ... maxlen = 5 z deque NIE był obsługiwaną opcją ze względu na problemy z szybkością dostępu / niezawodnością.

PROSTE ROZWIĄZANIE:

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

Po dołączeniu po prostu ponownie zdefiniuj „l” jako pięć ostatnich elementów „l”.

print(l)

Nazwij to gotowe.

Do swoich celów możesz zatrzymać się tutaj ... ale potrzebowałem popleft (). Podczas gdy pop () usuwa element z prawej strony, w którym został właśnie dodany ... pop (0) usuwa go z lewej strony:

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

Cynk kapelusza do Jamesa na Tradewave.net

Nie ma potrzeby stosowania funkcji klasowych ani deque.

Dalej ... aby dołączyć lewy i pop prawy:

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

Byłby twoim odpowiednikiem appendleft (), gdybyś chciał załadować listę z przodu bez użycia deque

Wreszcie, jeśli zdecydujesz się dołączyć od lewej ...

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
źródło
14

Możesz podklasy 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']
>>> 
John La Rooy
źródło
2
Można by też trzeba przedłużyć insert, extenda setitemmetody ( l[1:1] = range(100)) do tego, aby być niezawodne.
Lauritz V. Thaulow
1
Rozważ del self[0].
Alfe
1
i być może trzeba będzie __add__również zastąpić
Lee
7

dequejest powolny dla dostępu swobodnego i nie obsługuje dzielenia. Idąc za sugestią Gnibblera, stworzyłem kompletną listpodklasę.

Jednak jest przeznaczony do „przewijania” tylko od prawej do lewej. Na przykład insert()„pełna” lista nie będzie miała żadnego efektu.

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()
Julio
źródło
1

Możesz użyć limitowanej kolekcji w PyMongo - to przesada, ale dobrze spełnia swoje zadanie:

import pymongo

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

#do inserts ...

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

Dlatego za każdym razem, gdy zadzwonisz my_capped_list, odzyskasz ostatnie 5 wstawionych elementów.

ajsp
źródło
0

Najczęściej, gdy potrzebujesz takiego ułatwienia, pisze się funkcję, która pobiera listę, a następnie zwraca ostatnie pięć elementów.

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

Ale jeśli naprawdę chcesz mieć listę niestandardową, z ograniczeniem do pięciu elementów, możesz zastąpić listę wbudowaną i jej metody, zrobiłbyś coś takiego dla wszystkich metod.

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
źródło
Powodem, dla którego nie mogę użyć funkcji, która zwraca część listy, jest to, że lista z czasem stanie się BARDZO duża i będzie zawierać wiele bezużytecznych danych, które nigdy nie zostaną ponownie użyte.
lanrat
Można to ponownie kontrolować za pomocą funkcji. jeśli urosną, zrzuć te na początku.
Senthil Kumaran
returnW insert()nie ma sensu, ponieważ list.insertjest przeznaczony do pracy w miejscu.
glglgl
-3

Może to być tak proste, jak poniższe rozwiązanie

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
źródło