Saltar a contenido

Listas

Fork (1)

  1. Mike Arney Unsplash

Las listas permiten almacenar objetos mediante un orden definido y con posibilidad de duplicados. Las listas son estructuras de datos mutables, lo que significa que podemos añadir, eliminar o modificar sus elementos.

Creando listas

Una lista está compuesta por cero o más elementos. En Python debemos escribir estos elementos separados por comas y dentro de corchetes.

Veamos algunos ejemplos de listas:

>>> languages = ['Python', 'Ruby', 'Javascript']#(1)!

>>> fibonacci = [0, 1, 1, 2, 3, 5, 8, 13]#(2)!

>>> empty_list = []#(3)!

>>> data = [#(4)!
...    'Tenerife',
...    {'cielo': 'limpio', 'temp': 24},
...    3718,
...    (28.2933947, -16.5226597)
... ]

  1. Una lista de 3 cadenas de texto.
  2. Una lista de 8 números enteros.
  3. La lista vacía (0 elementos).
  4. Una lista heterogénea de 4 elementos de distinta naturaleza.
Datos heterogéneos

Una lista en Python puede contener datos heterogéneos a diferencia de otros lenguajes de programación. Esto hace de la lista una estructura de datos muy versátil.

Ejercicio

Entra en el intérprete interactivo de Python ❯❯❯ y crea una lista con las 5 ciudades que más te gusten.

Conversión

Para convertir otros tipos de datos en una lista podemos usar la función list(). Por ejemplo podemos convertir una cadena de texto en una lista:

>>> list('Python')
['P', 'y', 't', 'h', 'o', 'n']

Si nos fijamos en lo que ha pasado, al convertir la cadena de texto Python se ha creado una lista con 6 elementos, donde cada uno de ellos representa un carácter de la cadena. Podemos extender este comportamiento a cualquier otro tipo de datos que permita ser iterado (iterables).

Otro ejemplo interesante de conversión puede ser la de los rangos. En este caso queremos obtener una lista explícita con los valores que constituyen el rango \([0,9]\):

>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Nombre de variable

Aunque está permitido, no suele ser una buena práctica llamar list a una variable ya que destruirías la función que nos permite trabajar con listas. Tampoco parece muy razonable utilizar nombres como <algo>_list o list_<algo> ya que no es necesario incluir en el nombre de una variable su propia naturaleza.

Operaciones con listas

Existen multitud de operaciones que se pueden realizar sobre listas. A continuación veremos la mayoría de ellas:

Obtener un elemento

Igual que en el caso de las cadenas de texto, podemos obtener un elemento de una lista a través del índice (lugar) que ocupa. Veamos un ejemplo:

>>> shopping = ['Agua', 'Huevos', 'Aceite']

>>> shopping[0]
'Agua'

>>> shopping[1]
'Huevos'

>>> shopping[2]
'Aceite'

>>> shopping[-1]#(1)!
'Aceite'

  1. ¡Aquí también funcionan los índices negativos!

El índice que usemos para acceder a los elementos de una lista tiene que estar comprendido entre los límites de la misma. Si usamos un índice antes del comienzo o después del final obtendremos un error (excepción):

>>> shopping = ['Agua', 'Huevos', 'Aceite']

>>> shopping[3]
Traceback (most recent call last):
  Cell In[2], line 1
    shopping[3]
IndexError: list index out of range

>>> shopping[-5]
Traceback (most recent call last):
  Cell In[3], line 1
    shopping[-5]
IndexError: list index out of range

Trocear una lista

El troceado de listas funciona de manera totalmente análoga al troceado de cadenas. Veamos algunos ejemplos:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping[:3]#(1)!
['Agua', 'Huevos', 'Aceite']

>>> shopping[2:4]
['Aceite', 'Sal']

>>> shopping[-1:-4:-1]
['Limón', 'Sal', 'Aceite']

>>> shopping[::-1]#(2)!
['Limón', 'Sal', 'Aceite', 'Huevos', 'Agua']

  1. También podríamos haber escrito shopping[0:3] aunque no es habitual.
  2. Equivale a invertir la lista.

En el troceado de listas, a diferencia de lo que ocurre al obtener elementos, no debemos preocuparnos por acceder a índices inválidos (fuera de rango) ya que Python los restringirá a los límites de la lista:

>>> shopping
['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping[10:]
[]

>>> shopping[-100:2]
['Agua', 'Huevos']

>>> shopping[2:100]
['Aceite', 'Sal', 'Limón']

Ninguna de las operaciones anteriores modifican la lista original, simplemente devuelven una lista nueva.

Invertir una lista

Python nos ofrece varios mecanismos para invertir los elementos de una lista, en función del resultado que busquemos:

  • Opcion A Mediante troceado de listas con «step» negativo:

    >>> shopping
    ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']
    
    >>> shopping[::-1]
    ['Limón', 'Sal', 'Aceite', 'Huevos', 'Agua']
    
  • Opcion B Mediante la función reversed():

    >>> shopping
    ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']
    
    >>> list(reversed(shopping))
    ['Limón', 'Sal', 'Aceite', 'Huevos', 'Agua']
    

Mediante la función reverse() (nótese que es sin «d» al final):

>>> shopping
['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping.reverse()

>>> shopping#(1)!
['Limón', 'Sal', 'Aceite', 'Huevos', 'Agua']

  1. Se ha modificado la lista original.

Añadir al final de la lista

Una de las operaciones más utilizadas en listas es añadir elementos al final de las mismas. Para ello Python nos ofrece la función append(). Se trata de un método «destructivo» que modifica la lista original.

Veamos un ejemplo donde añadimos un producto a la lista de la compra:

>>> shopping = ['Agua', 'Huevos', 'Aceite']

>>> shopping.append('Atún')

>>> shopping
['Agua', 'Huevos', 'Aceite', 'Atún']

Patrón creación

Una forma muy habitual de trabajar con listas es empezar con una vacía e ir añadiendo elementos poco a poco. Se podría hablar de un patrón creación.

Supongamos un ejemplo en el que queremos construir una lista con los números pares en el intervalo \([0,20]\):

>>> even_numbers = []

>>> for num in range(20 + 1):#(1)!
...     if num % 2 == 0:
...         even_numbers.append(num)
...

>>> even_numbers
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

  1. Para «llegar» al 20 hay que incrementar en una unidad.

Añadir en cualquier posición

Ya hemos visto cómo añadir elementos al final de una lista. Sin embargo, Python ofrece una función insert() que vendría a ser una generalización de la anterior, para incorporar elementos en cualquier posición.

Simplemente debemos especificar el índice de inserción y el elemento a insertar. También se trata de una función destructiva.

En el siguiente ejemplo insertamos dos nuevos productos a la lista de la compra en posiciones arbitrarias:

>>> shopping = ['Agua', 'Huevos', 'Aceite']

>>> shopping.insert(1, 'Jamón')#(1)!

>>> shopping
['Agua', 'Jamón', 'Huevos', 'Aceite']

>>> shopping.insert(3, 'Queso')#(2)!

>>> shopping
['Agua', 'Jamón', 'Huevos', 'Queso', 'Aceite']

  1. Se podría leer como: «Quiero que 'Jamón' quede en la posición 1 de la lista».
  2. Se podría leer como: «Quiero que 'Queso' quede en la posición 3 de la lista».

Al igual que ocurría con el troceado de listas, en este tipo de inserciones no obtendremos un error si especificamos índices fuera de los límites de la lista. Estos se ajustarán al principio o al final en función del valor que indiquemos:

>>> shopping = ['Agua', 'Huevos', 'Aceite']

>>> shopping.insert(100, 'Mermelada')#(1)!

>>> shopping
['Agua', 'Huevos', 'Aceite', 'Mermelada']

>>> shopping.insert(-100, 'Arroz')#(2)!

>>> shopping
['Arroz', 'Agua', 'Huevos', 'Aceite', 'Mermelada']

  1. Se inserta lo más a la «derecha» posible.
  2. Se inserta lo más a la «izquierda» posible.

append vs insert

Podría existir la tentación de utilizar insert() para añadir elementos al final de una lista...

>>> values = [1, 2, 3]
>>> values.insert(len(values), 4)
>>> values
[1, 2, 3, 4]

¡No lo hagas! Utiliza append() Es más eficiente y más legible.

Repetir elementos

Al igual que con las cadenas de texto, el operador * nos permite repetir los elementos de una lista.

Siguiendo con el ejemplo de la lista de la compra, podríamos querer comprar 3 unidades de cada producto:

>>> shopping = ['Agua', 'Huevos', 'Aceite']

>>> shopping * 3
['Agua',
 'Huevos',
 'Aceite',
 'Agua',
 'Huevos',
 'Aceite',
 'Agua',
 'Huevos',
 'Aceite']

Combinar listas

Python nos ofrece varios mecanismos para combinar dos listas, en función del resultado que busquemos:

Mediante el operador +:

>>> shopping = ['Agua', 'Huevos', 'Aceite']
>>> fruitshop = ['Naranja', 'Manzana', 'Piña']

>>> shopping + fruitshop
['Agua', 'Huevos', 'Aceite', 'Naranja', 'Manzana', 'Piña']

Mediante la función extend():

>>> shopping = ['Agua', 'Huevos', 'Aceite']
>>> fruitshop = ['Naranja', 'Manzana', 'Piña']

>>> shopping.extend(fruitshop)#(1)!

>>> shopping
['Agua', 'Huevos', 'Aceite', 'Naranja', 'Manzana', 'Piña']

  1. Esto es equivalente a: shopping += fruitshop

Hay que tener en cuenta que extend() funciona adecuadamente si pasamos una lista como argumento. En otro caso, quizás los resultados no sean los esperados.

Veamos un ejemplo:

>>> shopping = ['Agua', 'Huevos', 'Aceite']

>>> shopping.extend('Limón')#(1)!

>>> shopping
['Agua', 'Huevos', 'Aceite', 'L', 'i', 'm', 'ó', 'n']

    • extend() «recorre» (o itera) sobre cada uno de los elementos del objeto en cuestión.
    • Al ser una cadena de texto cada elemento es un carácter.

Se podría pensar en utilizar append() para combinar listas. La realidad es que no funciona exactamente como esperamos; la segunda lista se añadiría como una sublista de la principal.

Veamos un ejemplo:

>>> shopping = ['Agua', 'Huevos', 'Aceite']
>>> fruitshop = ['Naranja', 'Manzana', 'Piña']

>>> shopping.append(fruitshop)

>>> shopping
['Agua', 'Huevos', 'Aceite', ['Naranja', 'Manzana', 'Piña']]

Modificar listas

Para modificar un elemento de una lista debemos acceder a su índice y asignar el valor correspondiente.

En el siguiente ejemplo preferimos comprar jugo que agua:

>>> shopping = ['Agua', 'Huevos', 'Aceite']

>>> shopping[0]
'Agua'

>>> shopping[0] = 'Jugo'

>>> shopping
['Jugo', 'Huevos', 'Aceite']

En el caso de acceder a un índice no válido de la lista, incluso para modificar, obtendremos un error:

>>> shopping[100]
Traceback (most recent call last):
  Cell In[1], line 1
    shopping[100]
IndexError: list index out of range

Modificar con troceado

No sólo es posible modificar un elemento de cada vez, sino que podemos asignar valores a trozos de una lista.

En el siguiente ejemplo reemplazamos huevos, aceite y sal por atún y pasta:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping[1:4]
['Huevos', 'Aceite', 'Sal']

>>> shopping[1:4] = ['Atún', 'Pasta']#(1)!

>>> shopping
['Agua', 'Atún', 'Pasta', 'Limón']

  1. La lista que asignamos no necesariamente debe tener la misma longitud que el trozo que sustituimos.

Borrar elementos

Python nos ofrece varios mecanismos para borrar elementos de una lista:

Mediante la sentencia del:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> del shopping[3]

>>> shopping
['Agua', 'Huevos', 'Aceite', 'Limón']

Mediante la función remove():

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping.remove('Sal')#(1)!

>>> shopping
['Agua', 'Huevos', 'Aceite', 'Limón']

  1. Si existen valores duplicados, la función remove() sólo borarrá la primera ocurrencia.

La sentencia del y la función remove() efectivamente borran el elemento indicado de la lista, pero no «devuelven»1 nada. Sin embargo, Python nos ofrece la función pop() que además de borrar, nos «recupera» el elemento; algo así como una extracción. Lo podemos ver como una combinación de acceso + borrado:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> product = shopping.pop()#(1)!
>>> product
'Limón'

>>> shopping
['Agua', 'Huevos', 'Aceite', 'Sal']

>>> product = shopping.pop(2)#(2)!
>>> product
'Aceite'

>>> shopping
['Agua', 'Huevos', 'Sal']

  1. Cuando no se indica el índice, Python extrae en último elemento. Equivale a: shopping.pop(-1)
  2. Extraer el elemento en la posición 2.

Mediante troceado de listas:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping[1:4] = []

>>> shopping
['Agua', 'Limón']

Borrado completo de la lista

Python nos ofrece varios mecanismos para borrar una lista por completo:

Mediante la función clear():

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping.clear()#(1)!

>>> shopping
[]

  1. Misma zona de memoria.

Mediante la asignación a lista vacía:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping = []#(1)!

>>> shopping
[]

  1. Nueva zona de memoria.

Recolector de basura

La memoria que queda «en el limbo» después de asignar un nuevo valor a la lista es detectada por el recolector de basura de Python, quien se encarga de liberar aquellos datos que no están referenciados por ninguna variable.

Encontrar un elemento

Si queremos descubrir el índice que corresponde a un determinado valor dentro una lista debemos usar la función index().

Como ejemplo supongamos que queremos encontrar el aceite en nuestra lista de la compra:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping.index('Aceite')
2

Hay que tener en cuenta que si el elemento que buscamos no está en la lista, obtendremos un error:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping.index('Pollo')
Traceback (most recent call last):
  Cell In[2], line 1
    shopping.index('Pollo')
ValueError: 'Pollo' is not in list

Múltiples ocurrencias

Si buscamos un valor que existe más de una vez en una lista, la función index() sólo nos devolverá el índice de la primera ocurrencia.

No existe find

En listas no disponemos de la función find() que sí estaba disponible para cadenas de texto.

Pertenencia de un elemento

Si queremos comprobar la existencia de un determinado elemento en una lista, podríamos buscar su índice, pero la forma pitónica de hacerlo es utilizar el operador in.

Si no estamos seguros de si hemos incluido ciertos productos en nuestro ejemplo de la lista de la compra, lo podemos comprobar de la siguiente manera:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> 'Aceite' in shopping
True

>>> 'Pollo' in shopping
False

Valor booleano

El operador in siempre devuelve un valor booleano, es decir, verdadero o falso.

Ejercicio

pypas   isogram

Longitud de una lista

Podemos conocer el número de elementos que tiene una lista mediante la función len().

Por ejemplo para conocer la cantidad de productos de nuestra lista de la compra haríamos lo siguiente:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> len(shopping)
5

Número de ocurrencias

Para contar cuántas veces aparece un determinado valor dentro de una lista podemos usar la función count().

Un ejemplo «divertido» de la serie The Big Bang Theory:

>>> sheldon_greeting = ['Penny', 'Penny', 'Penny']

>>> sheldon_greeting.count('Howard')
0

>>> sheldon_greeting.count('Penny')
3

Dividir «string» como lista

Una tarea muy habitual al trabajar con cadenas de texto es dividirlas por algún tipo de separador. En este sentido, Python nos ofrece la función split(), que debemos usar anteponiendo el «string» que queramos dividir.

Veamos un ejemplo con ciertos refranes:

>>> proverb = 'No hay mal que por bien no venga'
>>> proverb.split()#(1)!
['No', 'hay', 'mal', 'que', 'por', 'bien', 'no', 'venga']

>>> tools = 'Martillo,Sierra,Destornillador'
>>> tools.split(',')#(2)!
['Martillo', 'Sierra', 'Destornillador']

  1. Si no se indica nada, la función split() usa por defecto cualquier secuencia de espacios en blanco, tabuladores y saltos de línea como separador.
  2. En este caso se ha indicado que el separador sea una coma.

Existe una variante de split() en la que indicamos el número máximo de divisiones. Supongamos un ejemplo en el que nos dan una URL y nos piden separar el dominio de la ruta:

>>> url = 'python.org/downloads/releases'

>>> url.split('/')#(1)!
['python.org', 'downloads', 'releases']

>>> url.split('/', 1)#(2)!
['python.org', 'downloads/releases']

  1. Aquí split() no nos está sirviendo mucho...
  2. Al indicar el máximo de «una división» hemos conseguido el resultado.

También existe la función rsplit() que se comporta exactamente igual que la función split() pero empezando por la derecha.

Ejercicio

pypas   num-words

Particionado de cadenas de texto

Existe una forma algo más «elaborada» de dividir una cadena a través del particionado. Para ello podemos valernos de la función partition() que proporciona Python.

Esta función toma un argumento como separador, y divide la cadena de texto en 3 partes: lo que queda a la izquierda del separador, el separador en sí mismo y lo que queda a la derecha del separador

Veamos un ejemplo muy sencillo a partir de una operación matemática:

>>> text = '3+4'

>>> text.partition('+')
('3', '+', '4')

También existe la función rpartition() que se comporta exactamente igual que la función partition() pero empezando por la derecha.

Unir lista como «string»

Dada una lista, podemos convetirla a una cadena de texto, uniendo todos sus elementos mediante algún separador. Para ello hacemos uso de la función join() con la siguiente estructura:

Dark image Light image

Veamos varios ejemplos uniendo los productos de la lista de la compra:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> ','.join(shopping)
'Agua,Huevos,Aceite,Sal,Limón'

>>> ' '.join(shopping)
'Agua Huevos Aceite Sal Limón'

>>> '|'.join(shopping)
'Agua|Huevos|Aceite|Sal|Limón'

Hay que tener en cuenta que join() sólo funciona si todos sus elementos son cadenas de texto:

>>> ', '.join([1, 2, 3, 4, 5])
Traceback (most recent call last):
  Cell In[1], line 1
    ', '.join([1, 2, 3, 4, 5])
TypeError: sequence item 0: expected str instance, int found
join vs split

La función join() es realmente la opuesta a la función split().

Ejercicio

pypas   fix-date

Ordenar una lista

Python nos ofrece varios mecanismos para ordenar una lista:

Mediante la función sorted():

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> sorted(shopping)#(1)!
['Aceite', 'Agua', 'Huevos', 'Limón', 'Sal']

    • sorted() «devuelve» una nueva lista ordenada.
    • La lista shopping se mantiene intacta.

Mediante la función sort():

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> shopping.sort()#(1)!

>>> shopping
['Aceite', 'Agua', 'Huevos', 'Limón', 'Sal']

    • sort() no devuelve nada.
    • Sólo se encarga de ordenar (modificar) la lista indicada shopping.

Ambos métodos admiten un parámetro «booleano» reverse para indicar si queremos que la ordenación se haga en sentido inverso:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> sorted(shopping, reverse=True)
['Sal', 'Limón', 'Huevos', 'Agua', 'Aceite']

Iterar sobre una lista

Al igual que hemos visto con las cadenas de texto, también podemos iterar sobre los elementos de una lista utilizando la sentencia for.

Recorremos por ejemplo los productos de la lista de la compra:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> for product in shopping:
...     print(product)
...
Agua
Huevos
Aceite
Sal
Limón

break y continue

En esta estructura también es posible utilizar tanto break como continue.

Ejercicio

pypas   chars-list

Iterar usando enumeración

Hay veces que no sólo nos interesa «visitar» cada uno de los elementos de una lista, sino que también queremos saber su índice dentro de la misma. Para ello Python nos ofrece la función enumerate().

Para el ejemplo de la lista de la compra, nos podría interesar aplicar esta estructura programática:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> for index, product in enumerate(shopping):#(1)!
...     print(index, product)
...
0 Agua
1 Huevos
2 Aceite
3 Sal
4 Limón

  1. En cada iteración del bucle las variables index y product reciben el índice y el producto existentes en la lista de la compra.

Por defecto enumerate() empieza sus índices en 0 (como era de esperar) pero si quisiéramos modificar este comportamiento también se podría.

Ejemplo empezando la enumeración de la lista de la compra en 10:

>>> shopping = ['Agua', 'Huevos', 'Aceite', 'Sal', 'Limón']

>>> for index, product in enumerate(shopping, 10):
...     print(index, product)
...
10 Agua
11 Huevos
12 Aceite
13 Sal
14 Limón

Iterar sobre múltiples listas

Python ofrece la posibilidad de iterar sobre múltiples listas en paralelo utilizando la función zip(). Se basa en ir «juntando» ambas listas elemento a elemento:

Zip diagram

Veamos un ejemplo en el que añadimos ciertos detalles a nuestra lista de la compra:

>>> shopping = ['Agua', 'Aceite', 'Arroz']
>>> details = ['mineral natural', 'de oliva virgen', 'basmati']

>>> for product, detail in zip(shopping, details):#(1)!
...     print(product, detail)
...
Agua mineral natural
Aceite de oliva virgen
Arroz basmati

  1. En cada iteración del bucle la variable product toma un elemento de la lista shopping y la variable detail toma un elemento de la lista details.

Distinta longitud

En el caso de que las listas no tengan la misma longitud, la función zip() realiza la combinación hasta que se agota la lista más corta.

Dado que zip() produce un iterador, si queremos obtener una lista explícita con la combinación en paralelo de las listas, debemos construir dicha lista de la siguiente manera:

>>> shopping = ['Agua', 'Aceite', 'Arroz']
>>> details = ['mineral natural', 'de oliva virgen', 'basmati']

>>> list(zip(shopping, details))
[('Agua', 'mineral natural'),
 ('Aceite', 'de oliva virgen'),
 ('Arroz', 'basmati')]

Ejercicio

pypas   dot-product

Comparar listas

¿Cómo determina Python si una lista es mayor o menor que otra? Analicemos lo que ocurre en el siguiente ejemplo:

>>> [1, 2, 3] < [1, 2, 4]
True

Python llega a la conclusión de que la lista [1, 2, 3] es menor que [1, 2, 4] porque va comparando elemento a elemento:

  • El 1 es igual en ambas listas.
  • El 2 es igual en ambas litas.
  • El 3 es menor que el 4, por lo que la primera lista es menor que la segunda.

Entender la forma en la que se comparan dos listas es importante para poder aplicar otras funciones y obtener los resultados deseados.

Ver también

Esta comparación funciona de forma totalmente análoga a la comparación de cadenas de texto.

Cuidado con las copias

Las listas son estructuras de datos mutables y esta característica nos obliga a tener cuidado cuando realizamos copias de listas, ya que la modificación de una de ellas puede afectar a la otra.

Veamos un ejemplo sencillo:

>>> original_list = [4, 3, 7, 1]

>>> copy_list = original_list#(1)!

>>> original_list[0] = 15#(2)!

>>> original_list#(3)!
[15, 3, 7, 1]

>>> copy_list#(4)!
[15, 3, 7, 1]

  1. Con esta asignación hacemos que copy_list «apunte» a la misma zona de memoria que original_list.
  2. En esa zona de memoria estamos modificando el primer elemento de la lista original_list.
  3. Obviamente esta lista se habrá modificado.
  4. También se ha modificado la «copia».

Una posible solución a este problema sería efectuar una «copia dura». Para ello Python proporciona la función copy():

>>> original_list = [4, 3, 7, 1]

>>> copy_list = original_list.copy()#(1)!

>>> original_list[0] = 15#(2)!

>>> original_list#(3)!
[15, 3, 7, 1]

>>> copy_list#(4)!
[4, 3, 7, 1]

    • La función copy() hace una copia en otra zona de memoria.
    • Aquí también valdría utilizar un troceado «completo» copy_list = original_list[:]
  1. Modificamos el primer elemento de original_list.
  2. La modificación de este elemento sólo afecta a original_list.
  3. Dado que copy_list está en otra zona de memoria no se ve afectada por el cambio.
Copia profunda

En el caso de que estemos trabajando con listas que contienen elementos mutables, debemos hacer uso de la función deepcopy() dentro del módulo copy de la librería estándar.

Veracidad múltiple

Si bien podemos usar sentencias condicionales para comprobar la veracidad de determinadas expresiones, Python nos ofrece dos funciones «built-in» con las que podemos evaluar si se cumplen todas las condiciones all() o si se cumple alguna condición any(). Estas funciones trabajan sobre iterables, y el caso más evidente es una lista.

Supongamos un ejemplo en el que queremos comprobar si una determinada palabra cumple las siguientes condiciones:

  • Su longitud total es mayor que 4.
  • Empieza por «p».
  • Contiene, al menos, una «y».
>>> word = 'python'

>>> if len(word) > 4 and word.startswith('p') and word.count('y') >= 1:
...     print('Cool word!')
... else:
...     print('No thanks')
...
Cool word!

>>> word = 'python'

>>> enough_length = len(word) > 4#(1)!
>>> right_beginning = word.startswith('p')#(2)!
>>> min_ys = word.count('y') >= 1#(3)!

>>> is_cool_word = all([enough_length, right_beginning, min_ys])#(4)!

>>> if is_cool_word:
...     print('Cool word!')
... else:
...     print('No thanks')
...
Cool word!

  1. True
  2. True
  3. True
  4. all([True, True, True]) True

>>> word = 'yeah'

>>> enough_length = len(word) > 4#(1)!
>>> right_beginning = word.startswith('p')#(2)!
>>> min_ys = word.count('y') >= 1#(3)!

>>> is_fine_word = any([enough_length, right_beginning, min_ys])#(4)!

>>> if is_fine_word:
...     print('Fine word!')
... else:
...     print('No thanks')
...
Fine word!

  1. False
  2. False
  3. True
  4. any([False, False, True]) True

A tener en cuenta la peculiaridad de estas funciones cuando trabajan con la lista vacía:

>>> all([])
True

>>> any([])
False

Casos de uso

Este enfoque puede ser interesante cuando se manejan muchas condiciones o bien cuando queremos separar las condiciones y agruparlas en una única lista.

Listas por comprensión

Las listas por comprensión establecen una técnica para crear listas de forma más compacta basándose en el concepto matemático de conjuntos definidos por comprensión.

Podríamos decir que su sintaxis sigue un modelo VLC (Value-Loop-Condition) tal y como se muestra en la siguiente figura:

Dark image Light image

Empecemos por un ejemplo en el que convertimos una cadena de texto con valores numéricos en una lista con los mismos valores pero convertidos a enteros:

>>> values = '32,45,11,87,20,48'

>>> int_values = []

>>> for value in values.split(','):
...     int_value = int(value)
...     int_values.append(int_value)
...

>>> int_values
[32, 45, 11, 87, 20, 48]
>>> values = '32,45,11,87,20,48'

>>> int_values = [int(value) for value in values.split(',')]

>>> int_values
[32, 45, 11, 87, 20, 48]

A continuación se presenta un diagrama con la transformación de la estructura clásica en listas por comprensión:

Transformation of list comprehensions

Condiciones en comprensiones

También existe la posibilidad de incluir condiciones en las listas por comprensión.

Continuando con el ejemplo anterior, supongamos que sólo queremos crear la lista con aquellos valores que empiecen por el dígito 4:

>>> values = '32,45,11,87,20,48'

>>> int_values = [int(v) for v in values.split(',') if v.startswith('4')]

>>> int_values
[45, 48]

Anidamiento en comprensiones

En la iteración que usamos dentro de la lista por comprensión es posible usar bucles anidados.

Veamos un ejemplo en el que generamos todas las combinaciones de una serie de valores:

>>> values = '32,45,11,87,20,48'
>>> svalues = values.split(',')

>>> combinations = [f'{v1}x{v2}' for v1 in svalues for v2 in svalues]

>>> combinations
['32x32',
 '32x45',
 '32x11',
 '32x87',
 '32x20',
 '32x48',
 '45x32',
 '45x45',
 ...
 '48x45',
 '48x11',
 '48x87',
 '48x20',
 '48x48']

Casos de uso

Las listas por comprensión son una herramienta muy potente y nos ayuda en muchas ocasiones, pero hay que tener cuidado de no generar expresiones excesivamente complejas. En estos casos es mejor una aproximación clásica.

Ejercicio

pypas   fcomp

sys.argv

Cuando queramos ejecutar un programa Python desde línea de comandos, tendremos la posibilidad de acceder a los argumentos de dicho programa. Para ello se utiliza una lista «especial» que la encontramos dentro del módulo sys y que se denomina argv:

Dark image Light image

Veamos una aplicación de lo anterior en un ejemplo que convierte un número decimal a una determinada base, ambos argumentos pasados por línea de comandos:

dec2base.py
import sys

number = int(sys.argv[1])#(1)!
tobase = int(sys.argv[2])#(2)!

match tobase:
    case 2:
        result = f'{number:b}'
    case 8:
        result = f'{number:o}'
    case 16:
        result = f'{number:x}'
    case _:
        result = None

if result is None:
    print(f'Base {tobase} not implemented!')
else:
    print(result)

  1. El primer segundo argumento es el número a convertir.
  2. El segundo tercer argumento es la base a la que convertir.

Si lo ejecutamos obtendríamos lo siguiente:

$ python dec2base.py 65535 2
1111111111111111

Funciones matemáticas

Python nos ofrece, entre otras2, estas tres funciones matemáticas básicas que se pueden aplicar sobre listas.

Mediante la función sum():

>>> data = [5, 3, 2, 8, 9, 1]
>>> sum(data)
28

Mediante la función max():

>>> data = [5, 3, 2, 8, 9, 1]
>>> max(data)
9

Mediante la función min():

>>> data = [5, 3, 2, 8, 9, 1]
>>> min(data)
1

Ejercicio

Escribe un programa avg.py que reciba desde línea de comandos una serie de números y calcule la media de dichos valores (redondeando a 2 cifras decimales).

La llamada se haría de la siguiente manera:

$ python avg.py 32 56 21 99 12 17

Ejemplo:

  • Entrada 32 56 21 99 12 17
  • Salida 40.17

Ten en cuenta que sys.argv es una lista (como otra cualquiera) y que admite troceado de listas.

Lista de listas

Como ya hemos visto en varias ocasiones, las listas son estructuras de datos que pueden contener elementos heterogéneos. Estos elementos pueden ser a su vez listas.

A continuación planteamos un ejemplo del contexto deportivo. Un equipo de fútbol suele tener una disposición en el campo organizada en líneas de jugadores/as. En aquella alineación con la que España ganó la copa del mundo en 2023 había una disposición 4-3-3 con las siguientes jugadoras:

Campeonas 2023

Veamos una posible representación de este equipo de fútbol usando una lista compuesta de listas. Primero definimos cada una de las líneas:

>>> goalkeeper = 'Cata'
>>> defenders = ['Olga', 'Laia', 'Irene', 'Ona']
>>> midfielders = ['Jenni', 'Teresa', 'Aitana']
>>> forwards = ['Mariona', 'Salma', 'Alba']

Y ahora las juntamos en una única lista:

>>> team = [goalkeeper, defenders, midfielders, forwards]

>>> team
['Cata',
 ['Olga', 'Laia', 'Irene', 'Ona'],
 ['Jenni', 'Teresa', 'Aitana'],
 ['Mariona', 'Salma', 'Alba']]

Podemos comprobar el acceso a distintos elementos:

>>> team[0]#(1)!
'Cata'

>>> team[1][0]#(2)!
'Olga'

>>> team[2]#(3)!
['Jenni', 'Teresa', 'Aitana']

>>> team[3][1]#(4)!
'Salma'

  1. Portera.
  2. Lateral izquierdo.
  3. Centrocampistas.
  4. Delantera centro.

También podemos recorrer toda la alineación:

>>> for playline in team:
...     if isinstance(playline, list):#(1)!
...         for player in playline:
...             print(player, end=' ')
...         print()
...     else:
...         print(playline)
...
Cata
Olga Laia Irene Ona
Jenni Teresa Aitana
Mariona Salma Alba

  1. Es necesario comprobar si es una lista porque la portera está «sola».

Ejercicio

pypas   mul-matrix2

Ejercicios

  1. pypas   max-value
  2. pypas   max-value-with-min
  3. pypas   min-value
  4. pypas   min-value-with-max
  5. pypas   remove-dups
  6. pypas   flatten-list
  7. pypas   remove-consecutive-dups
  8. pypas   all-same
  9. pypas   sum-diagonal
  10. pypas   powers2
  11. pypas   dec2bin
  12. pypas   sum-mixed
  13. pypas   n-multiples
  14. pypas   drop-even
  15. pypas   nth-power
  16. pypas   name-initials
  17. pypas   non-consecutive
  18. pypas   mul-reduce
  19. pypas   digit-rev-list
  20. pypas   time-plus-minutes
  21. pypas   add-positives
  22. pypas   add-opposites
  23. pypas   descending-numbers
  24. pypas   merge-sorted
  25. pypas   trimmed-add
  26. pypas   wolves
  27. pypas   minmax
  28. pypas   cascading-subsets
  29. pypas   diff-cuboid
  30. pypas   fl-strip
  31. pypas   logical-chain
  32. pypas   first-unused-id
  33. pypas   find-odds
  34. pypas   chemistry
  35. pypas   next-item
  36. pypas   v-partition
  37. pypas   attach-len
  38. pypas   reversing-words
  39. pypas   barycenter
  40. pypas   sort-custom
  41. pypas   flatten-list-deep
  42. pypas   first-duplicated
  43. pypas   fill-values
  44. pypas   frange
  45. pypas   qual-name
  46. pypas   mul-matrix

  1. Más adelante veremos el comportamiento de las funciones. Devolver o retornar un valor es el resultado de aplicar una función. 

  2. Existen multitud de paquetes científicos en Python para trabajar con listas o vectores numéricos. Una de las más famosas es la librería Numpy