Programación con Python: Listas

Las listas en Python son una estructura de datos muy versátil que permite almacenar y manipular colecciones de elementos. Son secuencias ordenadas y modificables, y pueden contener elementos de diferentes tipos. A continuación mostraremos las principales operaciones que podemos realizar con listas en Python, e incluiremos explicaciones y ejemplos prácticos.

Creación de listas

Podemos crear listas en Python encerrando los elementos entre corchetes []:

# Lista vacía
lista_vacia = []

# Lista de números enteros
numeros_enteros = [1, 2, 3, 4, 5]

# Lista de números flotantes
numeros_flotantes = [1.5, 2.25, 3.75]

# Lista de cadenas de texto
nombres = ["Ana", "Carlos", "María"]

# Lista con elementos de diferentes tipos
mixta = [10, "Python", True, 3.14]

Acceso a elementos

Podemos acceder a los elementos de una lista utilizando índices, teniendo en cuenta que el primer elemento tiene un índice de 0, y el último de -1:

numeros = [10, 20, 30, 40, 50]

primer_elemento = numeros[0]   # 10
segundo_elemento = numeros[1]  # 20
ultimo_elemento = numeros[-1]  # 50 (índice negativo para acceder al último elemento)

Modificación de elementos

Podemos modificar los elementos de una lista asignando nuevos valores a través de sus índices:

nombres = ["Ana", "Carlos", "María"]

nombres[1] = "Juan"  # Modificar el segundo elemento de la lista

print(nombres)  # ["Ana", "Juan", "María"]

Operaciones con listas

Longitud de la lista

Podemos obtener la longitud de una lista utilizando la función len():

numeros = [1, 2, 3, 4, 5]
longitud = len(numeros)  # 5

Concatenación de listas

Podemos concatenar dos o más listas utilizando el operador +:

lista1 = [1, 2, 3]
lista2 = [4, 5, 6]
concatenada = lista1 + lista2  # [1, 2, 3, 4, 5, 6]

Repetición de listas

Podemos repetir una lista utilizando el operador *:

lista = [1, 2, 3]
repetida = lista * 3  # [1, 2, 3, 1, 2, 3, 1, 2, 3]

Añadir elementos a una lista

Podemos añadir elementos a una lista utilizando los métodos append() e insert():

frutas = ["manzana", "plátano"]

# Método append(): añade un elemento al final de la lista
frutas.append("naranja")  # ["manzana", "plátano", "naranja"]

# Método insert(): inserta un elemento en una posición específica de la lista
frutas.insert(1, "pera")  # ["manzana", "pera", "plátano", "naranja"]

Eliminar elementos de una lista

Podemos eliminar elementos de una lista utilizando los métodos remove() y pop(), y también mediante una sentencia del:

nombres = ["Ana", "Carlos", "María"]

# Método remove(): elimina el primer elemento que coincida con el valor proporcionado
nombres.remove("Carlos")  # ["Ana", "María"]

# Método pop(): elimina el elemento en la posición dada (o el último si no se proporciona índice)
nombres.pop(0)  # ["María"]

# Sentencia del: elimina el elemento en la posición dada (o toda la lista si no se proporciona índice)
del nombres[0]  # []

Búsqueda en listas

Podemos buscar elementos en una lista utilizando el operador in:

numeros = [1, 2, 3, 4, 5]

existe_3 = 3 in numeros  # True
existe_6 = 6 in numeros  # False

Comprobar si una lista está vacía

Podemos comprobar si una lista está vacía simplemente usando el valor booleano de la lista. En Python, una lista vacía se evalúa como False, mientras que una lista con elementos se evalúa como True:

lista_vacia = []
lista_no_vacia = [1, 2, 3]

if not lista_vacia:
    print("Esta lista está vacía.")

if lista_no_vacia:
    print("Esta lista contiene elementos.")

Comprobar si un elemento existe en una lista

Podemos utilizar el operador in para comprobar si un elemento está presente en una lista:

numeros = [1, 2, 3, 4, 5]

if 3 in numeros:
    print("El número 3 está en la lista.")

Comparación de listas

Podemos comparar listas para verificar si son iguales o diferentes:

lista1 = [1, 2, 3]
lista2 = [1, 2, 3]
lista3 = [4, 5, 6]

print(lista1 == lista2)  # True (misma estructura y elementos)
print(lista1 == lista3)  # False (elementos diferentes)

Troceado de listas (slicing)

Podemos obtener sublistas (slices) de una lista utilizando la sintaxis [inicio:fin] teniendo en cuenta que el índice de inicio sí está incluido, pero el de fin no:

numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print(numeros[2:6])  # [3, 4, 5, 6]
print(numeros[2:])   # [3, 4, 5, 6, 7, 8, 9, 10]
print(numeros[:-2])  # [1, 2, 3, 4, 5, 6, 7, 8]

También disponemos de funcionalidades más avanzadas, como el paso o la asignación de valores con la sintaxis [inicio:fin:paso]:

numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Obtener una sublista con paso negativo (se recorre en orden inverso)
sub_lista_inversa = numeros[::-1]  # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

# Obtener una sublista con paso positivo (obtener los números pares)
pares = numeros[1::2]  # [2, 4, 6, 8, 10]

# Modificar valores usando slicing
numeros[1:4] = [20, 30, 40]  # [1, 20, 30, 40, 5, 6, 7, 8, 9, 10]

# Eliminar elementos usando slicing (reemplazar con una lista vacía)
numeros[1:4] = []  # [1, 5, 6, 7, 8, 9, 10]

Listas anidadas

En Python podemos tener listas dentro de listas, lo que se conoce como listas anidadas:

lista_anidada = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

print(lista_anidada[1][2])  # 6 (tercer elemento de la segunda lista)

Copia de listas

Debemos tener en cuenta que al asignar una lista a otra variable, ambas variables apuntan a la misma lista en memoria. Si modificamos una de ellas, la otra también cambiará. Para crear una copia independiente de una lista, podemos utilizar el método copy() o la notación de slicing:

original = [1, 2, 3]

# Copia utilizando copy()
copia1 = original.copy()

# Copia utilizando slicing
copia2 = original[:]

# Modificar la copia independiente
copia1[0] = 100

print(original)  # [1, 2, 3]
print(copia1)    # [100, 2, 3]
print(copia2)    # [1, 2, 3]

Como se puede observar en el ejemplo, la modificación de copia1 no afecta a la lista original, ya que son listas diferentes en memoria. Sin embargo, debemos tener en cuenta que si la lista contiene objetos mutables (como listas anidadas o diccionarios), las copias independientes todavía pueden compartir referencias a esos objetos internos. Por lo tanto, si modificamos un objeto mutable dentro de una lista copiada, ese cambio se reflejará en ambas listas.

Listas y bucles

Las listas son especialmente útiles cuando se combinan con bucles para procesar múltiples elementos de manera eficiente.

Bucle «for» con listas

Este bucle for recorre la lista numeros y muestra cada elemento en la consola:

numeros = [1, 2, 3, 4, 5]

for numero in numeros:
    print(numero)

Bucle «for» con índices y enumeración

A veces resulta útil acceder tanto al índice como al elemento en cada iteración. Podemos usar la función enumerate() para obtener ambos:

frutas = ["manzana", "plátano", "naranja"]

for indice, fruta in enumerate(frutas):
    print(f"Índice: {indice}, Fruta: {fruta}")

Bucle «while» con listas

El bucle while se puede usar para recorrer una lista hasta que se cumpla una condición:

numeros = [1, 2, 3, 4, 5]
indice = 0

while indice < len(numeros):
    print(numeros[indice])
    indice += 1

Comprensión de listas

Las comprensiones de listas nos proporcionan una forma muy concisa y legible para crear listas basadas en una expresión y un bucle for:

# Crear una lista de cuadrados de los números del 1 al 5
cuadrados = [numero ** 2 for numero in range(1, 6)]  # [1, 4, 9, 16, 25]

# Crear una lista solo con números pares del 1 al 10
pares = [numero for numero in range(1, 11) if numero % 2 == 0]  # [2, 4, 6, 8, 10]

También podemos utilizar comprensión avanzada para realizar tareas más complejas:

# Obtener todos los números impares mayores que 10 en una lista
numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
impares_mayores_que_10 = [numero for numero in numeros if numero > 10 and numero % 2 != 0]

# Crear una matriz de ceros de 3x3 utilizando comprensión de listas anidadas
matriz_ceros = [[0 for _ in range(3)] for _ in range(3)]

Funciones y métodos útiles para listas

Python proporciona diversas funciones y métodos que pueden resultar muy útiles para trabajar con listas:

numeros = [3, 1, 5, 2, 4]

# Ordenar la lista de forma ascendente
numeros.sort()  # [1, 2, 3, 4, 5]

# Ordenar la lista de forma descendente
numeros.sort(reverse=True)  # [5, 4, 3, 2, 1]

# Obtener el índice de la primera aparición de un elemento
indice = numeros.index(4)  # 1

# Contar el número de veces que aparece un elemento en la lista
apariciones = numeros.count(3)  # 1

# Obtener una copia ordenada de la lista sin modificar la original
lista_ordenada = sorted(numeros)  # [1, 2, 3, 4, 5]

# Limpiar la lista (eliminar todos los elementos)
numeros.clear()  # []

Función map()

La función map() se puede utilizar para aplicar una función a todos los elementos de una lista y devolver una nueva lista con los resultados:

def cuadrado(numero):
    return numero ** 2

numeros = [1, 2, 3, 4, 5]
cuadrados = list(map(cuadrado, numeros))  # [1, 4, 9, 16, 25]

También podemos utilizar comprensión de listas para lograr el mismo resultado de manera más concisa:

numeros = [1, 2, 3, 4, 5]
cuadrados = [numero ** 2 for numero in numeros]  # [1, 4, 9, 16, 25]

Función filter()

La función filter() se puede utilizar para filtrar elementos de una lista según una condición dada:

def es_par(numero):
    return numero % 2 == 0

numeros = [1, 2, 3, 4, 5, 6]
pares = list(filter(es_par, numeros))  # [2, 4, 6]

Nuevamente, podemos utilizar comprensión de listas para lograr el mismo resultado:

numeros = [1, 2, 3, 4, 5, 6]
pares = [numero for numero in numeros if numero % 2 == 0]  # [2, 4, 6]

Función sum()

La función sum() nos permite sumar todos los elementos de una lista numérica:

numeros = [1, 2, 3, 4, 5]
total = sum(numeros)  # 15

Función join()

La función join() nos permite unir elementos de una lista para formar una cadena de texto:

nombres = ["Ana", "Carlos", "María"]
cadena_unida = ", ".join(nombres)  # "Ana, Carlos, María"

Función zip()

La función zip() combina elementos de dos o más listas en tuplas:

nombres = ["Ana", "Carlos", "María"]
edades = [25, 30, 28]

combinado = list(zip(nombres, edades))
# [('Ana', 25), ('Carlos', 30), ('María', 28)]

Función sorted()

La función sorted() nos permite obtener una nueva lista ordenada a partir de una lista dada sin modificar la original:

numeros = [3, 1, 5, 2, 4]
numeros_ordenados = sorted(numeros)  # [1, 2, 3, 4, 5]

Función reversed()

La función reversed() nos permite obtener una nueva lista con sus elementos en orden inverso sin modificar la original:

numeros = [1, 2, 3, 4, 5]

for numero in reversed(numeros):
    print(numero) # [5, 4, 3, 2, 1]

for numero in numeros:
    print(numero) # [1, 2, 3, 4, 5]

Función enumerate()

La función enumerate() nos permite obtener tanto el índice como el elemento durante la iteración:

frutas = ["manzana", "plátano", "naranja"]

for indice, fruta in enumerate(frutas):
    print(f"Índice: {indice}, Fruta: {fruta}")

Método count()

El método count() nos permite contar cuántas veces aparece un elemento específico en una lista:

numeros = [1, 2, 2, 3, 4, 2]
contador = numeros.count(2)  # 3 (el número 2 aparece 3 veces en la lista)

Método index()

El método index() nos permite encontrar el índice de la primera aparición de un elemento en una lista:

numeros = [10, 20, 30, 40, 50]
indice = numeros.index(30)  # 2 (el número 30 está en el índice 2 de la lista)

Método clear()

El método clear() se utiliza para eliminar todos los elementos de una lista, dejándola vacía:

numeros = [1, 2, 3, 4, 5]
numeros.clear()  # []

Listas como pilas y colas

Podemos usar una lista para implementar una pila (LIFO) o una cola (FIFO):

# Implementación de una pila usando una lista
pila = []
pila.append(1)         # [1]
pila.append(2)         # [1, 2]
elemento = pila.pop()  # elemento = 2, pila = [1]

# Implementación de una cola usando una lista (menos eficiente)
cola = []
cola.append(1)          # [1]
cola.append(2)          # [1, 2]
elemento = cola.pop(0)  # elemento = 1, cola = [2]

Listas y mutabilidad

Las listas son mutables, lo que significa que podemos modificar sus elementos sin crear una nueva lista. Por ejemplo, podemos agregar, eliminar o actualizar elementos en una lista sin tener que crear una copia:

numeros = [1, 2, 3]
numeros[0] = 10  # [10, 2, 3]

numeros.append(4)  # Agregar un elemento al final: [10, 2, 3, 4]
numeros.insert(1, 5)  # Insertar un elemento en una posición: [10, 5, 2, 3, 4]

numeros.remove(2)  # Eliminar el elemento 2: [10, 5, 3, 4]
numeros.pop()  # Eliminar el último elemento: [10, 5, 3]

del numeros[1]  # Eliminar un elemento por índice: [10, 3]

numeros.extend([6, 7, 8])  # Extender la lista con otra: [10, 3, 6, 7, 8]

Como se observa en el ejemplo, podemos modificar los elementos de una lista directamente y realizar varias operaciones para agregar o eliminar elementos según sea necesario.

Clonación de listas

Si queremos crear una copia independiente de una lista, debemos asegurarnos de no hacer una asignación directa, ya que ambas variables apuntarían a la misma lista en memoria. Podemos utilizar el método copy() o la notación de slicing para clonar una lista:

original = [1, 2, 3]

# Clonar usando copy()
copia1 = original.copy()

# Clonar usando slicing
copia2 = original[:]

Ahora, las tres listas son independientes y cualquier modificación en una lista no afectará a las otras.

Conversiones de datos básicos a listas

A continuación veremos cómo realizar conversiones de diferentes tipos de datos básicos (cadenas y enteros por ejemplo) a listas en Python. En general, podremos convertir cualquier secuencia o estructura iterable en una lista utilizando la función list().

Conversión de cadenas de texto a lista

Podemos convertir una cadena de texto en una lista utilizando el método split(). Este método separa la cadena en subcadenas usando un delimitador específico y devuelve una lista con las subcadenas:

cadena = "Hola,esto,es,una,cadena"
lista = cadena.split(",")  # ['Hola', 'esto', 'es', 'una', 'cadena']

El delimitador que hemos utilizado en el ejemplo es la coma (,), pero podemos usar cualquier carácter como delimitador:

otra_cadena = "Esto-es-una-cadena-con-guiones"
otra_lista = otra_cadena.split("-")  # ['Esto', 'es', 'una', 'cadena', 'con', 'guiones']

Si no especificamos un delimitador en split(), se utilizará el espacio en blanco como delimitador por defecto:

texto = "Hola esto es una cadena"
palabras = texto.split()  # ['Hola', 'esto', 'es', 'una', 'cadena']

Conversión de caracteres de una cadena a lista

Si tenemos una cadena de texto y queremos utilizar cada carácter para formar una lista, podemos hacerlo utilizando la función list() o también con una comprensión de listas. En el siguiente ejemplo, iteramos por cada carácter en la cadena y creamos una lista con cada carácter como elemento:

cadena = "Hola"
lista1 = list(cadena)  # ['H', 'o', 'l', 'a']
lista2 = [caracter for caracter in cadena]  # ['H', 'o', 'l', 'a']

Conversión de valores numéricos a lista

Si queremos convertir un valor numérico en una lista, podemos hacerlo utilizando la función list() pasando el valor como un argumento iterable. En el siguiente ejemplo convertimos el número en una cadena de texto utilizando str(), y luego utilizamos list() para formar una lista con todos los dígitos:

numero = 12345
lista = list(str(numero))  # ['1', '2', '3', '4', '5']

Conversión de rangos a lista

Los rangos en Python también se pueden convertir a listas utilizando la función list(). Por ejemplo, podemos crear un rango del 1 al 5 y luego convertirlo en una lista:

rango = range(1, 6)
lista = list(rango)  # [1, 2, 3, 4, 5]

Listas, funciones y conjuntos

A continuación enumeramos algunas ventajas adicionales asociadas al uso de funciones y otros tipos de estructuras de datos que veremos de forma detallada en otras unidades. Hemos creído conveniente exponerlas ahora para ser conscientes del gran potencial que presentan las listas de Python.

Listas como argumentos de funciones

Las listas se pueden utilizar como argumentos de funciones, lo que nos proporciona la funcionalidad necesaria para pasar múltiples elementos a una función agrupándolos en una sola variable. Además, esto nos permitirá por ejemplo realizar cambios en los elementos de la lista dentro de la función:

def duplicar_elementos(lista):
    for i in range(len(lista)):
        lista[i] *= 2

numeros = [1, 2, 3, 4, 5]
duplicar_elementos(numeros)

print(numeros)  # [2, 4, 6, 8, 10]

Listas y funciones lambda

Las funciones lambda son funciones anónimas que se pueden usar en combinación con las funciones map(), filter(), y otras:

numeros = [1, 2, 3, 4, 5]

# Elevar al cuadrado cada número usando una función lambda con map()
cuadrados = list(map(lambda x: x ** 2, numeros))  # [1, 4, 9, 16, 25]

# Filtrar solo los números pares usando una función lambda con filter()
pares = list(filter(lambda x: x % 2 == 0, numeros))  # [2, 4]

Listas y operaciones con conjuntos

Podemos realizar operaciones de conjuntos (unión, intersección, diferencia) con listas utilizando el tipo de dato set:

lista1 = [1, 2, 3]
lista2 = [3, 4, 5]

# Unión de listas
union = list(set(lista1) | set(lista2))  # [1, 2, 3, 4, 5]

# Intersección de listas
interseccion = list(set(lista1) & set(lista2))  # [3]

# Diferencia entre listas (elementos que están en lista1 pero no en lista2)
diferencia = list(set(lista1) - set(lista2))  # [1, 2]

Debemos matizar que el tipo de dato set no mantiene el orden original de los elementos, por lo que al convertirlo de vuelta a una lista, el orden puede variar.