Recursos libres para aprender a programar en español
Basado en https://github.com/mumukiproject/mumuki-guia-python3-recorridos-2021
En lecciones anteriores definimos funciones, usamos registros y listas. Ahora que tenemos todas esas herramientas, llega el momento de combinarlas y aprender a hacer cosas más complejas recorriéndolas. ¡Acompañanos!
Ana, contadora de una conocida empresa 🏢, tiene diccionarios para representar los balances de cada mes y distintas listas para guardarlos. Por ejemplo:
#En julio ganó $50, en agosto perdió $12, etc.
balances_ultimo_semestre = [
{ "mes": "julio", "ganancia": 50 },
{ "mes": "agosto", "ganancia": -12 },
{ "mes": "septiembre", "ganancia": 1000 },
{ "mes": "octubre", "ganancia": 300 },
{ "mes": "noviembre", "ganancia": 200 },
{ "mes": "diciembre", "ganancia": 0 }
]
balances_primer_trimestre = [
{ "mes": "enero", "ganancia": 2 },
{ "mes": "febrero", "ganancia": 10 },
{ "mes": "marzo", "ganancia": -20 }
]
Dicho esto, Ana necesita saber la ganancia acumulada de un conjunto de balances.
Definí la función
ganancia_total
que dado una lista de balances cualquiera nos devuelva la suma de todas:>> ganancia_total(balances_de_un_periodo): 1538
Nuestro viejo amigo for...in
y una variable local nos van a ayudar en esta tarea. Lo importante es pensar con qué valor la inicializamos y qué debemos hacer en cada iteración. ☺️
Probá las siguientes consultas y verificá que devuelvan lo mismo:
>>> ganancia_total([
{ "mes": "enero", "ganancia": 2 },
{ "mes": "febrero", "ganancia": 10 },
{ "mes": "marzo", "ganancia": -20 }])
-8
>>> ganancia_total([
{ "mes": "julio", "ganancia": 50 },
{ "mes": "agosto", "ganancia": -12 },
{ "mes": "septiembre", "ganancia": 1000 },
{ "mes": "octubre", "ganancia": 300 },
{ "mes": "noviembre", "ganancia": 200 },
{ "mes": "diciembre", "ganancia": 0 }])
1538
¡Excelente! 🤩
En este caso como queríamos obtener una sumatoria, nuestra variable tenía que inicializarse en 0 ya que es el valor neutro de la suma. Si por ejemplo quisieramos hacer una productoria (lo mismo que la sumatoria pero multiplicando) deberíamos inicializar nuestra variable en 1. No siempre es fácil darse cuenta qué valor inicial debemos utilizar, pero es muy importante que lo pensemos antes de encarar el for...in
. 🤗
¡Ana tiene nuevos requirimientos! Ahora nos pidió lo siguiente: “Quiero saber cuántos balances fueron positivos, es decir, aquellos en los que la ganancia fue mayor a cero”. 📈
Definí la función
cantidad_de_balances_positivos
que dada una lista de balances nos retorne cuántos son positivos.
Lo importante en este ejercicio es inicializar una variable local e incrementarla solo cuando corresponda. Para eso vas a tener que usar un if
. 🤓
Probá las siguientes consultas y verificá que devuelvan lo mismo:
>>> cantidad_de_balances_positivos([{ "mes": "noviembre", "ganancia": 5 }])
1
>>> cantidad_de_balances_positivos([
{ "mes": "marzo", "ganancia": 8 },
{ "mes": "agosto", "ganancia": 10 }])
2
>>> cantidad_de_balances_positivos([]), 0
>>> cantidad_de_balances_positivos([
{ "mes": "marzo", "ganancia": 0 },
{ "mes": "agosto", "ganancia": 0 }])
0
>>> cantidad_de_balances_positivos([
{ "mes": "enero", "ganancia": 10 },
{ "mes": "febrero", "ganancia": -10 },
{ "mes": "marzo", "ganancia": 2 },
{ "mes": "abril", "ganancia": 100 }])
3
>>> cantidad_de_balances_positivos([
{ "mes": "enero", "ganancia": -1 },
{ "mes": "febrero", "ganancia": -2 },
{ "mes": "marzo", "ganancia": -3 }])
0
La variable local que utilizaste es lo que en programación se conoce como contador, una variable que se incrementa cada vez que hacemos algo dentro de un for...in
o solo aquellas veces que se cumpla una condición (como en este caso). 😉
Más allá de las ganancias acumuladas o los balances positivos, Ana quisiera poder obtener todas las ganancias a partir de una lista de balances. Para ello debemos transformar, o mapear, cada elemento de la lista. 👍
Definí la función
ganancias
que toma una lista de balances y devuelve una lista que posea solo las ganancias de cada uno.>>> ganancias([ { "mes": "enero", "ganancia": 40 }, { "mes": "febrero", "ganancia": 12 }, { "mes": "marzo", "ganancia": 8} ]) [40, 12, 8]
¿Y ahora con qué valor inicializamos la variable local? 🤔
En este caso te va a servir una lista vacía, ¡pero no te olvides de agregarle elementos! 😉
Probá las siguientes consultas y verificá que devuelvan lo mismo:
self.assertEqual(ganancias([{ "mes": "marzo", "ganancia": 10 }, { "mes": "agosto", "ganancia": 2 }, { "mes": "septiembre", "ganancia":0 }]), [10, 2, 0])
self.assertEqual(ganancias([{ "mes": "marzo", "ganancia": 10 }, { "mes": "agosto", "ganancia": 2 }, { "mes": "septiembre", "ganancia":0 }, { "mes": "diciembre", "ganancia": 8 }]), [10, 2, 0, 8])
self.assertEqual(ganancias([{ "mes": "marzo", "ganancia": 8 }, { "mes": "agosto", "ganancia": 7 }]), [8,7])
¡Excelente! 👏 Ahora ya sabemos cómo transformar cada elemento de una lista para obtener una lista nueva 💪. Esta operación es muy común y se conoce como mapeo de una lista.
¡Ahora que sabemos mapear vamos a practicarlo! 💪
Definí la función
meses
que dada una lista con diccionarios devuelve una lista de meses. 📆>> meses([ { "mes": "enero", "ganancia": 870 }, { "mes": "febrero", "ganancia": 1000 }, { "mes": "marzo", "ganancia": 1020 }, { "mes": "abril", "ganancia": 2300 }, { "mes": "mayo", "ganancia": -10 } ]) ["enero", "febrero", "marzo", "abril", "mayo"]
Probá las siguientes consultas y verificá que devuelvan lo mismo:
self.assertEqual(meses([{ "mes": "enero", "ganancia": 10 }, { "mes": "febrero", "ganancia": 2 }, { "mes": "marzo", "ganancia": 5 }]), ["enero", "febrero", "marzo"])
self.assertEqual(meses([{ "mes": "enero", "ganancia": 10 }, { "mes": "febrero", "ganancia": 2 }, { "mes": "marzo", "ganancia": 5 }, { "mes": "abril", "ganancia": 8 }, { "mes": "mayo", "ganancia": 12 }, { "mes": "junio", "ganancia": 25 }]), ["enero", "febrero", "marzo", "abril", "mayo", "junio"])
En el ejercicio anterior hicimos un mapeo utilizando for...in
. En Python contamos con otras formas de hacer eso, ¡las listas por comprensión! 🤩
Veamos cómo funcionan, si a partir de una lista de strings quisieramos obtener una lista con los largos de cada uno, podríamos definir:
def largos(palabras):
largos = []
for palabra in palabras:
list.append(largos, len(palabra))
return largos
Sin embargo, también podríamos hacerlo de esta manera utilizando listas por comprensión:
def largos(palabras):
return [len(palabra) for palabra in palabras]
Redefiní la función
meses
para que haga lo mismo que antes pero utilizando listas por comprensión.
Probá las siguientes consultas y verificá que devuelvan lo mismo:
self.assertEqual(meses([{ "mes": "enero", "ganancia": 10 }, { "mes": "febrero", "ganancia": 2 }, { "mes": "marzo", "ganancia": 5 }]), ["enero", "febrero", "marzo"])
self.assertEqual(meses([{ "mes": "enero", "ganancia": 10 }, { "mes": "febrero", "ganancia": 2 }, { "mes": "marzo", "ganancia": 5 }, { "mes": "abril", "ganancia": 8 }, { "mes": "mayo", "ganancia": 12 }, { "mes": "junio", "ganancia": 25 }]), ["enero", "febrero", "marzo", "abril", "mayo", "junio"])
¡Excelente! 🙌
Lo bueno de conocer más de una manera de hacer lo mismo es que puedas elegir qué herramienta te gusta más para resolver los problemas. 🛠️
Ana realiza muchas proyecciones sobre los balances de su empresa, por lo que le dijimos que podíamos darle una mano ahora que sabemos mapear. Lo que le interesa hacer es poder ver cuáles serían las ganancias de un balance si todas hubieran sido del doble 💰. Por ejemplo:
>>> balances_ultimo_semestre = [
{ "mes": "julio", "ganancia": 50 },
{ "mes": "agosto", "ganancia": -12 },
{ "mes": "septiembre", "ganancia": 1000 },
{ "mes": "octubre", "ganancia": 300 },
{ "mes": "noviembre", "ganancia": 200 },
{ "mes": "diciembre", "ganancia": 0 }
]
>>> doble_de_ganancias(balances_ultimo_semestre)
[100, -24, 2000, 600, 400, 0]
Como verás, si las ganancias fueran negativas ahora serán ¡doblemente negativas! 📉
Definí la función
doble_de_ganancias
. Podés utilizar tantofor...in
como listas por comprensión, lo que vos prefieras. ☺️
Probá las siguientes consultas y verificá que devuelvan lo mismo:
self.assertEqual(doble_de_ganancias([{ "mes": "enero", "ganancia": 1000 }, { "mes": "febrero", "ganancia": -200 }, { "mes": "marzo", "ganancia": 500 }]), [2000, -400, 1000])
self.assertEqual(doble_de_ganancias([{ "mes": "enero", "ganancia": -1000 }, { "mes": "febrero", "ganancia": -200 }, { "mes": "marzo", "ganancia": -500 }]), [-2000, -400, -1000])
self.assertEqual(doble_de_ganancias([{ "mes": "enero", "ganancia": 0 }, { "mes": "febrero", "ganancia": 0 }]), [0, 0])
¡Perfecto! 👏
¿Y si solo quisieramos algunos balances? ¿podemos filtrar utilizando for...in
? 😳
Con la programación se puede hacer cualquier cosa, o casi 😅. Ya hicimos una función para poder saber la cantidad de balances positivos (cantidad_de_balances_positivos
), ahora vamos a ver cómo podemos hacer para saber cuáles son esos balances. 📆
Definí la función
balances_positivos
que toma los balances de un período y devuelve una lista con aquellos cuya ganancia fue mayor a cero.
Acá también vas a necesitar empezar con una lista vacía, pero a diferencia de cuando hicimos el mapeo, acá no vas a agregar elementos en cada iteración, solo en algunos casos. 👀
Probá las siguientes consultas y verificá que devuelvan lo mismo:
self.assertEqual(balances_positivos([{ "mes": "marzo", "ganancia": 10 }, { "mes": "agosto", "ganancia": 2 }, { "mes": "septiembre", "ganancia": 8 }]), [{ "mes": "marzo", "ganancia": 10 }, { "mes": "agosto", "ganancia": 2 }, { "mes": "septiembre", "ganancia": 8 }])
self.assertEqual(balances_positivos([{ "mes": "marzo", "ganancia": 10 }, { "mes": "agosto", "ganancia": 2 }, { "mes": "septiembre", "ganancia": 0 }]), [{ "mes": "marzo", "ganancia": 10 }, { "mes": "agosto", "ganancia": 2 }])
self.assertEqual(balances_positivos([{ "mes": "julio", "ganancia": 12 }, { "mes": "agosto", "ganancia": -2 }]), [{ "mes": "julio", "ganancia": 12 }])
self.assertEqual(balances_positivos([{ "mes": "agosto", "ganancia": -12 }, { "mes": "septiembre", "ganancia": 0 }]), [])
¡Muy bien! 🙌 Ahora ya sabemos cómo filtrar una lista. En criollo, aprendimos a obtener los elementos de una lista que cumplen una condición determinada. En este caso obtuvimos una nueva lista con los balances que presentaban una ganancia positiva. 💰
¡Practiquemos haciendo un nuevo filtrado! 😉
Definí la función
afortunados
, que filtra aquellos balances que tuvieron una ganancia mayor a $1000. 💵
Probá las siguientes consultas y verificá que devuelvan lo mismo:
self.assertEqual(afortunados([{ "mes": "enero", "ganancia": 1000 }, { "mes": "febrero", "ganancia": 2000 }, { "mes": "marzo", "ganancia": 2500 }, { "mes": "abril", "ganancia": 1001 }, { "mes": "mayo", "ganancia": 0 }, { "mes": "junio", "ganancia": -25 }]), [{ "mes": "febrero", "ganancia": 2000 }, { "mes": "marzo", "ganancia": 2500 }, { "mes": "abril", "ganancia": 1001 }])
self.assertEqual(afortunados([{ "mes": "enero", "ganancia": 1000 }, { "mes": "febrero", "ganancia": 0 }, { "mes": "marzo", "ganancia": 200 }, { "mes": "abril", "ganancia": -30 }]), [])
Así como podíamos hacer mapeos utilizando listas por comprensión, también podemos hacer filtrados. 👐
Imaginemos que tenemos la función mayores_a_5
que dada una lista de números nos retorna una nueva con aquellos que son mayores a 5:
def mayores_a_5(numeros):
mayores = []
for numero in numeros:
if numero > 5:
list.append(mayores, numero)
return mayores
Otra manera de definirla sería haciendo:
def mayores_a_5(numeros):
return [numero for numero in numeros if numero > 5]
Redefiní la función
afortunados
para que haga lo mismo pero utilizando listas por comprensión.
Probá las siguientes consultas y verificá que devuelvan lo mismo:
self.assertEqual(afortunados([{ "mes": "enero", "ganancia": 1000 }, { "mes": "febrero", "ganancia": 2000 }, { "mes": "marzo", "ganancia": 2500 }, { "mes": "abril", "ganancia": 1001 }, { "mes": "mayo", "ganancia": 0 }, { "mes": "junio", "ganancia": -25 }]), [{ "mes": "febrero", "ganancia": 2000 }, { "mes": "marzo", "ganancia": 2500 }, { "mes": "abril", "ganancia": 1001 }])
self.assertEqual(afortunados([{ "mes": "enero", "ganancia": 1000 }, { "mes": "febrero", "ganancia": 0 }, { "mes": "marzo", "ganancia": 200 }, { "mes": "abril", "ganancia": -30 }]), [])
¡Excelente! 👏
También se puede combinar la idea de filtrar y de mapear en las listas por comprensión. Por ejemplo: si quisieramos el doble de los números mayores a 5 podríamos hacer:
def doble_de_los_mayores_a_5(numeros)
[numero * 2 for numero in numeros if numero > 5]
A modo de resumen, la sintaxis general de las listas por comprensión es [expresion for elemento in lista if condicion]
. 🕶️
¿Sabes cuántos tienen 28 días? 🤔
¡Todos! 😜
Fuera del mal chiste, algunos meses son más largos que otros, es por eso que queremos saber de una lista de balances, cuáles corresponden a meses largos. Los meses largos son los que tienen 31 días. Veamos un ejemplo:
>>> balances_primer_trimestre = [
{ "mes": "enero", "ganancia": 2 },
{ "mes": "febrero", "ganancia": 10 },
{ "mes": "marzo", "ganancia": -20 }
]
>>> balances_de_meses_largos(balances_primer_trimestre)
[{ "mes": "enero", "ganancia": 2 }, { "mes": "marzo", "ganancia": -20 }]
Definí la función
balances_de_meses_largos
que funciona como te mostramos arriba. Podés utilizarfor...in
o listas por comprensión. En ambos casos te va a ser útil el operadorin
.
Por si no te acordás, los meses con 31 días son enero, marzo, mayo, julio, agosto, octubre y diciembre. 🗓️
Probá las siguientes consultas y verificá que devuelvan lo mismo:
self.assertEqual(balances_de_meses_largos([{ "mes": "enero", "ganancia": 1000 }, { "mes": "febrero", "ganancia": -200 }, { "mes": "marzo", "ganancia": 500 }, { "mes": "abril", "ganancia": 800 }, { "mes": "mayo", "ganancia": 770 }, { "mes": "junio", "ganancia": 870 }]), [{ "mes": "enero", "ganancia": 1000 }, { "mes": "marzo", "ganancia": 500 }, { "mes": "mayo", "ganancia": 770 }])
self.assertEqual(balances_de_meses_largos([{ "mes": "julio", "ganancia": 500 }, { "mes": "agosto", "ganancia": 900 }, { "mes": "septiembre", "ganancia": 1800 }, { "mes": "octubre", "ganancia": 900 }, { "mes": "noviembre", "ganancia": 2300 }, { "mes": "diciembre", "ganancia": 2000 }]), [{ "mes": "julio", "ganancia": 500 }, { "mes": "agosto", "ganancia": 900 }, { "mes": "octubre", "ganancia": 900 }, { "mes": "diciembre", "ganancia": 2000 }])
self.assertEqual(balances_de_meses_largos([{ "mes": "enero", "ganancia": 1000 }, { "mes": "febrero", "ganancia": -200 }, { "mes": "marzo", "ganancia": 500 }]), [{ "mes": "enero", "ganancia": 1000 }, { "mes": "marzo", "ganancia": 500 }])
self.assertEqual(balances_de_meses_largos([{ "mes": "abril", "ganancia": 800 }, { "mes": "mayo", "ganancia": 770 }, { "mes": "junio", "ganancia": 870 }]), [{ "mes": "mayo", "ganancia": 770 }])
En ejercicios anteriores ya definimos las funciones meses
y afortunados
tanto usando for...in
como listas por comprensión. 😉
Definí la función
meses_afortunados
utilizando listas por comprensión que dada una lista de balances reque nos diga los meses de aquellos que fueron afortunados.
En la Biblioteca
te dejamos las definiciones de estas funciones que utilizaban listas por comprensión para que recuerdes la lógica.
Pero no podes invocar meses
ni afortunados
. Tampoco for...in
, ¿se te ocurre cómo combinarlas en una nueva lista por comprensión? 🤔
Probá las siguientes consultas y verificá que devuelvan lo mismo:
#...solution[5]...#
#...solution[9]...#
self.assertEqual(meses_afortunados([{ "mes": "enero", "ganancia": 1001 }, { "mes": "febrero", "ganancia": -10 }, { "mes": "marzo", "ganancia": 2300 }, { "mes": "abril", "ganancia": 800 }]), ["enero", "marzo"])
self.assertEqual(meses_afortunados([{ "mes": "enero", "ganancia": 999 }, { "mes": "febrero", "ganancia": -10 }, { "mes": "marzo", "ganancia": 20 }, { "mes": "abril", "ganancia": 800 }]), [])
¡Muy bien! 🎉
Si bien esta es una forma de resolver el problema tiene como contra que no aprovechamos las funciones definidas previamente. Vamos a ver otra forma de resolver este problema en el próximo ejercicio. 💪
¡Afortunadamente este es el último ejercicio de la lección! 😜
Ahora sí, combinemos meses
y afortunados
para saber cuáles fueron esos meses en los que nos visitó la buena fortuna. 🤑
Definí la función
meses_afortunados
que dada una lista de balances devuelve aquellos meses que fueron afortunados. Esta vez no podés usar listas por comprensión.
En este ejercicio no vas a tener que hacer un for...in
, simplemente invocar correctamente a meses
y afortunados
. Para recordarlas podés ver la Biblioteca
. ☺️
Probá las siguientes consultas y verificá que devuelvan lo mismo:
#...solution[4]...#
#...solution[8]...#
self.assertEqual(meses_afortunados([{ "mes": "enero", "ganancia": 1001 }, { "mes": "febrero", "ganancia": -10 }, { "mes": "marzo", "ganancia": 2300 }, { "mes": "abril", "ganancia": 800 }]), ["enero", "marzo"])
self.assertEqual(meses_afortunados([{ "mes": "enero", "ganancia": 999 }, { "mes": "febrero", "ganancia": -10 }, { "mes": "marzo", "ganancia": 20 }, { "mes": "abril", "ganancia": 800 }]), [])
En esta última lección te reencontraste con el for...in
y resolviste problemas más complejos con él 🤯. Más especificamente creaste funciones para filtrar y/o transformar los elementos de una lista.
¡Muy buen trabajo! 👍