Saltar al contenido principal

Colecciones: listas, tuplas, diccionarios y conjuntos

En Python existen varias formas de agrupar datos: listas, tuplas, diccionarios y conjuntos. Cada una tiene sus propias características (orden, mutabilidad, forma de acceso) y resulta más adecuada que las demás según lo que necesites hacer con esos datos.

Las listas y los diccionarios son las dos colecciones más utilizadas. Una lista es una secuencia ordenada de elementos accesibles por posición (índice); un diccionario es una colección de pares clave-valor, accesibles por clave en lugar de por posición. Ambas son mutables: se pueden modificar después de crearlas (añadir, eliminar o cambiar elementos).

Listas

Del mismo modo que una cadena, una lista es una secuencia de valores. En una cadena, los valores son caracteres; en una lista, pueden ser de cualquier tipo. Los valores de una lista se llaman elementos.

Definición de listas

Hay varias formas de crear una nueva lista, la más simple es encerrar los elementos entre corchetes.

[10, 20, 30, 40]
['manuel', 'miguel', 'gonzalo']

El primer ejemplo es una lista de 4 enteros. La segunda es una lista de tres cadenas. Los elementos de una lista no tienen que ser del mismo tipo. La siguiente lista contiene una cadena, un flotante, un entero y otra lista:

['spam', 2.0, 5, [10, 20]]

Una lista que no contiene elementos se llama lista vacía. Puedes crear una con corchetes vacíos, []. Como puedes observar, puedes asignar los valores de una lista a variables:

quesos = ['Cheddar', 'Edam', 'Gouda']
numeros = [17, 123]
vacia = []
print(quesos, numeros, vacia) # Imprime: ['Cheddar', 'Edam', 'Gouda'] [17, 123] []

Mutabilidad de listas

La sintaxis para acceder a elementos de una lista es la misma que para acceder a los caracteres de una cadena: el operador corchete. La expresión dentro de los corchetes especifica el índice. Recordemos que los índices comienzan en 0.

print(quesos[0]) # Imprime: Cheddar

A diferencia de las cadenas, las listas son mutables porque pueden cambiar el orden de los elementos de una lista o reasignar un elemento en una lista. Cuando el operador corchete aparece en el lado izquierdo de una asignación, este identifica el elemento de la lista que será asignado:

numeros = [17, 123]
numeros[1] = 5
print(numeros) # Imprime: [17, 5]

El elemento en la posición uno de numeros, que era 123, ahora es 5.

Puedes pensar en una lista como una relación entre índices y elementos. Esta relación se llama mapeo. Cada índice mapea a uno de los elementos.

Los índices en una lista funcionan de la misma manera que los índices de una cadena.

Alias de mutables

Si igualamos una lista a otra, no se copia el contenido de una a otra, sino que se crea un alias. Esto ocurre para todos los tipos de datos que son mutables.

Veamos un ejemplo:

lista_original = [1, 2, 3]
alias_lista = lista_original

alias_lista.append(4)
print(lista_original) # Salida: [1, 2, 3, 4]

Aquí alias_lista referencia al mismo objeto que lista_original, por lo que cualquier cambio en alias_lista también afectará a lista_original.

Copiar mutables

Si quieres realizar una copia de un tipo de datos mutable, como son las listas, puedes usar deepcopy() para crear una copia totalmente independiente:

import copy

lista_original = [1, 2, 3]
alias_lista = copy.deepcopy(lista_original) # Crea una copia completa

alias_lista.append(4)
print(lista_original) # Salida: [1, 2, 3]
print(alias_lista) # Salida: [1, 2, 3, 4]

Recorrer listas

La forma más común de recorrer los elementos de una lista es con un bucle for. La sintaxis es la misma que para cadenas:

for queso in quesos:
print(queso)

Esto funciona bien si solo necesitas leer los elementos de la lista. Pero si quieres escribir o actualizar los elementos, necesitas los índices. Una forma común de hacer esto es combinando las funciones range y len.

for i in range(len(numeros)):
numeros[i] = numeros[i] * 2

Este bucle recorre la lista y actualiza cada elemento. len devuelve el número de elementos de una lista. range devuelve una lista de índices desde 0 hasta n-1, donde n es la longitud de la lista. Cada vez que pasa a través del recorrido, i obtiene el índice del siguiente elemento. La sentencia de asignación dentro del bucle utiliza la variable i para leer el valor original del elemento y asignar un nuevo valor.

Un bucle for a través de una lista vacía nunca ejecuta el código contenido en el cuerpo:

for x in vacia:
print('Esto nunca sucede.')

Aunque una lista puede contener otra lista, las listas anidadas siguen contando como un solo elemento. El tamaño de esta lista es 4:

['spam', 1, ['Brie', 'Roquefort', 'Pol le Veq'], [1, 2, 3]]
Break, continue y else

Al igual que la cláusula while, podemos utilizar break, continue y else dentro de una iteración creada con for.

Enumerate

La función enumerate() en Python se utiliza para iterar sobre una secuencia y proporciona tanto el índice como el valor del elemento actual. La sintaxis es:

for indice, valor in enumerate(lista):
# Código que utiliza el índice y el valor

Ejemplo:

lista = ['a', 'b', 'c', 'd']

for indice, valor in enumerate(lista):
print(f"Índice: {indice}, Valor: {valor}")

El resultado será:

Índice: 0, Valor: a
Índice: 1, Valor: b
Índice: 2, Valor: c
Índice: 3, Valor: d

En cada iteración del bucle, enumerate() devuelve una tupla (índice, valor) correspondiente al elemento actual de la lista.

Operaciones con listas

Concatenación de listas

El operador + concatena listas:

a = [1, 2, 3]
b = [4, 5, 6]
c = a + b
print(c) # Imprime: [1, 2, 3, 4, 5, 6]

De igual manera, el operador * repite una lista un determinado número de veces:

[0] * 4 # [0, 0, 0, 0]
[1, 2, 3] * 3 # [1, 2, 3, 1, 2, 3, 1, 2, 3]

Comprobar si un valor está en la lista

El operador in también funciona en listas, y en este caso nos permite saber si un valor está en una lista:

quesos = ['Cheddar', 'Edam', 'Gouda']
'Edam' in quesos # Imprime: True
'Brie' in quesos # Imprime: False

Slicing

De igual manera que con las cadenas, se puede realizar slicing sobre listas.

Añadir nuevos elementos

El método append agrega un nuevo elemento al final de una lista:

t = ['a', 'b', 'c']
t.append('d')
print(t) # Imprime: ['a', 'b', 'c', 'd']

extend toma una lista como argumento y agrega todos los elementos:

t1 = ['a', 'b', 'c']
t2 = ['d', 'e']
t1.extend(t2)
print(t1) # Imprime: ['a', 'b', 'c', 'd', 'e']

Este ejemplo deja la lista t2 sin modificar.

También podemos usar el método insert. El primer parámetro indica la posición en la que se quiere añadir el elemento y el segundo parámetro es el elemento a añadir:

t1 = ['a', 'b', 'c']
t1.insert(1,"d")
print(t1) # Imprime: ['a', 'd', 'b', 'c']

Ordenar una lista

sort ordena los elementos de una lista de menor a mayor:

t = ['d', 'c', 'e', 'b', 'a']
t.sort()
print(t) # Imprime: ['a', 'b', 'c', 'd', 'e']

Eliminar elementos de una lista

Para eliminar elementos existen varias formas.

  • Si sabes el índice del elemento que quieres, puedes usar pop:

    t = ['a', 'b', 'c']
    x = t.pop(1)
    print(t) # Imprime: ['a', 'c']
    print(x) # Imprime: b

    pop modifica la lista y devuelve el elemento que fue removido. Si no proves el índice, la función elimina y retorna el último elemento.

    Si no necesitas el valor removido, puedes usar el operador del.

    t = ['a', 'b', 'c']
    del t[1]
    print(t) # Imprime: ['a', 'c']
  • Si sabes qué elemento quieres remover (pero no sabes el índice), puedes usar remove:

    t = ['a', 'b', 'c']
    t.remove('b')
    print(t) # Imprime: ['a', 'c']
  • Para eliminar más de un elemento, puedes usar del junto al índice:

    t = ['a', 'b', 'c', 'd', 'e', 'f']
    del t[1:5]
    print(t) # Imprime: ['a', 'f']
  • También podemos eliminar todos los elementos de una lista con el método clear:

    t = ['a', 'b', 'c', 'd', 'e', 'f']
    t.clear()
    print(t) # Imprime: []

Obtener el índice de un elemento

Para obtener el índice de un elemento en una lista en Python, puedes usar el método .index(). Este método devuelve el índice de la primera aparición del elemento en la lista. Aquí tienes un ejemplo:

lista = ['manzana', 'plátano', 'naranja', 'pera']
indice = lista.index('naranja')
print(indice)

En este ejemplo, el código imprimirá 2, que es el índice de la primera aparición de 'naranja' en la lista.

Error ValueError

Si el elemento no está en la lista, el método .index() lanzará un error ValueError. Para evitar esto, puedes comprobar primero si el elemento está en la lista usando el operador in:

elemento = 'naranja'

if elemento in lista:
indice = lista.index(elemento)
print(f'El índice de {elemento} es {indice}')
else:
print(f'{elemento} no está en la lista')

Este código comprobará si el elemento está en la lista antes de intentar obtener el índice.

List Comprehensions

Las List Comprehensions son una construcción concisa y poderosa que permite crear listas de manera elegante y eficiente. Proporciona una forma más compacta de expresar operaciones de bucle en una sola línea. La sintaxis básica es:

nueva_lista = [expression for element in iterable if condition]
  • expression: Es la expresión que se evalúa y agrega a la nueva lista.
  • element: Variable que toma cada valor del iterable.
  • iterable: La secuencia sobre la cual se itera.
  • condition (opcional): Una condición que filtra los elementos que se agregarán a la nueva lista.

Ejemplos

Agregar cuadrados de números pares:

numeros = [1, 2, 3, 4, 5, 6]
cuadrados_pares = [x**2 for x in numeros if x % 2 == 0]
print(cuadrados_pares) # Imprime: [4, 16, 36]

Lista de cuadrados hasta cierto límite:

limite = 5
cuadrados = [x**2 for x in range(limite)]
print(cuadrados) # Imprime: [0, 1, 4, 9, 16]

Tuplas

Las tuplas son un conjunto de datos ordenado e inmutable de elementos del mismo tipo o diferente.

Que una lista sea inmutable significa que los valores no pueden ser modificadas una vez declarados.

Una tupla se inicializa con paréntesis:

tupla = (1, 2, 3)
print(tupla) # Imprime: (1, 2, 3)

También pueden declararse sin (), separando por , todos los elementos.

tupla = 1, 2, 3
print(type(tupla)) # Imprime: <class 'tuple'>
print(tupla) # Imprime: (1, 2, 3)

Diccionarios

Un diccionario es como una lista, pero más general. En una lista, los índices de posiciones deben ser enteros; en un diccionario, los índices pueden ser de cualquier tipo.

Puedes pensar en un diccionario como una asociación entre un conjunto de índices (que se llaman claves) y un conjunto de valores. Cada clave apunta a un valor. La asociación de una clave y un valor se llama par clave-valor o elemento. Es un formato similar a un JSON.

Definición de diccionarios

La función dict crea un nuevo diccionario sin elementos. Debido a que dict es el nombre de una función interna, no deberías usarlo como nombre de una variable.

eng2gal = dict()
print(eng2gal) # Imprime: {}

Las llaves, {}, representan un diccionario vacío. También se puede crear un diccionario vacío de la siguiente manera:

eng2gal = {}

Puedes crear un nuevo diccionario con varios pares de clave-valor.

eng2gal = {'one': 'un', 'two': 'dous', 'three': 'tres'}

Agregar conjunto de pares clave-valor

Para agregar elementos a un diccionario, puedes utilizar corchetes:

eng2gal['one'] = 'un'

Esta línea crea un elemento asociando la clave one al valor un. Si imprimimos el diccionario de nuevo, veremos un par clave-valor con dos puntos entre la clave y el valor:

print(eng2gal) # Imprime: {'one':'un'}

Si en el diccionario ya existiera la clave one, se sobrescribiría el valor para esta clave.

Acceso a valores a través de su clave

Pero eso no es un problema porque los elementos de un diccionario nunca se indexan con índices enteros. En lugar de eso, utilizas las claves para encontrar los valores correspondientes:

print(eng2gal['two']) # Imprime: 'dous'

La clave "two" siempre se asocia al valor "dous", así que el orden de los elementos no importa.

Si la clave no está en el diccionario, obtendrás una excepción:

print(eng2gal['four']) # Se obtiene la excepción: KeyError: 'four'

Los diccionarios, al igual que las listas, son mutables. Si un diccionario se modifica dentro de una función, el contenido también se modifica en el resto del programa.

El método get() es una forma segura de obtener el valor asociado a una clave en un diccionario sin generar una excepción si la clave no está presente. La sintaxis básica es la siguiente:

valor = diccionario.get(clave, valor_por_defecto)
  • diccionario: el diccionario del cual quieres obtener el valor.
  • clave: la clave que estás buscando en el diccionario.
  • valor_por_defecto (opcional): es el valor que se devolverá si la clave no está presente en el diccionario. Si este argumento no se proporciona, None se utilizará como valor por defecto.

Ejemplo:

diccionario = {'a': 1, 'b': 2, 'c': 3}

valor = diccionario.get('b', 'No encontrado')
print(valor) # Imprime: 2

valor_no_encontrado = diccionario.get('z', 'No encontrado')
print(valor_no_encontrado) # Imprime: No encontrado

valor_no_encontrado = diccionario.get('z')
print(valor_no_encontrado) # Imprime: None

Acceso en f-strings

En Python, puedes acceder a los valores de un diccionario dentro de una f-string usando la misma sintaxis que usas fuera de ella, es decir, con corchetes [].

Si usas comillas simples ' dentro de una f-string entre comillas simples, puede haber conflicto. Por eso, es habitual usar comillas dobles para la cadena externa:

print(f"El nombre es {persona['nome']}")

También puedes utilizar get():

print(f"El nombre es {persona.get('nome', 'desconocido')}")

Operadores y métodos de dicionarios

Operadores para diccionarios

  • La función len() funciona en los diccionarios, esta devuelve el número de pares clave-valor:

    len(eng2sp) # 3
  • El operador in funciona en diccionarios, este te indica si algo aparece como una clave en el diccionario (aparecer como valor no es suficiente).

    'one' in eng2sp # True
    'uno' in eng2sp # False

Métodos de un diccionario

  • Para ver si algo aparece como valor en un diccionario, puedes usar el método values(), el cual retorna los valores como una lista:

    vals = list(eng2sp.values())
  • También se pueden obtener las claves con el método keys():

    claves = list(eng2sp.keys())
  • El método update() actualiza un diccionario con elementos de otro diccionario. Este método modifica el diccionario original agregando elementos del diccionario pasado como argumento. Si la clave ya existe, el valor se sobrescribe.

    diccionario1 = {'a': 1, 'b': 2}
    diccionario2 = {'b': 3, 'c': 4}

    diccionario1.update(diccionario2)

    print(diccionario1) # Salida: {'a': 1, 'b': 3, 'c': 4}
  • El método pop() elimina un elemento del diccionario especificando su clave. Devuelve el valor de dicha clave.

    diccionario = {'a': 1, 'b': 2, 'c': 3}
    valor_eliminado = diccionario.pop('b')
    print("Valor eliminado:", valor_eliminado) # Salida: 2
    print("Diccionario actualizado:", diccionario) # Salida: {'a': 1, 'c': 3}

Recorrer diccionarios

Iterar sobre claves

Si utilizas un diccionario como una secuencia para una sentencia for, esta recorre las claves del diccionario. Este bucle imprime cada clave y su valor correspondiente:

contadores = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}
for clave in contadores:
print(clave, contadores[clave])

Esto imprime:

jan 100
chuck 1
annie 42

De nuevo, las claves no están en ningún orden en particular. El bucle for itera a través de las claves del diccionario, así que debemos utilizar el operador índice para obtener el valor correspondiente para cada clave.

Iterar sobre pares clave-valor

Utilizando el método items() podemos iterar el diccionario obteniendo para cada clave su valor.

diccionario = {'a': 1, 'b': 2, 'c': 3}

for clave, valor in diccionario.items():
print(clave, valor)

Iterar sobre valores

Utilizando el método values() obtenemos una lista con los valores del diccionario. Podemos iterar sobre esta lista con la cláusula for:

diccionario = {'a': 1, 'b': 2, 'c': 3}

for valor in diccionario.values():
print(valor)

Dict Comprehension

Dict Comprehension es una construcción sintáctica que permite crear diccionarios de manera concisa y eficiente en una única línea de código. Es similar a List Comprehension, pero creando diccionarios en lugar de listas.

La estructura general de un Dict Comprehension es la siguiente:

{nueva_clave: nuevo_valor for elemento in iterable}
  • nueva_clave: la expresión determina la clave del nuevo par clave-valor.
  • nuevo_valor: la expresión que determina el valor asociado a la clave.
  • elemento: variable que toma los valores del iterable.
  • iterable: la secuencia sobre la que se está iterando.

Ejemplos:

  • Crear un diccionario de cuadrados de números

    cuadrados = {numero: numero**2 for numero in range(1, 6)}
    print(cuadrados) # Imprime: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

Representación de entidades

Se puede emplear un diccionario para representar una entidad modelando sus atributos y valores. En este caso, cada atributo de la entidad sería una clave, y el valor asociado sería el valor correspondiente.

Por ejemplo, imagina que quieres representar una entidad llamada "Persona" con los atributos nombre, edad y ciudad. Podrías hacerlo de la siguiente manera:

persona = {
"nombre": "Ana",
"edad": 30,
"ciudad": "Santiago"
}

Aquí, el diccionario persona tiene tres claves: "nombre", "edad", y "ciudad". Cada clave tiene su valor correspondiente.

También puedes representar una entidad dentro de otra utilizando diccionarios en Python, es decir, anidar diccionarios. Esto permite modelar relaciones más complejas, donde una entidad contiene otra como parte de sus atributos.

Por ejemplo, vamos a representar una entidad "Estudiante" que tiene un atributo "direccion", que a su vez es una entidad con varios atributos (calle, ciudad, código postal). Esto se hace creando un diccionario dentro de otro.

estudiante = {
"nombre": "Xoán",
"edad": 22,
"curso": "Ingeniería Informática",
"direccion": {
"calle": "Rúa das Camelias",
"ciudad": "A Coruña",
"codigo_postal": "15001"
}
}

En este ejemplo, el diccionario estudiante tiene como claves: "nombre", "edad", "curso" y "direccion". El valor de la clave "direccion" es un diccionario que contiene a su vez otras claves como "calle", "ciudad" y "codigo_postal".

Para acceder a valores dentro de diccionarios anidados, utilizas múltiples niveles de claves:

# Acceder al nombre del estudiante
print(estudiante["nombre"]) # Salida: Xoán

# Acceder a la ciudad de la dirección
print(estudiante["direccion"]["ciudad"]) # Salida: A Coruña

# Acceder al código postal de la dirección
print(estudiante["direccion"]["codigo_postal"]) # Salida: 15001

Conjuntos

El tipo set es un tipo de datos que permite representar conjuntos. Un conjunto es una colección desordenada de elementos únicos del mismo tipo o diferente, es decir, que no se repiten.

Se inicializan con llaves:

conjunto = {1, 3, 2, 9, 3, 1}
print(conjunto) # Imprime: {1, 3, 2, 9}

A diferencia de las tuplas, son mutables.

¿Cuándo usar cada colección?

Ya conocemos cuatro formas de agrupar datos en Python: lista, tupla, conjunto y diccionario. Elegir la adecuada depende de lo que necesites hacer con esos datos.

ColecciónOrdenadaMutableElementos repetidosAcceso
listPor índice
tupleNoPor índice
setNoNoPor pertenencia (in)
dictSí (desde Python 3.7)Claves no, valores síPor clave

Guía rápida:

  • Usa una lista cuando necesites una colección de elementos que pueda crecer, encogerse o reordenarse (el caso general por defecto).
  • Usa una tupla cuando los datos no deban cambiar una vez creados (por ejemplo, una coordenada (x, y)) o cuando quieras dejar claro que esa colección es "de solo lectura".
  • Usa un conjunto cuando lo único que importa es si un elemento está presente o no, sin repeticiones ni orden (por ejemplo, comprobar rápidamente si un valor ya ha aparecido).
  • Usa un diccionario cuando cada dato tenga una "etiqueta" natural con la que quieras acceder a él, en lugar de una posición (por ejemplo, representar los atributos de una entidad: {"nombre": "Ana", "edad": 20}).