Datos estructurados

Listas

Las listas son probablemente la forma de estructurar datos más útiles y versátiles de Python. Una lista es una colección de elementos con las siguientes propiedades:

  • Las listas son ordenadas: Una lista no es solo una colección de objetos. El orden en el que especificamos los elementos cuando construimos una lista es una característica innata de esta construcción y se mantiene durante toda su vida mientras no aplicamos modificaciones.

  • Las listas pueden contener cualquier tipo de elemento: Incluso una colección de elementos de diferentes tipos, aunque no sea una opción demasiado recomendable.

  • Se puede acceder a los elementos de la lista mediante un índice: Se puede acceder a los elementos individuales de una lista mediante un índice que especificaremos entre corchetes después del nombre de la variable. La indexación de la lista comienza en el valor cero.

  • Las listas son mutables: Una vez creada, se pueden añadir, eliminar, cambiar y mover elementos. Python ofrece una amplia gama de operaciones que permiten modificar sus listas.

Una lista tiene la siguiente forma:

Lista

En lenguaje Python una lista se define de la siguiente manera:

[10]:
llista = ["foo", "bar", "baz", "qux", "quux", "corge"]

print(llista)
['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

Creación de una lista

Una lista vacía se puede crear de dos formas, usando una función llamada list:

ll = list()

La manera explícita que consiste en poner los dos corchetes sin ningún elemento:

lista2 = []

Las listas pueden tener un número muy grande, pero finito de elementos, tantos como la memoria del ordenador en el que estamos trabajando nos permita, o como hemos explicado antes de no tener ningún elemento, con lo que conseguimos una lista vacía.

[11]:
a = [0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
    20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
    40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
    60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

print(f'La llista a es: {a}')
La llista a es: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

Accediendo a los elementos de una lista

Se puede acceder a elementos individuales de una lista especificando su posición, también llamada índice, entre corchetes. Como ya hemos comentado en la parte introductoria del tema la indexación de listas comienza en el índice cero, esto significa que el primer elemento se encuentra en esta posición y el último en la posición \(n-1\) donde \(n\) es el número de elementos de la lista.

Veamos un pequeño ejemplo:

[12]:
ll = ["foo", "bar", "baz", "qux", "quux", "corge"]

# Ahora intentaremos obtener la información guardada en una posición de la lista

primera = ll[0]
print(primera)

tercera = ll[2]
print(tercera)
foo
baz

Para indexar también se pueden utilizar números negativos. El significado de utilizar un número negativo es que la indexación se hace del final de la lista en lugar de su principio. De esta forma tenemos que:

ll[-1] # indexa el último elemento de la lista.

Slicing

Python también permite una sintaxis de indexación avanzada que permite extraer sublistas de una lista. Esta técnica es conocida como slicing. Sea ll una variable que identifica una lista, una expresión de la forma ll[inicio:final] devuelve la porción de ll que comienza en la posición inicio, y termina en la posición final-1. Está en decir, la posición indexada por ‘final’ no está incluida en esta sub-lista.

En resumen, podemos realizar las siguientes selecciones:

a[inicio:final] # elementos de la lista de la posición inicio hasta la posición final-1.
a[inicio:] # elementos de la lista de la posición inicio hasta el final de la lista.
a[:final] # elementos de la lista de la primera posición hasta la posición final-1.
en[:] # seleccionamos toda la lista

Veamos un ejemplo de slicing en código Python:

[13]:
print("Lista entera:", end=" ")
print(ll)
print("Mi sub-lista:", end=" ")

sublista = ll[1:3]
print(sublista)
Lista entera: ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
Mi sub-lista: ['bar', 'baz']

A partir del conocimiento básico del slicing, podemos realizar selecciones más complejas añadiendo un último parámetro ll[inicio:final:incremento], este parámetro incremento indica el incremento de los índices a la hora de realizar la selección. Si no indicamos ningún incremento el valor por defecto es 1. Como ocurre con los índices inicio y final, el parámetro incremento también puede ser un número negativo. De esta forma, podremos hacer las siguientes selecciones:

a[::-1] # todos los elementos de la lista en orden inverso
a[1::-1] # los primeros dos elementos, en orden inverso
a[:-3:-1] # los dos últimos elementos, en orden inverso
a[-3::-1] # todos los elementos, excepto los dos últimos en orden inverso

A continuación tiene ejemplos de selección en código Python:

[14]:
lista = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print(lista[:5]) # Seleccionamos del primero hasta al 5o elemento
print(lista[5:]) # Seleccionamos del 5o elemento hasta al 5o final

# Se puede especificar cómo es el incremento de los índices

print(lista[1::3])

print("Soluciones")
# ¿Qué que puede dar ll[:]?
print(lista[:])

# ¿Cómo conseguiremos los elementos pares de la lista?
pares = lista[1::2]
print("Pares: " + str(pares))

# ¿Y los impares del 3 al 9?

senars_3_9 = lista[::2]
print(f'Impares del 3 al 9: {senars_3_9}')
[1, 2, 3, 4, 5]
[6, 7, 8, 9, 10]
[2, 5, 8]
Soluciones
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Pares: [2, 4, 6, 8, 10]
Impares del 3 al 9: [1, 3, 5, 7, 9]

Mutabilidad

Una vez que hemos creado una lista, podemos añadir, eliminar, cambiar y mover elementos a voluntad. Python ofrece una amplia gama de herramientas que nos permiten modificarlas.

Modificación de un valor

Podemos modificar o sustituir un único valor de una lista de forma muy similar a cómo modificamos una variable. En este caso debemos especificar cuál de los valores de la lista queremos modificar. Como podemos suponer, seleccionaremos el elemento mediante su índice.

[15]:
ll = [1, 2, 3, 4, 5]

print(f'Lista original " {ll}')
ll[0] = -33

print(f'Lista modificada en la primera posicion {ll}')

ll[-1] = 55
print(ll)
Lista original " [1, 2, 3, 4, 5]
Lista modificada en la primera posicion [-33, 2, 3, 4, 5]
[-33, 2, 3, 4, 55]

También podemos hacer selecciones de slices de la lista y asignarle múltiples elementos en una sola asignación usando listas del mismo tamaño.

[16]:
ll[0:2] = [33, 33]
print(ll)
[33, 33, 3, 4, 55]

Métodos que modifican una lista

Las operaciones que tenemos a continuación trabajan de una manera que no habíamos visto hasta ahora sobre ninguna variable de los nuestros programas. Estas operaciones no son funciones ni subprogramas, ya que modifican la lista sobre la que están actuando, los conocemos como métodos. De momento nos basta con ponerle nombre, pero un poco más adelante en este mismo tema volveremos a hablar de ello.

Estos métodos son:

  • append

  • extend

  • insert

  • remove

  • pop

Descripción de los métodos

Append: Método que recibe un elemento por parámetro y lo añade al final de la lista

Extend: Método que recibe una lista y la añade al final de la lista.

[17]:
a = [1, 2]
a.append(3)
print(a)
a.append(5)
[1, 2, 3]
[18]:
a.extend([5, 6])
print(a)

[1, 2, 3, 5, 5, 6]

Insert: Método que recibe un entero y un elemento. Añade el elemento a la posición seleccionada de la lista.

[19]:
# Del resultat de la operació extend, veiem que no tenim el nombre 4.
# usam la métode insert per afegir-ho
a.insert(3, 4)
print(a)
[1, 2, 3, 4, 5, 5, 6]

Remove: Método que recibe un elemento por parámetro y lo elimina de la lista. Si el elemento no existe, este método provocará un error en nuestro código.

[20]:
a.remove(1)
print(a)
[2, 3, 4, 5, 5, 6]
[21]:
# Observad que pasaría si tengo esta lista
b = [1,2,2,3,4,5,6,7]
b.remove(2)
print(b)
[1, 2, 3, 4, 5, 6, 7]
[22]:
# En último caso, intentamos eliminar un elemento que no existe
c = [1, 2, 3, 4, 5]
c.remove('a')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [22], in <cell line: 3>()
      1 # En último caso, intentamos eliminar un elemento que no existe
      2 c = [1, 2, 3, 4, 5]
----> 3 c.remove('a')

ValueError: list.remove(x): x not in list

Pop: Método que recibe un entero por parámetro y elimina el elemento que está indexado por ese entero. Si no especificamos ningún valor por parámetro, elimina el último elemento. Si el índice del elemento no existe, este método provocará un error en nuestro código.

[ ]:
# Volvemos a nuestra lista en, vamos a usar el método pop
a.pop(0)
print(a)
a.pop()
print(a)

En el siguiente enlace hay más información de cada uno de los métodos anteriores: documentación Python

Operadores y funciones

Python nos provee de toda una serie de operaciones que nos permiten obtener información de las listas, tenemos funciones ya programadas que nos permitirán ahorrar mucho trabajo.

Operadores

El operador in y el modificador not nos permiten saber si un elemento está o no está en nuestra lista. Estos dos métodos sirven, por ejemplo, para saber si un elemento está en una lista antes de eliminarlo.

[ ]:
separadores = [' ', ',', ';', '-']

x = ',' in separadores
print(x)

x = 'j' not in separadores
print(x)

También tenemos el operador de concatenación + y el de multiplicación *. * + Nos permite concatenar varias listas, tal y como lo hacemos con los Strings. * * Nos permite repetir varias veces una lista, crea una nueva.

Veremos su uso mediante ejemplos:

[ ]:
lista_a = [1, 2, 3]
lista_b = [4, 5, 6]

lista_c = [0] * 10
print(lista_c)

print(lista_a + lista_b)

Funciones sobre listas

Hay una serie de funciones ya definidas en Python que nos permiten obtener información de una lista.

  • len: nos devuelve un entero con la longitud de la lista.

  • min: nos devuelve el valor más pequeño de la lista.

  • max: nos devuelve el mayor valor de la lista.

[ ]:
coleccion = [1,2,3,9,5,6]

# Probamos la funcion longitud
longitud = len(coleccion)

print(coleccion)

# Probamos la funcion max
valor_maximo = max(coleccion)

print(valor_maximo)

Listas como parámetros de subprogramas

Debido a su mutabilidad, es decir, la capacidad de ser modificadas en tiempo de ejecución, las listas pasadas como parámetro de un subprograma tienen un comportamiento distinto al que tienen las variables que hemos empleado hasta ahora.

Si pasamos una lista como parámetro de un subprograma, sea un procedimiento o una función y hacemos cualquiera modificación, esta se verá reflejada en la variable del ámbito externo que ha sido usada en el paso de parámetro.

Vamos a ver un ejemplo:

[ ]:
"Función que recibe una lista y un elemento y añade el elemento al final de la lista"
def anyadir(lista, element):

    lista.append(element)

ll_mutable = []

print("Lista antes de llamar al método añade_elemento: ", ll_mutable)
anyadir(ll_mutable, 5)
print("Lista después de llamar al método añade_elemento:", ll_mutable)


Iterando sobre listas

Nosotros ya conocemos la operación de iteración, ahora que conocemos la existencia de listas veremos cómo podemos usar el operador for para recorrer las listas de forma automática. También cómo iterarlo mediante el acceso a su índice:

[ ]:
# Recorrer una lista obteniendo cada uno de sus elementos
llista_pobles = ["Arta", "Sineu", "Alcudia", "Mancor", "Valldemossa"]

for poble in llista_pobles: # el operador for nos devuelve cada uno de los elementos de la lista
    print(poble)
[ ]:
#Recorrer una lista con los índices para modificar valores
notas = [9, 4.5, 3.0, 7, 6.5, 3]
longitud = len(notas)

for i in range(0, longitud): # recuerde que la función range nos crea una lista
    print(i, notas[i])
    notas[i] = notas[i] + 1

print(notas)

También podemos usar la función enumerate para recorrer una lista y obtener los índices:

[ ]:

for idx, item in enumerate(notas): # mentre no final print(f'indice {idx} , item {item}')

Ejercicios

  1. Dada la siguiente lista, realiza las operaciones que siguen y verifica su corrección después de la ejecución mi_lista = [1,2,3,4,5,6,7]

1. Cambia el primer valor por 33.
2. Muestra su longitud por pantalla.
3. Muestra los 3 últimos valores.
4. Cambia el valor central por el valor -5.
5. Añade la lista [8, 9, 10] a mi lista.
6. Elimina el valor 3.
7. Verifica que ya no existe el valor 4.
8. Inserta el valor 10 después del valor 2.
  1. A partir de la lista siguiente a = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] escribe un programa que imprima todos los elementos de la lista que son menores de 5.

  2. Escribir un programa que dado un número añada todos los divisores de ese número en una lista y luego la muestre por pantalla.

  3. Dadas dos listas, digamos por ejemplo estas dos: a = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] y b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] y escribe una función que devuelva una lista que contenga solo los elementos que son comunes entre las listas (sin duplicados). Las listas no tienen por qué tener el mismo tamaño.

  4. Escribir una función para contar el número de strings con longitud 2 o más y el primer y último carácter son los mismos muestras = ['abc', 'xyz', 'aba', '1221'] Resultado esperado: 2

  5. Dada la lista b del ejercicio 3, elimina todos los números impares.

  6. Escribir una función que recibe dos listas por parámetro y devuelva True si tienen al menos un miembro común.

Tuplas

Python proporciona otro tipo de colección ordenada de objetos, llamada tupla. Las tuplas son idénticas a las listas en todos los aspectos, salvo las siguientes propiedades:

  • Las tuplas se definen especificando los elementos entre paréntesis en vez de corchetes.

  • Las tuplas son inmutables.

[ ]:
# Creación de una tupla y uso con funciones
vocales = ('a', 'e', 'i', 'o', 'u')

# Operadores
esta = 'u' in vocales
print(esta)

long = len(vocales)
print(long)

maxim = max(vocales)
print(maxim)

mi_tupla = (4,5)
a,b =  mi_tupla # desempaquetado
print(a)
print (b)
# Indexación
vocales[0] = 'A'

¿Por qué utilizar una tupla en lugar de una lista?

Cuando no queremos que los datos puedan modificarse. Si se pretende que los valores de la colección se mantengan constantes durante toda la vida del programa es necesario usar una tupla en lugar de una lista, ya que protegerá los datos contra una posible modificación accidental.

Las tuplas nos son muy prácticas para construir funciones que devuelven más de un valor, no es una práctica altamente recomendable, pero puede resultar muy útil en algunas situaciones.

Diccionarios

Un diccionario consiste en una colección no ordenada de pares de clave-valor. A diferencia de las secuencias, indexadas por un rango de números, los diccionarios son indexados por claves que pueden ser de cualquier tipo inmutable; los strings y los números siempre pueden ser claves. Las tuplas se pueden utilizar como claves. No podemos utilizar listas como claves, ya que las listas se pueden modificar (son mutables).

Lo mejor es pensar en las claves del diccionario como un conjunto, donde a cada elemento del conjunto le corresponde un valor.

A continuación describimos sus operaciones básicas:

Creación de un diccionario nuevo:

De forma muy similar a cómo lo hemos aprendido a hacer con las listas, podemos crear diccionarios usando la palabra reservada dict o de manera explícita usando corchetes:

[ ]:
dicc = dict() # Fixau-vos que aixo es el constructor de la classe diccionari
dicc2 = {}

También con valores ya definidos:

[ ]:
dicc = {43142512: "Joan Petit", 44216793: "Marina Aniram", 44444444: "Joan Petit"}
print(dicc)

Accediendo a los elementos de un diccionario

En esta estructura no accedemos a los elementos con un índice, sino que se realiza con el valor de una de sus claves:

[ ]:
nom = dicc[44444444]
print(nom)

diccionari_strings = {"biel": "professor", "arnau": "alumne"}
print(diccionari_strings["arnau"])

Modificación de un valor

Una vez que hemos creado un diccionario, podemos añadir, eliminar, cambiar y mover elementos a voluntad, ya que estos son una estructura mutable. Python ofrece una amplia gama de modos de operar con los diccionarios.

Un único valor de un diccionario se puede sustituir, de forma muy similar a cómo modificamos una variable, pero ahora hemos de especificar cuál de los valores queremos modificar, lo hacemos mediante su clave.

[ ]:
#Donada una clau podem modificar el seu valor
dicc[43142512] = "Matt Murdock"
nom = dicc[43142512]
print(nom)

Añadir información

También podemos añadir nuevas parejas clave-valor de forma dinámica.

[ ]:
# Insercion de nuevas parejas de datos clave- valor
dicc[43142133] = "Ororo Munroe"
dicc[41142133] = "Kurt Wagner"
dicc[46094245] = "Jean Gray"
dicc[40111134] = "Robert Drake"

print(dicc)

Operadores, funciones y métodos

Seguimos con la descripción escribiendo las operaciones que pueden realizarse con un diccionario.

Operadores

En los diccionarios también podemos aplicar el operador de pertenencia in y su modificador not:

[ ]:
# Consultam pertinença

dni = 40111134
print("Tenemos el dni", dni, "? ")

pertany = 40111134 in dicc
print(pertany)

print(dicc[dni])

Funciones

En los diccionarios también podemos usar la función len que nos dice cuántos elementos contiene el diccionario. También podemos usar las funciones max y min que nos devuelven información de las claves.

Métodos

Además de modificar y añadir elementos usando los corchetes también lo podemos hacer mediante métodos propios de los diccionarios. Además, usando los métodos adecuados podemos obtener la información del diccionario en forma de lista, sean: las claves, los valores o tuplas clave-valor:

Modificación

  • clear: Elimina todas las parejas clave-valor del diccionario.

  • get: Nos devuelve el valor de la clave que pasamos por parámetro, si la clave no existe, devuelve Error.

  • pop: Elimina la clave del diccionario y nos devuelve su valor, si no existe devuelve Error.

[ ]:
## get
clau = 40111134
valor =  dicc.get(clau)
print(valor)

# pop
#clau2 = 43142133
valor = dicc.pop(clau2)   # error si una clave no existe

print(valor)

nombre_elements = len(dicc)
print(nombre_elements)


# clear
#dicc.clear()
print(dicc)

Consulta

  • keys: método que nos devuelve una lista con el conjunto de claves que hay en el diccionario.

  • values: método que nos devuelve una lista con los valores que hay en el diccionario.

  • items: método que nos devuelve una lista de tuplas. Cada tupla tiene una clave y su correspondiente valor.

Es importante destacar 2 cosas:

  • Las listas que obtenemos de las operaciones anteriores, pueden no seguir el orden de inserción.

  • Obtener listas nos da la posibilidad de iterar sobre ellas usando un bucle definido for.

Para terminar

Las listas y diccionarios son dos de los tipos Python más usados. Como ha visto, tienen varias similitudes, pero difieren en cómo se accede a sus elementos. Recordamos que se accede a los elementos de las listas mediante un índice numérico en función del orden y en el diccionario se accede mediante claves.

Debido a esta diferencia, las listas y diccionarios son adecuados para circunstancias distintas.

Ejercicios

  1. Eliminar los nombres vacíos de la siguiente lista: nombres = ["", "jose", "juana", "luis" , "encarnacion", "", "pepa"] Ayuda: También podemos aplicar la función len a los strings, un string vacío tiene longitud 0.

  2. A partir de la lista del ejercicio anterior, queremos obtener un diccionario donde las claves son los nombres y los valores son los apellidos. apellidos = ["stark", "targaryen", "baratheon", "martillo", "lannister"]

  3. Combinar los siguientes diccionarios en uno solo: diccionario1 = {'a': 1, 'b': 2, 'c': 3} y diccionario2 = {'c': 4, 'd': 5, 'e': 6}.

  4. Encontrar la clave con el valor más alto en el siguiente diccionario: notas = {'Juan': 85, 'María': 92, 'Carlos': 78, 'Sofía': 95, 'Pedro': 88}

```