Versión imprimible multipagina. Haga click aquí para imprimir.
Conceptos Iniciales
1 - Variables y Tipos de Datos
Variables
Una variable es un contenedor para almacenar datos en la memoria de la computadora. Podemos pensar en ella como una caja con una etiqueta. La etiqueta es el nombre de la variable y dentro de la caja se almacena su valor.
Para declarar una variable en Python solo escribimos el nombre y le asignamos un valor:
edad = 28
precio = 19.95
soltero = True
Los nombres de variables deben comenzar con letras o guión bajo, y sólo pueden contener letras, números y guiones bajos. Se recomienda usar nombres significativos que representen el propósito de la variable.
En Python las variables no necesitan ser declaradas con un tipo particular. El tipo se infiere automáticamente al asignar el valor:
edad = 28 # edad es de tipo entero (int)
precio = 19.95 # precio es de tipo float
estudiante = True # soltero es de tipo booleano
Una vez asignada, una variable puede cambiar su valor en cualquier momento:
edad = 30 # Cambiamos edad a 30
Alcance y tiempo de vida
El alcance de una variable se refiere a las partes del código donde está disponible. Las variables declaradas fuera de funciones son globales y están disponibles en todo el archivo. Las variables dentro de una función son locales y solo visibles dentro de ella.
El tiempo de vida es el período durante el cual existe la variable en memoria. Las variables locales existen mientras se ejecuta la función, luego son destruidas. Las globales existen mientras el programa está en ejecución.
Asignación
La asignación con el operador =
permite cambiar o inicializar el valor de una variable:
numero = 10
numero = 20 # Ahora numero vale 20
También existen los operadores de asignación compuesta como +=
y -=
que combinan una operación y asignación:
numero += 5 # Suma 5 a numero (numero = numero + 5)
numero -= 2 # Resta 2 a numero
Tipos de datos
Los tipos de datos definen el tipo de valor que puede almacenar una variable. Python tiene varios tipos incorporados, incluyendo:
Numéricos: Para almacenar valores numéricos como enteros, flotantes, complejos:
entero = 10
flotante = 10.5
complejo = 3 + 4j
Cadenas: Para almacenar texto:
texto = "Hola Mundo"
Booleano: Para valores lógicos Verdadero o Falso:
variable_verdadera = True
variable_falsa = False
Colecciones: Para almacenar múltiples valores como listas, tuplas y diccionarios:
Listas: Secuencias mutables de valores:
lista = [1, 2, 3]
Tuplas: Secuencias inmutables de valores:
tupla = (1, 2, 3)
Diccionarios: Estructuras de pares llave-valor:
diccionario = {"nombre":"Juan", "edad": 20}
Es importante elegir el tipo de dato que mejor represente la información que queremos almacenar.
Operadores
Los operadores nos permiten realizar operaciones con valores y variables en Python. Algunos operadores comunes son:
Aritméticos:
+, -, *, /, %, //, **
Comparación:
==, !=, >, <, >=, <=
Lógicos:
and, or, not
Asignación:
=, +=, -=, *=, /=
Veamos ejemplos concretos de expresiones usando estos operadores en Python:
# Aritméticos
5 + 4 # Suma, resultado 9
10 - 3 # Resta, resultado 7
4 * 5 # Multiplicación, resultado 20
# Comparación
5 > 4 # Mayor que, resultado Verdadero
7 < 10 # Menor que, resultado Verdadero
# Lógicos
True and False # Resultado False
True or False # Resultado True
not True # Resultado False
# Asignación
numero = 10
numero += 5 # Suma 5 a numero, equivalente a numero = numero + 5
Cada tipo de operador trabaja con tipos de datos específicos. Debemos usarlos de forma consistente según el tipo de datos de nuestras variables.
Conversiones de tipo
A veces necesitamos convertir un tipo de dato a otro para realizar ciertas operaciones. En Python podemos convertir de forma explícita o implícita:
Explícita: Usando funciones como int()
, float()
, str()
:
flotante = 13.5
entero = int(flotante) # convierte 13.5 a 13
texto = "100"
numero = int(texto) # convierte "100" a 100
Implícita: Python convierte automáticamente en algunos casos:
entero = 100
flotante = 3.5
resultado = entero + flotante # resultado es 103.5, entero se convirtió a float
Algunas conversiones pueden generar pérdida de datos o errores:
flotante = 13.5
entero = int(flotante)
print(entero) # 13, se pierden los decimales
Para prevenir esto debemos elegir explícitamente conversiones que tengan sentido para nuestros datos.
Conclusión
En este artículo revisamos conceptos clave como variables, operadores, tipos de datos y conversiones en Python. Aplicar bien estos conceptos te permitirá manipular datos de forma eficiente en tus programas. Recomiendo practicar con ejemplos propios para ganar experiencia en usar estas características. ¡Éxitos en tu aprendizaje de Python!
¡Felicitaciones por llegar hasta acá! Espero que este recorrido por el universo de la programación te haya resultado tan interesante como lo fue para mí al escribirlo.
Queremos conocer tu opinión, así que no dudes en compartir tus comentarios, sugerencias y esas ideas brillantes que seguro tenés.
Además, para explorar más allá de estas líneas, date una vuelta por los ejemplos prácticos que armamos para vos. Todo el código y los proyectos los encontrarás en nuestro repositorio de GitHub learn-software-engineering/examples-programming.
Gracias por ser parte de esta comunidad de aprendizaje. ¡Seguí programando y explorando nuevas areas en este fascinante mundo del software!
2 - Operaciones de Entrada y Salida
Salida a pantalla
Python también provee funciones para enviar la salida de un programa a la “salida estándar”, generalmente la pantalla o terminal1.
La función print()
muestra el valor pasado como parámetro:
nombre = "Eric"
print(nombre) # muestra "Eric"
Podemos imprimir múltiples valores separados por comas2:
print("Hola", nombre, "!") # muestra "Hola Eric!"
También podemos usar valores literales sin asignar a variables3:
print("2 + 3 =", 2 + 3) # muestra "2 + 3 = 5"
Formateo de salida
Python provee varias formas de dar formato a la salida4:
f-Strings: Permiten insertar variables dentro de una cadena:
nombre = "Eric"
print(f"Hola {nombre}") # muestra "Hola Eric"
%s: Inserta cadenas de texto en una cadena de formato:
nombre = "Eric"
print("Hola %s" % nombre) # muestra "Hola Eric"
%d: Inserta números enteros:
valor = 15
print("El valor es %d" % valor) # muestra "El valor es 15"
.format(): Inserta valores en una cadena de formato:
nombre = "Eric"
print("Hola {}. Bienvenido".format(nombre))
# muestra "Hola Eric. Bienvenido"
Estas opciones de formateo nos permiten interpolar variables y valores en cadenas de texto para generar outputs personalizados. Podemos combinar múltiples valores y formateos en una sola cadena de salida2.
Entrada desde el teclado
Python provee funciones incorporadas para leer datos ingresados por el usuario en tiempo de ejecución. Esto se conoce como “entrada estándar”4.
La función input()
permite leer un valor ingresado por el usuario y asignarlo a una variable. Por ejemplo:
nombre = input("Ingresa tu nombre: ")
Esto muestra el mensaje “Ingresa tu nombre: " y espera a que el usuario escriba un texto y presione Enter. Ese valor se asigna a la variable nombre
2.
La función input()
siempre regresa una cadena de texto. Si queremos pedir un número u otro tipo de dato, debemos convertirlo usando int()
, float()
, etc1:
edad = int(input("Ingresa tu edad: "))
pi = float(input("Ingresa el valor de pi: "))
Leyendo múltiples valores
Podemos pedir y leer varios valores en una misma línea separándolos con comas3:
nombre, edad = input("Ingresa nombre y edad: ").split()
El método split()
divide la entrada en partes y retorna una lista de cadenas. Luego asignamos los elementos de la lista a variables separadas.
También podemos leer varias líneas de entrada con un ciclo4:
nombres = [] # lista vacía
for x in range(3):
nombre = input("Ingresa un nombre: ")
nombres.append(nombre)
Este código lee 3 nombres ingresados por el usuario y los agrega a una lista.
Salida a un archivo
Además de imprimir a pantalla, podemos escribir la salida a un archivo usando la función open()
1:
archivo = open("datos.txt", "w")
Esto abre datos.txt
para escritura (“w”) y retorna un objeto archivo.
Luego usamos archivo.write()
para escribir a ese archivo3:
archivo.write("Hola mundo!")
archivo.write("Este texto va al archivo")
Debemos cerrar el archivo con archivo.close()
cuando terminamos4:
archivo.close()
También podemos usar with
para abrir y cerrar automáticamente2:
with open("datos.txt", "w") as archivo:
archivo.write("Hola mundo!")
# no hace falta cerrar, es automático
Lectura de archivos
Para leer un archivo usamos open()
con modo “r” y iteramos sobre el objeto archivo1:
with open("datos.txt", "r") as archivo:
for linea in archivo:
print(linea) # muestra cada línea del archivo
Esto imprime cada línea, incluyendo los saltos de línea.
Podemos leer todas las líneas a una lista con readlines()
3:
linenas = archivo.readlines()
print(linenas)
Para leer el contenido completo a una cadena usamos read()
4:
texto = archivo.read()
print(texto)
También podemos leer un número determinado de bytes o caracteres con read(n)
2.
Operaciones para el manejo de archivos
Existen varias funciones incorporadas para manipular archivos en Python1:
open()
- Abre un archivo y retorna un objeto archivoclose()
- Cierra el archivowrite()
- Escribe datos al archivoread()
- Lee datos del archivoreadline()
- Lee una línea del archivotruncate()
- Vacía el archivoseek()
- Mueve la posición de lectura/escriturarename()
- Renombra el archivoremove()
- Elimina el archivo
Estas funciones nos permiten efectuar operaciones avanzadas para leer, escribir y mantener archivos.
Conclusión
En este artículo explicamos en detalle operaciones de entrada y salida en Python, incluyendo leer de entrada estándar y escribir a salida estándar o archivos. Manejar correctamente la entrada y salida es esencial para muchas aplicaciones de Python. Recomiendo practicar con ejemplos propios para dominar estas funciones3.
¡Felicitaciones por llegar hasta acá! Espero que este recorrido por el universo de la programación te haya resultado tan interesante como lo fue para mí al escribirlo.
Queremos conocer tu opinión, así que no dudes en compartir tus comentarios, sugerencias y esas ideas brillantes que seguro tenés.
Además, para explorar más allá de estas líneas, date una vuelta por los ejemplos prácticos que armamos para vos. Todo el código y los proyectos los encontrarás en nuestro repositorio de GitHub learn-software-engineering/examples-programming.
Gracias por ser parte de esta comunidad de aprendizaje. ¡Seguí programando y explorando nuevas areas en este fascinante mundo del software!
Referencias
McKinney, W. (2018). Python for data analysis: Data wrangling with Pandas, NumPy, and IPython. O’Reilly Media. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Lutz, M. (2013). Learning Python: Powerful Object-Oriented Programming. O’Reilly Media, Incorporated. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Matthes, E. (2015). Python crash course: A hands-on, project-based introduction to programming. No Starch Press. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Downey, A. B. (2015). Think Python: How to think like a computer scientist. Needham, Massachusetts: Green Tea Press. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
3 - Control de Flujo
Condiciones: tomando decisiones en el código
La vida está llena de decisiones: “Si llueve, llevaré un paraguas. De lo contrario, usaré anteojos de sol”. Estas decisiones también están presentes en el mundo de la programación. Las condiciones son como preguntas que la computadora se hace. Nos permiten tomar decisiones y ejecutar código específico dependiendo de una condición1. Pueden ser simples como “¿Está lloviendo?” o complejas como “¿Es fin de semana y tengo menos de $100 en mi cuenta bancaria?”.
if
La estructura if
nos permite evaluar condiciones y tomar decisiones basadas en el resultado de esa evaluación.
edad = 15
if edad >= 18:
print("Eres mayor de edad")
El código anterior permite ejecutar una porción de código si la edad de una persona es mayo o igual a 18 años.
if-else
Cuando se desea ejecutar un código alternativo si la condición es falsa, utilizamos la estructura if-else
edad = 21
if edad >= 18:
print("Eres mayor de edad")
else:
print("Eres menor de edad")
En este caso, se determina si la persona es mayor de edad, o menor de edad, el mensaje mostrado es diferente
if-elif-else
Cuando las condiciones son múltiples y no es suficientes con dos caminos, se utiliza la estructura if-elif-else
para evaluarlas forma encadenada.
edad = 5
if edad <= 13:
print("Eres un niño")
elif edad > 13 and edad < 18:
print("Eres un adolescente")
else:
print("Eres un adulto")
En el código anterior se observan tres caminos claros, uno para cuando la edad es menor o igual a 13 años, otro para cuando la edad esta entre 13 y 18 y otro para cuando es mayor o igual a 18.
Otra manera de resolver este problema es mediante la estructura switch-case
, que, aunque Python no incorpora de manera nativa, como si lo hacen otros lenguajes como Java o C++, es una herramienta importante para conocer. Esta estructura permite a los programadores manejar múltiples condiciones de manera más organizada que una serie de if-elif-else
.
En Java, por ejemplo:
int dia = 3;
switch(dia) {
case 1:
System.out.println("Lunes");
break;
case 2:
System.out.println("Martes");
break;
case 3:
System.out.println("Miércoles");
break;
// ... y así sucesivamente
default:
System.out.println("Día no válido");
}
En el ejemplo anterior, dependiendo del valor de dia
, se imprimirá el día correspondiente2.
Bucles: repitiendo acciones
A veces, en programación, necesitamos repetir una acción varias veces. En lugar de escribir el mismo código varias veces, podemos usar bucles. Estos, permiten repetir la ejecución de un bloque de código mientras se cumpla una condición3.
while
El bucle while
es útil cuando queremos repetir una acción basada en una condición.
# Imprime del 1 al 5
i = 1
while i <= 5:
print(i)
i = i + 1
do-while
Similar a while
pero garantiza al menos una ejecución dado que primero se ejecuta el bloque de código y luego se evalúa la condición. Python no implementa esta estructura, pero otros lenguajes como Java y C++ sí lo hacen.
int i = 1;
do {
System.out.println(i);
i++;
} while(i <= 5);
int numero = 0;
do {
std::cout << "Hola, mundo!" << std::endl;
numero++;
} while (numero < 5);
for
El bucle for
es útil cuando sabemos cuántas veces queremos repetir una acción.
for i in range(5):
print("Hola, mundo!")
El código anterior imprimirá “Hola, mundo!” cinco veces.
También podemos iterar sobre los elementos de una lista u objeto iterable:
nombres = ["María", "Florencia", "Julián"]
for nombre in nombres:
print(f"Hola {nombre}")
# Imprime
# Hola María
# Hola Florencia
# Hola Julián
Las sentencias break
y continue
Podemos usar break
para terminar el bucle y continue
para saltar a la siguiente iteración.
El break
se usa para terminar completamente el bucle cuando se cumple una condición, en el ejemplo siguiente, cuando i
llega a 5.
# Ejemplo de break
i = 0
while i < 10:
print(i)
if i == 5:
break
i += 1
# Imprime:
# 0
# 1
# 2
# 3
# 4
# 5
El continue
se usa para saltarse una iteración del bucle y continuar con la siguiente cuando se cumple una condición. Aquí lo usamos para saltarnos los números pares.
# Ejemplo de continue
i = 0
while i < 10:
i += 1
if i % 2 == 0:
continue
print(i)
# Imprime:
# 1
# 3
# 5
# 7
# 9
Anidamiento: combinando estructuras
Las estructuras de control de flujo pueden anidarse dentro de otras. Por ejemplo, podemos tener bucles dentro de bucles o condiciones dentro de bucles.
for i in range(5):
for j in range(10):
if (i % 2 == 0 and j % 3 == 0):
print(f"i = {i}, j = {j}")
Este código imprimirá combinaciones de i
y j
sólo cuando i
sea divisible por 2 y j
sea divisible por 3, demostrando cómo los bucles se anidan y se ejecutan3.
Patrones de uso comunes
Existen patrones específicos para resolver necesidades habituales con control de flujo.
Búsqueda
Buscar un valor en una colección:
frutas = ["manzana", "naranja"]
buscando = "naranja"
encontrado = False
for fruta in frutas:
if fruta == buscando:
encontrado = True
break
if encontrado:
print("Fruta encontrada!")
Acumulación
Acumular valores incrementales en un bucle.
total = 0
for i in range(10):
total += i
print(total) # Suma de 0..9 = 45
Diagramas de flujo: la ruta visual hacia el entendimiento del código
Los programadores, sin importar si son principiantes o expertos, a menudo se encuentran enfrentando desafíos que requieren una planificación detallada antes de sumergirse en el código. Aquí es donde los diagramas de flujo entran en juego como una herramienta esencial. Estos diagramas son representaciones gráficas de los procesos y la lógica detrás de un programa o sistema. En este artículo, desentrañaremos el mundo de los diagramas de flujo, desde sus conceptos básicos hasta las técnicas avanzadas, y cómo pueden beneficiar a programadores de todos los niveles.
Un diagrama de flujo es una representación gráfica de un proceso. Utiliza símbolos específicos para representar diferentes tipos de instrucciones o acciones. Su objetivo principal es simplificar la comprensión de un proceso, mostrando paso a paso cómo fluye la información o las decisiones. Estos diagramas:
- Facilitan la comprensión de procesos complejos.
- Ayudan en la fase de diseño y planificación de un programa.
- Sirven como documentación y referencia para futuros desarrollos.
Los diagramas de flujo son una herramienta poderosa que no solo beneficia a los principiantes, sino también a los programadores experimentados. Ofrecen una visión clara y estructurada de un proceso o programa, facilitando la planificación, el diseño y la comunicación entre los miembros del equipo.
Elementos básicos
Los diagramas de flujo constan de varios símbolos, cada uno con un significado específico:
- Ovalo: Representa el inicio o el fin de un proceso.
- Rectángulo: Denota una operación o instrucción.
- Diamante: Indica una decisión basada en una condición.
- Flechas: Muestran la dirección del flujo.
graph TD; start((Inicio)) process[Proceso] decision{¿Repetir?} final((Final)) start --> process; process --> decision; decision --> |Si| process decision --> |No| final
Ejemplos
Vamos a diseñar un diagrama de flujo para un programa que pida un número y nos diga si es par o impar.
graph TB inicio((Inicio)) entrada[Ingresar número] decision{¿Es par?} esPar[Es par] esImpar[Es impar] final((Final)) inicio --> entrada entrada --> decision decision --> |Si| esPar decision --> |No| esImpar esPar --> final esImpar --> final
Conforme los programas se vuelven más complejos, es posible que necesites incorporar bucles, múltiples condiciones y otros elementos avanzados en tu diagrama de flujo. Por ejemplo, aquí diagramamos un programa que sume los números desde el 1 al número ingresado por el usuario.
graph TD inicio((Inicio)) entrada[Ingresar número] setVariables[Establecer suma=0 y contador=1] bucle_condicion{¿contador <= N?} bucle_codigo[Sumar valor e incrementar el contador] resultado[Mostrar suma] final((Final)) inicio --> entrada entrada --> setVariables setVariables --> bucle_condicion bucle_condicion --> |Si| bucle_codigo bucle_codigo --> bucle_condicion bucle_condicion --> |No| resultado resultado --> final
Conclusión
El control de flujo es el corazón de la programación. Sin él, los programas serían secuencias lineales de acciones sin la capacidad de tomar decisiones o repetir tareas. Al dominar estas estructuras, no solo mejoras tu capacidad para escribir código, sino también tu capacidad para pensar lógicamente y resolver problemas complejos.
¡Felicitaciones por llegar hasta acá! Espero que este recorrido por el universo de la programación te haya resultado tan interesante como lo fue para mí al escribirlo.
Queremos conocer tu opinión, así que no dudes en compartir tus comentarios, sugerencias y esas ideas brillantes que seguro tenés.
Además, para explorar más allá de estas líneas, date una vuelta por los ejemplos prácticos que armamos para vos. Todo el código y los proyectos los encontrarás en nuestro repositorio de GitHub learn-software-engineering/examples-programming.
Gracias por ser parte de esta comunidad de aprendizaje. ¡Seguí programando y explorando nuevas areas en este fascinante mundo del software!
Referencias
Lutz, M. (2013). Learning Python: Powerful Object-Oriented Programming. O’Reilly Media, Incorporated. ↩︎
Deitel, P., & Deitel, H. (2012). Java: How to program. Upper Saddle River, NJ: Prentice Hall. ↩︎
Matthes, E. (2015). Python crash course: A hands-on, project-based introduction to programming. San Francisco, CA: No Starch Press. ↩︎ ↩︎
4 - Funciones
¿Qué son las funciones?
Una función, en términos simples, es un bloque de código que se ejecuta sólo cuando es llamado. Puedes pensar en ella como un pequeño programa dentro de tu programa principal, diseñado para realizar una tarea específica1. Una función también puede verse como una caja negra: le pasamos una entrada (parámetros), ocurre algún procesamiento interno, y produce una salida (retorno).
Las funciones nos permiten segmentar nuestro código en partes lógicas, donde cada parte realiza una única acción. Esto brinda varios beneficios2:
- Reutilización: Una vez definida la función, podemos ejecutar (llamar) ese código desde cualquier lugar de nuestro programa cuantas veces sea necesario.
- Organización: Permite dividir un programa grande en partes más pequeñas y manejables.
- Encapsulamiento: Las funciones reducen la complejidad escondiendo los detalles de implementación internos.
- Mantenimiento: Si necesitamos realizar cambios, solo debemos modificar el código en un lugar (la función) en lugar de rastrear todas las instancias de ese código.
Procedimientos vs. Funciones
Es vital distinguir entre estos dos conceptos. Mientras que una función siempre devuelve un valor, un procedimiento realiza una tarea pero no devuelve nada. En algunos lenguajes, esta diferencia es más clara que en otros. Python, por ejemplo, tiene funciones que pueden o no devolver valores.
Anatomía de una función
En Python, una función se declara usando la palabra clave def
, seguida del nombre de la función y paréntesis. El código dentro de la función se denomina el cuerpo de la función3 y contiene el conjunto de instrucciones a ejecutar para cumplir con su tarea..
def mi_funcion():
print("¡Hola desde mi función!")
Para llamar o invocar una función, simplemente usamos su nombre seguido de paréntesis:
mi_funcion() # Salida: ¡Hola desde mi función!
Parámetros y argumentos
Las funciones se vuelven aún más poderosas cuando les pasamos información, conocida como parámetros. Estos actúan como “variables” dentro de la función y permiten que la función trabaje con diferentes datos cada vez que se llama.
Mientras que los parámetros son variables definidas en la definición de la función. Los argumentos son los valores reales pasados al llamar a la función.
def saludo(nombre):
print(f"¡Hola {nombre}!")
saludo("María")
# Salida:
# ¡Hola María!
Podemos definir valores por defecto para los parámetros Python permite parámetros por defecto, que tienen un valor predeterminado, lo cual hace opcional pasar esos argumentos al llamar la función. También permite parámetros nombrados que permiten pasar los argumentos en cualquier orden, especificando su nombre.
def saludo(nombre="María", repeticiones=3):
repeticion = 1
while repeticion <= repeticiones:
print(f"¡Hola {nombre}!")
repeticion += 1
saludo()
# Salida:
# ¡Hola María!
# ¡Hola María!
# ¡Hola María!
saludo("Florencia", 4)
# Salida:
# ¡Hola Florencia!
# ¡Hola Florencia!
# ¡Hola Florencia!
# ¡Hola Florencia!
saludo(repeticiones=2, nombre="Julián")
# Salida
# ¡Hola Julián!
# ¡Hola Julián!
Retorno de valores
Las funciones pueden devolver un resultado o valor de retorno usando la palabra reservada return
.
def area_circulo(radio):
return 3.14 * (radio ** 2)
resultado = area_circulo(10)
print(resultado) # Salida: 314
El valor de retorno se pasa de vuelta a donde se llamó la función y se puede asignar a una variable para usarlo.
Las funciones también pueden ejecutar alguna tarea sin devolver nada explícitamente. En Python esto se conoce como retornar None
.
Variables locales y globales
Las variables locales se definen dentro de una función y solo existen en ese ámbito, mientras que las variables globales están definidas fuera y pueden ser accedidas desde cualquier parte del código. Es crucial entender su alcance (dónde puede ser accesible una variable) y duración (cuánto tiempo vive una variable).
x = 10 # x es global
def suma():
y = 5 # y es local
return x + y
suma() # Salida: 15
print(y) # Error, y no existe fuera de la función
Podemos leer variables globales desde una función, pero si necesitamos modificarla debemos declararla global
.
x = 10
def suma():
global x
x = x + 5
suma()
print(x) # 15
Buenas prácticas
Al crear funciones debemos seguir ciertos principios y patrones4:
- El nombre de una función debe indicar claramente su propósito.
- Hacer las funciones pequeñas, simples y enfocadas en una tarea. Una función debe hacer una cosa y hacerla bien.
- Utilizar nombres descriptivos para las funciones y sus parámetros.
- Evitar efectos secundarios y modificación de variables globales.
- Documentar adecuadamente el propósito y uso de cada función.
- Limitar el número de parámetros, idealmente de 0 a 3 parámetros.
Seguir estas buenas prácticas nos ayudará a crear funciones reutilizables, encapsuladas y fáciles de mantener.
Conclusión
Las funciones son componentes fundamentales en la programación, permitiéndonos organizar, reutilizar y encapsular código. Definiendo funciones que realicen una sola tarea mantenemos nuestros programas simplificados, fáciles de entender y modificar. Al comprender y dominar este concepto, no solo mejoras la calidad de tu código sino también tu eficiencia como desarrollador.
¡Felicitaciones por llegar hasta acá! Espero que este recorrido por el universo de la programación te haya resultado tan interesante como lo fue para mí al escribirlo.
Queremos conocer tu opinión, así que no dudes en compartir tus comentarios, sugerencias y esas ideas brillantes que seguro tenés.
Además, para explorar más allá de estas líneas, date una vuelta por los ejemplos prácticos que armamos para vos. Todo el código y los proyectos los encontrarás en nuestro repositorio de GitHub learn-software-engineering/examples-programming.
Gracias por ser parte de esta comunidad de aprendizaje. ¡Seguí programando y explorando nuevas areas en este fascinante mundo del software!
Referencias
McConnell, S. (2004). Code Complete. Microsoft Press. ↩︎
Joyanes Aguilar, L. (2008). Fundamentos de programación: algoritmos, estructura de datos y objetos. McGraw-Hill. ↩︎
Python Software Foundation. (2022). Documentación oficial de Python. ↩︎
Kindler, E., & Krivy, I. (2011). Object-Oriented Simulation of systems with Java: A working introduction. BoD–Books on Demand. ↩︎
5 - Funciones Recursivas
Recursión: el arte de llamarse a sí mismo
Imagina una caja de espejos donde cada espejo refleja lo que ve en el siguiente, creando una serie infinita de reflejos. La recursión en programación es algo similar. Es una técnica donde una función se llama a sí misma directa o indirectamente[^1^]. Esto crea un ciclo en el cual la función resuelve un problema dividiéndolo en instancias más pequeñas del mismo problema, hasta llegar a un caso base sencillo de resolver.
Por ejemplo, imaginemos una función que imprime un contador regresivo:
def cuenta_regresiva(numero):
if numero > 0:
print(numero)
cuenta_regresiva(numero - 1)
else:
print("¡Despegue!")
cuenta_regresiva(5)
Esta función se llama recursivamente reduciendo el número cada vez hasta llegar a 0, y luego imprime el mensaje de despegue.
La recursión es un enfoque declarativo donde se enfoca en dividir un problema en casos recursivos sin necesidad de controlar explícitamente el bucle usando iteradores o contadores como en la programación imperativa.
La estructura de una función recursiva
El poder de la recursión radica en su simplicidad. Sin embargo, es esencial entender su estructura para evitar caer en trampas comunes. Una función recursiva típica tiene dos partes principales1:
- Caso base: El caso más simple con una solución conocida que no requiere recursión. Es la condición de parada, que detiene la recursión. Sin el caso base, caeríamos en una recursión infinita que eventualmente desborda la pila de llamadas.
- Caso recursivo: Es donde ocurre la mágica llamada recursiva. En este punto, la función se llama a sí misma con un argumento modificado, generalmente una versión reducida del problema original.
Ejemplos clásicos de recursión
Factorial
El factorial de un entero positivo \(n\) es el producto de todos los enteros positivos menores o iguales a \(n\). Por ejemplo:
- \(5! = 5 * 4 * 3 * 2 * 1 = 120\)
- \(4! = 4 * 3 * 2 * 1 = 24\)
- \(3! = 3 * 2 * 1 = 6\)
Aquí está el código en Python para calcular el factorial usando recursión:
def factorial(n):
if n == 1:
return 1 # Caso base
return n * factorial(n-1) # Caso recursivo
- Caso base: El caso base es la instancia más simple y pequeña del problema que puede responderse directamente. Para el factorial, cuando \(n = 1\), el resultado es \(1\).
- Caso recursivo: Si \(n\) es mayor que \(1\), la función se llama a sí misma con \(n-1\), y multiplica el resultado por \(n\).
Digamos que quieres calcular el factorial de \(5\), así que llamas a factorial(5)
.
Esto es lo que sucede:
- Paso 1: Como \(n = 5\) no es \(1\), la función llama a
factorial(4)
, luego multiplica el resultado por \(5\). - Paso 2: Ahora, dentro de
factorial(4)
, \(n = 4\), entonces la función llama afactorial(3)
, luego multiplica el resultado por \(4\). - Paso 3: Dentro de
factorial(3)
, \(n = 3\), así que llama afactorial(2)
, luego multiplica el resultado por \(3\). - Paso 4: Dentro de
factorial(2)
, \(n = 2\), así que llama afactorial(1)
, luego multiplica el resultado por \(2\). - Paso 5: Finalmente,
factorial(1)
alcanza el caso base, donde \(n = 1\), así que retorna \(1\).
Ahora los resultados se desenrollan:
factorial(2)
retorna \(2 * 1 = 2\)factorial(3)
retorna \(3 * 2 = 6\)factorial(4)
retorna \(4 * 6 = 24\)factorial(5)
retorna \(5 * 24 = 120\)
El resultado final es \(120\), que es el valor de \(5!\).
Aquí hay una representación visual de la pila de llamadas:
factorial(5)
-> factorial(4)
-> factorial(3)
-> factorial(2)
-> factorial(1)
return 1
return 2
return 6
return 24
return 120
Serie de Fibonacci
La serie de Fibonacci es una secuencia de números donde cada número es la suma de los dos anteriores. Comienza con \(0\) y \(1\), y cada número posterior es la suma de los dos números anteriores. Los primeros números de la secuencia son: \(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …\)
Aquí está el código en Python para calcular el \(n^th\) número de Fibonacci usando recursión de cola:
def fibonacci(n, a=0, b=1):
if n == 0:
return a
return fibonacci(n-1, b, a+b)
La función toma tres parámetros:
- \(n\): La posición del número deseado en la secuencia.
- \(a\) y \(b\): Dos números que ayudan en el cálculo de la secuencia.
Aquí hay un desglose de cómo funciona la función:
Caso Base: Si \(n\) es \(0\), la función devuelve \(a\). Este es el valor del \(n^th\) número en la secuencia.
Caso Recursivo: Si \(n\) no es \(0\), la función se llama a sí misma con \(n-1\), \(b\), y \(a+b\). Estos parámetros cambian la posición en la secuencia y preparan los siguientes números para la suma.
Supongamos que queremos encontrar el \(5^th\) número en la secuencia de Fibonacci llamando a fibonacci(5)
.
Esto es lo que sucede:
- Paso 1: Dado que \(n = 5\), llama a
fibonacci(4, 1, 1)
(porque \(a = 0\), \(b = 1\), \(a + b = 1\)). - Paso 2: Dado que \(n = 4\), llama a
fibonacci(3, 1, 2)
(porque \(a = 1\), \(b = 1\), \(a + b = 2\)). - Paso 3: Dado que \(n = 3\), llama a
fibonacci(2, 2, 3)
(porque \(a = 1\), \(b = 2\), \(a + b = 3\)). - Paso 4: Dado que \(n = 2\), llama a
fibonacci(1, 3, 5)
(porque \(a = 2\), \(b = 3\), \(a + b = 5\)). - Paso 5: Dado que \(n = 1\), llama a
fibonacci(0, 5, 8)
(porque \(a = 3\), \(b = 5\), \(a + b = 8\)). - Paso 6: Dado que \(n = 0\), devuelve \(a\), que es \(5\).
El resultado es \(5\), que es el \(5^th\) número en la secuencia de Fibonacci.
Aquí hay una representación visual de la pila de llamadas:
fibonacci(5, 0, 1)
-> fibonacci(4, 1, 1)
-> fibonacci(3, 1, 2)
-> fibonacci(2, 2, 3)
-> fibonacci(1, 3, 5)
-> fibonacci(0, 5, 8)
return 5
Ventajas y desventajas
La recursión tiene ciertas ventajas2:
- Puede resultar en soluciones simples y elegantes para problemas que se dividen fácilmente en subproblemas.
- Elimina la necesidad de control de bucles explícito.
- Sigue la estructura matemática de una definición recursiva.
Las desventajas incluyen:
- Puede ser menos eficiente (alto consumo de memoria) que la iteración debido a las llamadas repetidas y creación de marcos de pila.
- Demasiada recursión puede desbordar la pila de llamadas y causar errores.
- Puede ser más difícil de depurar y analizar que la iteración.
Por lo tanto, la recursión es una herramienta poderosa que debe usarse con discreción en los casos apropiados.
Recursión vs Iteración
La recursión y la iteración (usando ciclos) son paralelos y podemos usar cualquiera para resolver muchos problemas. Ambas técnicas tienen el potencial de resolver los mismos problemas, pero su implementación y eficiencia pueden variar. Tomemos el ejemplo del factorial:
Iterativo
def factorial_iterativo(n):
resultado = 1
for i in range(1, n+1):
resultado *= i
return resultado
Recursivo
def factorial_recursivo(n):
if n == 1:
return 1
return n * factorial(n-1)
La versión iterativa es más eficiente en términos de espacio, pero la recursiva es más limpia y fácil de entender. La elección entre recursión e iteración a menudo depende del problema específico, las restricciones de memoria y las preferencias del programador.
Conclusión
La recursión es una técnica clave que permite escribir algoritmos elegante, naturales y eficientes si se utiliza adecuadamente. Entender cómo dividir un problema en casos recursivos es esencial para dominar esta habilidad. La recursión ofrece una alternativa declarativa única para resolver problemas complejos sin necesidad de administrar bucles explícitos. Sin embargo, es crucial recordar siempre definir un caso base adecuado y ser consciente de las limitaciones de la recursión en términos de eficiencia y uso de memoria. Saber combinar recursión e iteración nos da flexibilidad al crear soluciones óptimas.
Como siempre, la clave está en encontrar el equilibrio adecuado y utilizar la herramienta correcta para el trabajo adecuado.
¡Felicitaciones por llegar hasta acá! Espero que este recorrido por el universo de la programación te haya resultado tan interesante como lo fue para mí al escribirlo.
Queremos conocer tu opinión, así que no dudes en compartir tus comentarios, sugerencias y esas ideas brillantes que seguro tenés.
Además, para explorar más allá de estas líneas, date una vuelta por los ejemplos prácticos que armamos para vos. Todo el código y los proyectos los encontrarás en nuestro repositorio de GitHub learn-software-engineering/examples-programming.
Gracias por ser parte de esta comunidad de aprendizaje. ¡Seguí programando y explorando nuevas areas en este fascinante mundo del software!