Recursos libres para aprender a programar en español
Basado en https://github.com/mumukiproject/mumuki-guia-python3-repeticion-condicional-e-indexada-2021
Como te contábamos cuando empezaste, programar nos da un gran poder: nos permite automatizar tareas repetitivas y tediosas.
¿Y qué quiere decir eso de “repetitivas”? Pensemos, por ejemplo, ¿cómo haríamos una función que sume los primeros 5 elementos de una lista?
def sumar_los_primeros_5(una_lista):
return una_lista[0] + una_lista[1] + una_lista[2] + una_lista[3] + una_lista[4]
¿Notás qué es lo que se repite? Sí, estamos haciendo 4 veces lo mismo: acceder a un elemento por índice y luego sumarlo. Sin dudas, sería mucho más interesante que la computadora hiciera eso por nosotros… ¡o si no te estaríamos mintiendo con lo de automatizar! 😑
En esta guía vamos a aprender cómo decirle a la computadora que repita varias veces lo mismo, y también algunos trucos más.
En las lecciones anteriores vimos varias funciones (¡y algunos operadores!) que operan con números, otras con booleanos, y así.
Pero quizás ya hayas notado que hay dos operaciones en particular que son bastante peculiares: len
e in
. ¿Por qué? Observemos los siguientes ejemplos:
>>> len("no tenemos pertenencias sino equipaje")
37
>>> "en" in len("un alma sola dividida en dos")
True
>>> len([4, 8, 15, 16, 23, 42])
6
>>> 34 in [1, 1, 2, 3, 5, 8, 13, 21, 34]
True
¡len
e in
funcionan tanto con listas como con strings!
¿Será casualidad? ¿Habrá más operaciones en común entre estos dos tipos de datos? Averigüemoslo probando lo siguiente:
>>> "Del árbol" + " una hoja se cayó" >>> "Sin brújula y sin radio"[4] >>> [1, 4] + [4, 5] >>> ["el", "carozo", "cantó"][2]
Como vemos, las listas y los strings, si bien no son lo mismo, son más parecidos de lo que pensábamos: ambos tipos de datos representan secuencias.
'a'
, 'b'
, '#'
, etc;Otras operaciones que listas y strings tienen en común son los slices, que podemos traducir como segmentos, secciones, o (de forma más literal y graciosa) rebanadas 🍞, que nos permite obtener los elementos entre dos límites:
>>> numeros = [10, 20, 30, 40, 50]
>>> numeros[0:2]
[10, 20] # es la lista conformada por el 1er y 2do elemento;
# ⚠️ recordemos que los índices en Python cuentan desde 0
>>> numeros[2:4]
[30, 40] # es la lista conformada por el 3er y 4to elemento
>>> numeros[0:4]
[10, 20, 30, 40] # es la lista conformada
# por los elementos 1 al 4
>>> numeros[:4]
[10, 20, 30, 40] # equivalente al ejemplo anterior;
# si no ponemos el límite inferior,
# trae todos los elementos desde el principio
>>> numeros[3:]
[40, 50] # es la lista conformada
# por todos los elementos partir del 4to
# si no ponemos el límite superior,
# trae todos los elementos hasta el final
¡Usemos lo aprendido para extraer segmentos de los strings! Ya cargamos en la consola una variable de tipo string llamada
primera_estrofa
; aplicando lo visto te toca:
- Averiguar cuantos caracteres tiene
primera_estrofa
- Obtener un string con los primeros 22 caracteres de
primera_estrofa
- Obtener un string con los últimos 25 caracteres de
primera_estrofa
💡 Algunas sugerencias:
primera_estrofa[aca_va_el_punto_de_partida:]
¿Y cómo hacer para saber cuáles cuál es el primero de los últimos 25 caracteres? Probá restar 25 al largo de la lista 😉primera_estrofa
, escribiendo, simplemente, primera_estrofa
. El problema es que se verá mezclado con varios caracteres extraños. Probá usar entonces print(primera_estrofa)
. No te preocupes: ya hablaremos de esto de breve.Probá las siguientes consultas y verificá que devuelvan lo mismo:
def listo():
pass
primera_estrofa = """
Aquí me pongo a cantar
al compás de la vigüela,
que el hombre que lo desvela
una pena estraordinaria,
como la ave solitaria
con el cantar se consuela
""".strip()
>>> True
True
¡Bien! Probablemente te haya resultado bastante tedioso resolver la tercera tarea:
primera_estrofa[len(primera_estrofa) - 25:]
al fin y al cabo, no teníamos ninguna forma para indicar de forma más sencilla que queríamos los últimos 25 elementos…. ¿o sí? ¡Averguémoslo!
¡Los segmentos [:]
y el operador de indexación []
no serían tan útiles si no pudieramos contar también de atrás para adelante! ⬅️ Por eso es que Python nos permite utilizar:
0
y cuentan los elementos desde la primera posición hasta la última;-1
y cuentan los elementos desde la última posición hasta la primera.Por ejemplo, esto nos permitirá entender al string "hola mundo"
de dos formas diferentes…
h | o | l | a | m | u | n | d | o | ||
---|---|---|---|---|---|---|---|---|---|---|
➡️ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
⬅️ | -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
… y hacer operaciones como las siguientes:
>>> "hola mundo"[:4] # los primeros 4 caracteres, como ya conocemos
"hola"
>>> "hola mundo"[:-1] # todos los caracteres salvo el último
"hola mund"
>>> "hola mundo"[-5:] # los último 5 caracteres
"mundo"
>>> "hola mundo"[0] # primer carácter, como ya conocemos
"h"
>>> "hola mundo"[-1] # último carácter
"o"
>>> "hola mundo"[-2] # anteúltimo carácter
"d"
¡Pongamos todo lo visto en práctica! Definí:
- una función
sin_extremos
que tome una lista y devuelva otra igual pero sin su primer y último elemento;- una función
extremos
que haga exactamente lo contrario, es decir, tome una lista y devuelva otra con solamente el primer y último elemento.>>> sin_extremos([4, 5, 10, 2, 3]) [5, 10, 2] >>> extremos([4, 5, 10, 2, 3]) [4, 3]
En extremos
no va a alcanzar con sólo extraer segmentos o acceder por indices: vas a tener que de alguna forma crear una nueva lista especificando sus elementos. 🤔
Probá las siguientes consultas y verificá que devuelvan lo mismo:
>>> sin_extremos([4, 5, 10, 2, 3])
[5, 10, 2]
>>> sin_extremos([4, 5, 10, 2, 3, 9])
[5, 10, 2, 3]
>>> sin_extremos([4, 5, 10])
[5]
>>> sin_extremos([40, 20])
[]
>>> extremos([4, 5, 10, 2, 3])
[4, 3]
>>> extremos([4, 3])
[4, 3]
>>> extremos([1, 2, 5])
[1, 5]
🛑 Antes de continuar vamos a hacer un alto en el camino para ver una vaca 🐄 otra payada:
payada_de_la_vaca = """
Dígame usted compañero
y conteste con prudencia
Cual es la mansa paciencia
que puebla nuestras praderas
Y en melancólica espera
con abnegada paciencia
Nos da alimento y abrigo
Fingiendo indiferencia
"""
¿Tres comillas? ¿Es un error de tipeo? ¡No! En Python podemos escribir textos de varias líneas si los colocamos entre triples comillas dobles 🕶️. Si bien esto funciona muy bien, tiene un pequeño problema: cuando queramos verlo en la consola, aparecerán unos muy peculiares \n
:
>>> payada_de_la_vaca
'\nDígame usted compañero\ny conteste con prudencia\nCual es la mansa paciencia\nque puebla nuestras praderas\nY en melancólica espera \ncon abnegada paciencia\nNos da alimento y abrigo\nFingiendo indiferencia\n'
Este \n
, llamado salto de línea, representa que allí, antes del siguiente carácter, debe haber un enter ↵
. Perfecto, pero ¿y si queremos “escribir” el texto en la pantalla, con verdaderos enters en lugar de estos \n
? Démosle la bienvenida al procedimiento print
, que imprime los textos tal como queremos que se vean:
> print(payada_de_la_vaca)
Dígame usted compañero
y conteste con prudencia
Cual es la mansa paciencia
que puebla nuestras praderas
Y en melancólica espera
con abnegada paciencia
Nos da alimento y abrigo
Fingiendo indiferencia
¿Y qué pasará si usamos print con string que no contienen saltos de lína? ¿Y si imprimimos otros tipos de datos? Averigualo probando lo siguiente y comparando resultados:
>>> 5 >>> print(5) >>> [1, 2, 3] >>> print([1, 2, 3]) >>> "a la voz de aura" >>> print("a la voz de aura")
Como vemos, print
imprime el pantalla el valor recibido y funciona con listas, strings, y… casi cualquier tipo de dato. Y como todo buen procedimiento, ¡no devuelve nada!
Por otro lado, cuando estamos en la consola los resultados son apenas diferentes cuando usamos strings. Por eso es que no fue necesario… hasta ahora. 😛
¡Y llegamos al plato fuerte de la lección 🍝!
De todas las cosas interesantes que podemos hacer con las secuencias de cosas, probablemente la más poderosa sea la de recorrer cada uno de sus elementos 🚶, utilizando la estructura de control for
:
def imprimir_cada_elemento(elementos):
for elemento in elementos:
print(elemento)
Esta estructura de control nos permitirá…
…no, mejor no te contamos qué hace exactamente
for
😈. Descubrilo probando el procedimientoimprimir_cada_elemento
en la consola:>>> imprimir_cada_elemento(["Violeta", "Mercedes", "Natalia", "Charo", "María Elena"]) >>> imprimir_cada_elemento([True, False, False, True]) >>> imprimir_cada_elemento("adivinador") >>> imprimir_cada_elemento(range(1, 10)) # range en inglés significa rango >>> imprimir_cada_elemento(range(5, 30, 2)) # prestá atención al tercer argumento de range
Cuando termines, escribí
listo()
No olvides que los procedimientos no devuelven nada. Esto significa que ni print
ni imprimir_cada_elemento
están retornando valores, sino que sólo los están imprimiendo.
Ah, ¿y qué hace range
? También te lo dejamos a vos para que lo descubras 😇
Probá las siguientes consultas y verificá que devuelvan lo mismo:
def imprimir_cada_elemento(elementos):
for elemento in elementos:
print(elemento)
def listo():
pass
>>> True
True
¡Acaban de pasar varias cosas! Primero, han entrado en escena los rangos, que son otro tipo de dato de Python que representa una secuencia de números, que puede ser:
range(1, 10)
, que no es nada más y nada menos que la secuencia de los números del 1
al 9
(sí, el último no entra ❗)range(0, 10, 3)
, que son los números del 0
al 9
dando saltos de a 3: 0
, 3
, 6
, 9
Por otro lado, acabamos de ver también que el for ... in
nos permite “visitar” a cada elemento de una lista, string o rango de números, y hacer algo con éste. Para ello, esta estructura tiene tres partes:
in
nos permite especificar qué secuencia de elementos vamos a recorrer;for
nos permite elegir un nombre con el que nos referiremos a cada elemento de la secuencia;:
tendremos una o más acciones que ejecutaremos al visitar cada elemento. ⚠️ ¡Cuidado! Tienen que estar tabuladas respecto de la línea del for
En este caso, en imprimir_cada_elemento
elegimos:
elementos
:elemento
;print
.Muy interesante, pero no parece que hayamos hecho nada muy útil 😕. ¿Podremos hacer cosas más que sólo mostrar elementos?
Veamos ahora una nueva operacion_misteriosa
:
def operacion_misteriosa(elementos):
cantidad = 0
for elemento in elementos:
cantidad += 1
return cantidad
¿Qué hace? ¿Qué devuelve? ¿Te recuerda a algo conocido? ¿Tiene algo que llama la atención?
😵 ¡Muchas preguntas! ¡Marcá todas las opciones que creas correctas!
💡 Recordá que cantidad += 1
es lo mismo que escribir cantidad = cantidad + 1
. En otras palabras, estamos actualizando la variable local cantidad
, incrementádola de a uno en uno.
operacion_misteriosa
es una función, porque devuelve algooperacion_misteriosa
es un procedimiento, porque no devuelve nadaoperacion_misteriosa
visita cada elemento de elementos
, e incrementa cantidad
en 1
por cada unooperacion_misteriosa
inicializa una variable local cantidad
en 0
operacion_misteriosa
inicializa una variable global cantidad
en 0
operacion_misteriosa
hace lo mismo que la función len
operacion_misteriosa
no usa la variable elemento
operacion_misteriosa
hace exactamente lo mismo que la función len
: cuenta la cantidad de elementos en una secuencia 😄. Además empieza a darnos una idea de lo poderoso que es combinar variables locales con la estructura for
.
Y eso que aún no usamos realmente a cada elemento
. 🤭
Las cosas se ponen aún más interesantes cuando combinamos todo lo anterior. Por ejemplo, esta función sumatoria
…
def sumatoria(numeros):
suma = 0
for numero in numeros:
suma += numero
return suma
… calcula la suma de todos los elementos de una lista de números…
>>> sumatoria([10, 5, 20])
35 # porque es 10 + 5 + 20
>>> sumatoria([])
0 # porque la sumatoria de una lista vacía es 0
…o incluso de un rango:
>>> sumatoria(range(1, 6))
15 # porque es 1 + 2 + 3 + 4 + 5
Veamos si va entendiendo: completá la definición de la función
productoria
que, al igual quesumatoria
, toma una secuencia de números, pero en lugar de sumarlos a todos, los multiplica:productoria([10, 2, 3]) 60 # porque es 10 * 2 * 3 productoria([3, 3, 2, 4]) 72 # porque es 3 * 3 * 2 * 4 productoria([8]) 8 # porque la productoria de un número es ese mismo número productoria([]) 1 # porque la productoria de un una lista vacía es 1
¿Con cuanto deberás inicializar la variable local en la que acumularás los resultados? ¿También con 0
o te convendrá otro valor?
Probá las siguientes consultas y verificá que devuelvan lo mismo:
>>> productoria([10, 2, 3])
60
>>> productoria([3, 3, 2, 4])
72
>>> productoria([8])
8
>>> productoria(range(1, 10))
362880
>>> productoria([6])
6
>>> productoria([])
1
¡Bien! 👏 Si bien la función productoria
no existe en Python, sí existe la función sum
que calcula una sumatoria tal como vimos acá.
Lo que tienen estas dos funciones en común (y casi todas las que veremos a continuación) es que se basan en la estructura de un acumulador: una variable local que inicializamos con un valor de base, y cada vez que visitemos un elemento, la actualizaremos. Y al final, retornaremos ese resultado acumulado.
¡Pero el for
no se trata sólo de números! Por ejemplo también podríamos utilizarlo para acumular un resultado booleano. 😮
Agus quiere saber si en alguna de sus últimas marcas de natacion 🏊 superó su objetivo personal de 3 minutos:
>>> alguna_vez_supero_objetivo([3.2, 3.4, 3.01, 3.08])
False # todas sus marcas fueron de más de 3 minutos
>>> alguna_vez_supero_objetivo([3.1, 3.2, 2.9, 3.1])
True # una de sus marcas (2.9) fue menor a 3 minutos
Y para eso definió la siguiente función:
def alguna_vez_supero_objetivo(duraciones):
supero = False # en principio, no se superó la marca de 3 minutos
for duracion in duraciones:
supero = supero or duracion < 3 # pero si alguna de ellas es menor a 3 minutos,
# entonces sí la habrá superado
return supero
Como vemos, acá la variable local que estamos usando de acumulador es booleana, y en cada iteración (es decir, cada vez que visitamos una duracion
) actualizaremos su valor, para saber si esta duracion
o alguna de las anteriores fue menor a 3.
¡Ahora te toca a vos! Dani tampoco quiere perder la práctica diaria de fútbol ⚽ y necesita una función
todos_los_dias_un_poco
que reciba una lista con cuántos minutos practicó cada cada día, y retorne si su práctica diaria fue siempre mayor a 30 minutos:>>> todos_los_dias_un_poco([35, 40, 32, 60]) True # todos los días practicó más de 30 minutos >>> todos_los_dias_un_poco([15, 45, 90, 0]) False # un día practicó solo 15 minutos, y otro no practicó nada
Nuevamente, te dejamos el molde de la función para que te sirva como punto de partida.
Probá las siguientes consultas y verificá que devuelvan lo mismo:
>>> todos_los_dias_un_poco([35, 40, 32, 60])
True
>>> todos_los_dias_un_poco([15, 45, 90, 0])
False
Y por supuesto, también podemos usar el for
con listas de strings
Definí la función
juntar
que tome por parámetro una lista de strings y los una de la siguiente forma:>>> juntar(["super", "califragilistico", "espialidoso"]) "supercalifragilisticoespialidoso" >>> juntar(["cuatri", "motor"]) "cuatrimotor" >>> juntar([]) ""
Te dejamos como inspiración la definición de sumatoria
del ejercicio anterior:
def sumatoria(numeros):
suma = 0
for numero in numeros:
suma += numero
return suma
Probá las siguientes consultas y verificá que devuelvan lo mismo:
>>> juntar(["super", "califragilistico", "espialidoso"])
"supercalifragilisticoespialidoso"
>>> juntar([])
""
>>> juntar(["cuatri", "motor"])
"cuatrimotor"
¿Y si ahora quisieramos juntar listas de strings, pero indicando además un separador? Por ejemplo:
>>> juntar(" ", ["Nicki", "Nicole"])
"Nicki Nicole"
>>> juntar(",", ["Londra", "Paulo"])
"Londra,Paulo"
>>> juntar("", ["W", "O", "S"])
"WOS" # podemos seguir uniendo sin separadores si pasamos
# la lista vacía como argumento
¡Mejoremos nuestra funcion anterior! Modificá
juntar
para que podamos recibir también un separador. El separador es lo que va a ir entre cada string.
💡 Quizás, para evitar que te sobren separadores al principio o al final, te convenga extaer segmentos como vimos al inicio de la lección.
Por otro lado, para hacer las cosas un poco más fáciles, no es necesario que la nueva versión de juntar
funcione para la lista vacía 😌. Pero si lo hace, ¡mucho mejor 🕶️!
Probá las siguientes consultas y verificá que devuelvan lo mismo:
>>> juntar("", ["super", "califragilistico", "espialidoso"])
"supercalifragilisticoespialidoso"
>>> juntar("", ["cuatri", "motor"])
"cuatrimotor"
>>> juntar(" ", ["hola", "mundo"])
"hola mundo"
>>> juntar(",", ["hola", "mundo"])
"hola,mundo"
¡Bien! Esta versión final de nuestra función juntar
también existe en Python, y se llama str.join
🤓
¡Que suerte que te encontramos, pensábamos que ya te ibas! ¿Podrías quedarte un poco más en esta lección? Es que Agus y Dani nos pidieron les demos una mano para llevar cuenta de sus estadísticas.
Por ahora, para Agus ya definimos esta función…
def cuantas_veces_supero_objetivo(duraciones):
veces = 0
for duracion in duraciones:
if duracion < 3:
veces += 1
return veces
…que es muy parecida a la que ya habíamos definido, pero tiene una novedad: ahora estamos contando cuantas veces se superó el objetivo. Y para eso necesitamos un if
, con algunas novedades:
for
, tabulado, y no retorna nada;else
: si la condición no se cumple, no hace nada.En otras palabras: en cada iteración, si la condición duracion < 3
se cumple, el acumulador veces
se incrementará, y en caso contrario, permanecerá igual.
Sabiendo esto, ahora ayudemos a Dani a definir
cuantas_veces_entreno_lo_suficiente
, que retorna la cantidad de veces que entrenó más de 30 minutos.>>> cuantas_veces_entreno_lo_suficiente([35, 40, 32, 60]) 4 # todos los días practicó más de 30 minutos >>> cuantas_veces_entreno_lo_suficiente([15, 45, 90, 0]) 2 # sólo dos días entrenó más de 30 minutos
Probá las siguientes consultas y verificá que devuelvan lo mismo:
>>> cuantas_veces_entreno_lo_suficiente([35, 40, 32, 60])
4
>>> cuantas_veces_entreno_lo_suficiente([15, 45, 90, 0])
2
Estabamos por proponerte jugar a un conocido juego que consiste en encontrar a un personaje de pulóver blanco y rojo, pero por problemas de copyright vamos a hacer una versión de bajo presupuesto ©️: ¿Dónde están las llaves?
Usando lo visto en esta lección, definí la función
donde_estan_las_llaves
que tome un string con emojis y nos diga en qué posición están las llaves, contando desde1
:>>> donde_estan_las_llaves("🌂🐍🔑👛") 3 >>> donde_estan_las_llaves("🔑🔥👓") 1 >>> donde_estan_las_llaves("🍪🍪🍪🍪🍪🍪🍪🔑🧉") 8
Considerá que las llaves siempre están.
Como podrás ver, los strings pueden tener emojis. Así que los emojis son caracteres, por lo que podemos hacer cosas de este estilo:
>>> "🍪" == "👓"
False
Probá las siguientes consultas y verificá que devuelvan lo mismo:
>>> donde_estan_las_llaves("🔑🔥👓")
1
>>> donde_estan_las_llaves("👓🔑🔥")
2
En esta guía aprendiste algo muy importante: cómo hacer que la computadora repita tareas, usando la estructura de control for
. Además, conociste los segmentos (slices), que nos permiten extrar rebanadas de listas y strings, los y rangos, que nos permiten generar números enteros entre otros dos. Y todo esto lo combinamos con el poder de las listas, para agrupar elementos y hacer tareas repetitivas con todos ellos.
Este caldero de conocimientos ya tiene todos los ingredientes de la pócima de la programación… 🧙♀️🧙♂️
…pero no tan rápido, ¡aún falta uno! 🔮 Acompañanos a la siguiente lección para revelar el misterio de los diccionarios.