Python, forzando una lista a un tamaño fijo


83

En Python (3) quiero crear una lista que contendrá las últimas 5 variables ingresadas. Aquí hay un ejemplo:

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

Entonces, en Python, ¿hay alguna forma de lograr lo que se demostró anteriormente? La variable no necesita ser una lista, solo la usé como ejemplo.

¡Gracias!

Respuestas:


145

Es posible que desee utilizar un objeto collections.deque con el argumento constructor maxlen en su lugar:

>>>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, bueno: estaba a punto de sugerir la lista de subclases ala gnibbler, pero sospechaba que podría haber una solución prediseñada.
remitente

¿Cómo Python implementa la solución? ¿Aparece deque el elemento izquierdo cuando se agrega un nuevo elemento?
xiao 啸

Python tiene muchas estructuras de datos de lista que se pueden convertir en una lista cuando las necesite usando list (). Por ejemplo, haga una lista y pruebe la lista (MyDict).
Michael Dillon

1
@xiao es una cola de dos extremos, lo que significa que puede agregar de manera eficiente a cualquier extremo. De hecho, hay un método appendleft para agregar al frente de la deque. Si hay un maxlen presente y append / appendleft se repasará, se eliminará un elemento del otro extremo.
lambacck

1
Tenga en cuenta que esta solución es lenta para copias de fragmentos grandes, ya que es una lista doblemente vinculada, a diferencia de una simple listque es una matriz de CA.
Gulzar

14

Me encontré con este mismo problema ... maxlen = 5 de deque NO era una opción compatible debido a problemas de velocidad de acceso / confiabilidad.

Solución simple:

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

Después de agregar, simplemente redefina "l" como los cinco elementos más recientes de "l".

print(l)

Llámalo hecho.

Para sus propósitos, podría detenerse allí mismo ... pero necesitaba un popleft (). Mientras que pop () elimina un elemento de la derecha donde se acaba de agregar ... pop (0) lo elimina de la izquierda:

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

Punta de sombrero para James en Tradewave.net

No se necesitan funciones de clase ni deque.

Más ... para agregar a la izquierda y a la derecha:

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

Sería su equivalente de appendleft () si desea cargar su lista sin usar deque

Finalmente, si elige agregar desde la izquierda ...

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

Podrías subclase 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
También necesitaría extender los métodos insert, extendy setitem( l[1:1] = range(100)) para que esto sea infalible.
Lauritz V. Thaulow

1
Considere del self[0].
Alfe

1
y tal vez necesite anular __add__también
Lee

7

dequees lento para el acceso aleatorio y no admite la división. Siguiendo la sugerencia de gnibbler, armé un completolist subclase .

Sin embargo, está diseñado para "rodar" sólo de derecha a izquierda. Por ejemplo, insert()en una lista "completa" no tendrá ningún efecto.

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

Podría usar una colección limitada en PyMongo; es excesivo, pero hace bien el trabajo:

import pymongo

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

#do inserts ...

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

Por lo tanto, cada vez que llame my_capped_list, recuperará los últimos 5 elementos insertados.


0

La mayoría de las veces, cuando necesita este tipo de facilidad, debe escribir una función que toma la lista y luego devuelve los últimos cinco elementos.

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

Pero si realmente desea una lista personalizada, con un límite en cinco elementos, puede anular la lista incorporada y sus métodos, haría algo como esto, para todos sus métodos.

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

La razón por la que no puedo usar una función que devuelve parte de la lista es porque con el tiempo la lista se volverá MUY grande y contendrá muchos datos inútiles que nunca se volverán a usar.
lanrat

Eso puede ser controlado nuevamente por la función. si crece mucho, deshazte de los que están al principio.
Senthil Kumaran

El returnin no insert()tiene sentido, porque list.insertestá diseñado para operar en el lugar.
glglgl

-3

Puede ser tan simple como la siguiente solución

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))
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.