Ver índice de contenidos del libro

7.2. Listas

Presentaremos ahora una nueva estructura de datos: la lista. Usaremos listas para poder modelar datos compuestos pero cuya cantidad y valor varían a lo largo del tiempo. Son secuencias mutables y vienen dotadas de una variedad de operaciones muy útiles.

La notación para lista es una secuencia de valores encerrados entre corchetes y separados por comas. Por ejemplo, si representamos a los alumnos mediante su número de padrón, se puede tener una lista de inscritos en la materia como la siguiente: i [78455, 89211, 66540, 45750]. Al abrirse la inscripción, antes de que hubiera inscritos, la lista de inscritos se representará por una lista vacía: [].

7.2.1. Longitud de la lista. Elementos y segmentos de listas

Como a las secuencias ya vistas, a las listas también se les puede aplicar la función len() para conocer su longitud.

Para acceder a los distintos elementos de la lista se utilizará la misma notación de índices de cadenas y tuplas, con valores que van de 0 a la longitud de la lista −1.

>>> xs=[78455, 89211, 66540, 45750]
>>> xs[0]
78455
>>> len(xs)
4
>>> xs[4]
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> xs[3]
45750

Para obtener una sublista a partir de la lista original, se utiliza la notación de rangos, como en las otras secuencias.

Para obtener la lista que contiene sólo a quién se inscribió en segundo lugar podemos escribir:

>>> xs[1:2]
[89211]

Para obtener la lista que contiene al segundo y tercer inscritos podemos escribir:

>>> xs[1:3]
[89211, 66540]

Para obtener la lista que contiene al primero y segundo inscritos podemos escribir:

>>> xs[:2]
[78455, 89211]

7.2.2. Cómo mutar listas

Dijimos antes que las listas son secuencias mutables. Para lograr la mutabilidad Python provee operaciones que nos permiten cambiarle valores, agregarle valores y quitarle valores.

Para cambiar una componente de una lista, se selecciona la componente mediante su índice y se le asigna el nuevo valor:

>>> xs[1]=79211
>>> xs
[78455, 79211, 66540, 45750]

Para agregar un nuevo valor al final de la lista se utiliza la operación append(). Escribi-mosxs.append(47890)para agregar el padrón 47890 al final dexs`.

>>> xs.append(47890)
>>> xs
[78455, 79211, 66540, 45750, 47890]
>>>

Para insertar un nuevo valor en la posición cuyo índice es k (y desplazar un lugar el resto de la lista) se utiliza la operación insert().

Escribimos xs.insert(2, 54988) para insertar el padrón 54988 en la tercera posición de xs.

>>> xs.insert(2, 54988)
>>> xs
[78455, 79211, 54988, 66540, 45750, 47890]

Nota Las listas no controlan si se insertan elementos repetidos, si necesitamos exigir unicidad, debemos hacerlo mediante el código de nuestros programas.

>>> xs.insert(1,78455)
>>> xs
[78455, 78455, 79211, 54988, 66540, 45750, 47890]

Para eliminar un valor de una lista se utiliza la operación remove().

Escribimos xs.remove(45750) para borrar el padrón 45750 de la lista de inscritos:

>>> xs.remove(45750)
>>> xs
[78455, 78455, 79211, 54988, 66540, 47890]

Si el valor a borrar está repetido, se borra sólo su primera aparición:

>>> xs.remove(78455)
>>> xs
[78455, 79211, 54988, 66540, 47890]

Advertencia Si el valor a borrar no existe, se produce un error:

>>> xs.remove(78)
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list

7.2.3. Cómo buscar dentro de las listas

Queremos poder formular dos preguntas más respecto de la lista de inscritos:

  • ¿Está la persona cuyo padrón es v inscripta en esta materia?
  • ¿En qué orden se inscribió la persona cuyo padrón es v?.

Veamos qué operaciones sobre listas se pueden usar para lograr esos dos objetivos:

Para preguntar si un valor determinado es un elemento de una lista usaremos la operación in:

>>> xs
[78455, 79211, 54988, 66540, 47890]
>>> 78 in xs
False
>>> 66540 in xs
True
>>>

Nota Esta operación se puede utilizar para todas las secuencias, incluyendo tuplas y cadenas

Para averiguar la posición de un valor dentro de una lista usaremos la operación index().

>>> xs.index(78455)
0
>>> xs.index(47890)
4

Advertencia Si el valor no se encuentra en la lista, se producirá un error:

>>> xs.index(78)
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
ValueError: list.index(x): x not in list

Si el valor está repetido, el índice que devuelve es el de la primera aparición:

>>> ys=[10,20,10]
>>> ys.index(10)
0

Nota Esta operación está disponible en cadenas, pero no en tuplas.

Para iterar sobre todos los elementos de una lista usaremos una construcción for:

>>> zs = [5, 3, 8, 10, 2]
>>> for x in zs:
...     print x
...
5
3
8
10
2

Nota Esta construcción se puede utilizar sobre cualquier secuencia, incluyendo tuplas y cadenas.

En Python, las listas, las tuplas y las cadenas son parte del conjunto de las secuencias. Todas las secuencias cuentan con las siguientes operaciones:

Operación Resultado
x in s Indica si la variable x se encuentra en s
s + t Concantena las secuencias s y t
s * n Concatena n copias de s
s[i] Elemento i de s, empezando por 0
s[i:j] Porción de la secuencia s desde i hasta j (no inclusive)
s[i:j:k] Porción de la secuencia s desde i hasta j (no inclusive), con paso k
len(s) Cantidad de elementos de la secuencia s
min(s) Mínimo elemento de la secuencia s
max(s) Máximo elemento de la secuencia s

Problema 7.1. Queremos escribir un programa que nos permita armar la lista de los inscritos de una materia.

1. Análisis: El usuario ingresa datos de padrones que se van guardando en una lista.

2. Especificación: El programa solicitará al usuario que ingrese uno a uno los padrones de los inscritos. Con esos números construirá una lista, que al final se mostrará.

3. Diseño: ¿Qué estructura tiene este programa? ¿Se parece a algo conocido?

Es claramente un ciclo en el cual se le pide al usuario que ingrese uno a uno los padrones de los inscritos, y estos números se agregan a una lista. Y en algún momento, cuando se terminaron los inscritos, el usuario deja de cargar.

¿El ciclo es definido o indefinido?

Para que fuera un ciclo definido deberíamos contar de antemano cuántos inscritos tenemos, y luego cargar exactamente esa cantidad, pero eso no parece muy útil.

Estamos frente a una situación parecida al problema de la lectura de los números, en el sentido de que no sabemos cuántos elementos queremos cargar de antemano. Para ese problema, en 5.3, vimos una solución muy sencilla y c6moda: se le piden datos al usuario y, cuando se cargaron todos los datos se ingresa un valor distinguido (que se usa sólo para indicar que no hay más información). A ese diseño lo hemos llamado ciclo con centinela y tiene el siguiente esquema:

  • Pedir datos.
  • Mientras el dato pedido no coincida con el centinela:
    • Realizar cálculos.
    • Pedir datos.

Como sabemos que los números de padrón son siempre enteros positivos, podemos considerar que el centinela puede ser cualquier número menor o igual a cero. También sabemos que en nuestro caso tenemos que ir armando una lista que inicialmente no tiene ningún inscripto.

Modificamos el esquema anterior para ajustarnos a nuestra situación:

  • La lista de inscritos es vacía.
  • Pedir padrón.
  • Mientras el padrón sea positivo:
    • Agregar el padrón a la lista.
    • Pedir padrón.

2. Implementación: De acuerdo a lo diseñado en el párrafo anterior, el programa quedaría como se muestra en el código 7.1.

# inscritos.py: Permite ingresar padrones de alumnos inscritos
#!/usr/bin/env python*
# encoding: latin1*
""" Módulo para inscribir alumnos al curso - versión 0 """
 
# Iniciamos la interacción con el usuario*
print "Inscripcion en el curso 04 de 75.40"
 
# Leemos el primer padrón*
padron=input("Ingresa un padrón (<=0 para terminar): ")
 
# Procesamos los padrones*
# Inicialmente no hay inscritos*
ins = []
while padron > 0:
   # Agregamos el padrón leído a la lista de inscritos*
   ins.append(padron)
 
   # Leemos otro padrón más*
   padron=input("Ingresa un padrón (<=0 para terminar): ")
 
# Mostramos el resultado*
print "Esta es la lista de inscritos: ", ins

5. Prueba: Para probarlo lo ejecutamos con algunos lotes de prueba (inscripción de tres alumnos, inscripción de cero alumnos, inscripción de alumnos repetidos):

Inscripción en el curso 04 de 75.40
Ingresa un padrón (<=0 para terminar): 30
Ingresa un padrón (<=0 para terminar): 40
Ingresa un padrón (<=0 para terminar): 50
Ingresa un padrón (<=0 para terminar): 0
Esta es la lista de inscritos:  [30, 40, 50]
>>>
Inscripción en el curso 04 de 75.40
Ingresa un padron (<=0 para terminar): 0
Esta es la lista de inscritos:  []
>>>
Inscripción en el curso 04 de 75.40
Ingresa un padron (<=0 para terminar): 30
Ingresa un padron (<=0 para terminar): 40
Ingresa un padron (<=0 para terminar): 40
Ingresa un padron (<=0 para terminar): 30
Ingresa un padron (<=0 para terminar): 50
Ingresa un padron (<=0 para terminar): 0
Esta es la lista de inscritos:  [30, 40, 40, 30, 50]

Evidentemente el programa funciona de acuerdo a lo especificado, pero hay algo que no tuvimos en cuenta: permite inscribir a una misma persona más de una vez.

6. Mantenimiento: No permitir que haya padrones repetidos.

7. Diseño revisado: Para no permitir que haya padrones repetidos debemos revisar que no exista el padrón antes de agregarlo en la lista:

  • La lista de inscritos es vacía.
  • Pedir padrón.
  • Mientras el padrón sea positivo:
    • Si el padrón no está en la lista, agregar el padrón a la lista.
    • Pero si está en la lista, avisar que el padrón ya está en la lista.
    • Pedir padrón.

8. Nueva implementación: De acuerdo a lo diseñado en el párrafo anterior, el programa ahora quedaría como se muestra en el código 7.2.

9. Nueva prueba: Para probarlo lo ejecutamos con los mismos lotes de prueba anteriores (inscripción de tres alumnos, inscripción de cero alumnos, inscripción de alumnos repetidos):

Inscripción en el curso 04 de 75.40
Ingresa un padrón (<=0 para terminar): 30
Ingresa un padrón (<=0 para terminar): 40
Ingresa un padrón (<=0 para terminar): 50
Ingresa un padrón (<=0 para terminar): 0
Esta es la lista de inscritos: [30, 40, 50]
>>>
Inscripción en el curso 04 de 75.40
Ingresa un padrón (<=0 para terminar): 0
#inscritos.py: Permite ingresar padrones, sin repetir
 
#!/usr/bin/env python
# encoding: latin1
""" Módulo para inscribir alumnos al curso - versión 1 """
 
# Iniciamos la interacción con el usuario
print "Inscripcion en el curso 04 de 75.40"
 
# Leemos el primer padrón
padron=input("Ingresa un padrón (<=0 para terminar): ")
 
# Procesamos los padrones
# Inicialmente no hay inscritos
ins = []
while padron > 0:
   # Si todavía no está, agregamos el padrón a la lista de inscritos,
   if padron not in ins:
      ins.append(padron)
   # de lo contrario avisamos que ya figura
   else:
      print "Ya figura en la lista"
 
   # Leemos otro padrón mas
   padron=input("Ingresá un padrón (<=0 para terminar): ")
 
# Mostramos el resultado
print "Esta es la lista de inscritos: ", ins
Esta es la lista de inscriptos:  []
>>>
Inscripción en el curso 04 de 75.40
Ingresa un padrón (<=0 para terminar): 30
Ingresa un padrón (<=0 para terminar): 40
Ingresa un padrón (<=0 para terminar): 40
Ya figura en la lista
Ingresa un padrón (<=0 para terminar): 30
Ya figura en la lista
Ingresa un padrón (<=0 para terminar): 50
Ingresa un padrón (<=0 para terminar): 0
Esta es la lista de inscriptos:  [30, 40, 50]

Pero ahora el resultado es satisfactorio: no tenemos inscritos repetidos.

Ejercicio 7.7. Permitir que los alumnos se puedan inscribir o borrar.

Ejercicio 7.8. Inscribir y borrar alumnos como antes, pero registrar también el nombre y apellido de la persona inscripta, de modo de tener como lista de inscritos: [(20, "Ana", "García"), (10, "Juan", "Salas")].

Copyright (c) 2011-2014 Rosita Wachenchauzer, Margarita Manterola, Maximiliano Curia, Marcos Medrano, Nicolás Paez. La copia y redistribución de esta página se permite bajo los términos de la licencia Creative Commons Atribución - Compartir Obras Derivadas Igual 3.0 siempre que se conserve esta nota de copyright.