Versión imprimible multipagina. Haga click aquí para imprimir.
Programación
- 1: Introducción a la Programación
- 1.1: La Computadora
- 1.2: Sistemas de Numeración
- 1.3: Lógica Booleana
- 1.4: Configura tu Entorno de Desarrollo
- 2: Conceptos Iniciales
- 2.1: Variables y Tipos de Datos
- 2.2: Operaciones de Entrada y Salida
- 2.3: Control de Flujo
- 2.4: Funciones
- 2.5: Funciones Recursivas
- 3: Programación Orientada a Objetos
- 3.1: Clases y Objetos
- 3.2: Encapsulamiento
1 - Introducción a la Programación
La programación, en su esencia, es el acto de instruir a una máquina sobre cómo realizar una tarea específica. Es como si le estuvieras enseñando a tu perro a buscar la pelota, pero en este caso, el perro es tu computadora y la pelota es, por ejemplo, mostrar una foto en tu pantalla.
Ahora, es posible que pienses que programar es simplemente escribir líneas de código. En realidad, la programación es un proceso más amplio que incluye no solo escribir código, sino también resolver problemas, diseñar sistemas y pensar lógicamente.
En el universo de la programación, hay lenguajes de alto nivel y lenguajes de bajo nivel. Un lenguaje de bajo nivel, como el ensamblador, está más cerca de lo que la máquina entiende, mientras que un lenguaje de alto nivel, como Python o JavaScript, es más amigable para nosotros, los humanos. Imagina tener una conversación: los lenguajes de alto nivel son como charlar con un amigo en un café, mientras que los lenguajes de bajo nivel son como intentar comunicarte con alguien que habla un dialecto muy particular y localizado.
Además, algunos lenguajes de programación son compilados y otros son interpretados. Si un lenguaje es compilado, significa que se traduce a un lenguaje que la máquina entiende antes de ser ejecutado. Por otro lado, los lenguajes interpretados son traducidos en tiempo real, mientras se ejecutan.
Breve historia de la programación
La programación no es un concepto nuevo. De hecho, ha estado con nosotros desde mucho antes de que las computadoras existieran en la forma que las conocemos hoy. Dispositivos como el ábaco y el astrolabio son ejemplos tempranos de herramientas que usamos para realizar cálculos complejos.
Pero fue con la llegada de las máquinas mecánicas, como la Máquina Analítica de Charles Babbage, que se sentaron las bases para la programación moderna. ¡Estamos hablando del siglo XIX!
Con el paso del tiempo, nacieron lenguajes que marcaron hitos, como Fortran y COBOL. Estos lenguajes sentaron las bases para las revoluciones tecnológicas que vendrían. Con la evolución de los lenguajes, también surgieron nuevos paradigmas: primero el Procedural, luego el Orientado a Objetos y más recientemente, el Funcional.
Hoy, estamos en una era moderna, dominada por la programación para la web, móviles y la nube. Cada vez que deslizas tu dedo por la pantalla de tu celular o compras algo online, hay líneas y líneas de código trabajando detrás de escena.
La programación hoy
La programación es el motor de nuestra sociedad moderna. Desde aplicaciones para pedir comida hasta sistemas avanzados de inteligencia artificial que ayudan en investigaciones médicas, la programación está en todas partes.
Además de simplificar nuestras vidas cotidianas, la programación tiene un impacto profundo en la sociedad. Ha permitido avances en la automatización, el análisis de datos y el entretenimiento. Y lo que es aún más emocionante, es que apenas estamos rasguñando la superficie. Con los avances en inteligencia artificial, computación cuántica y el Internet de las Cosas (IoT), ¿quién sabe qué maravillas nos esperan en mundo de la programación?
1.1 - La Computadora
Si alguna vez te has preguntado qué es lo que hace “tictac” dentro de esa caja metálica que llamamos computadora, estás en el lugar indicado. En esencia, una computadora es una combinación de hardware y software que trabajan juntos para llevar a cabo tareas específicas.
El hardware es toda la parte física de la computadora: el CPU (que es como el cerebro de la máquina), la RAM (donde la computadora guarda la información con la que está trabajando en un momento determinado), dispositivos de almacenamiento (donde se guardan los datos de forma permanente) y periféricos (como el teclado, el mouse o la pantalla)1.
Por otro lado, el software es el conjunto de instrucciones que le dice al hardware qué hacer. Hay varios tipos de software, desde el software del sistema, como el sistema operativo que coordina todas las acciones de la máquina, hasta software de aplicación que nos permite hacer cosas como escribir documentos o jugar videojuegos2.
El papel del sistema operativo es crucial. Es el mediador entre el usuario y el hardware, asegurando que todo funcione armónicamente. Si el hardware fuera una orquesta, el sistema operativo sería el director que asegura que cada instrumento toque en el momento y de la manera correcta.
El sistema binario: el lenguaje secreto de las computadoras
A diferencia de nosotros, que usamos un sistema decimal basado en diez dígitos (del 0 al 9), las computadoras usan el sistema binario, que solo tiene dos dígitos: 0 y 1. ¿Por qué? Bueno, en el nivel más básico, una computadora está hecha de millones de transistores que pueden estar en uno de dos estados: encendido o apagado. Estos estados se representan con esos dígitos: 0 para apagado y 1 para encendido3.
Los términos “bit” y “byte” son fundamentales aquí. Un bit es la unidad más pequeña de datos en una computadora y puede tener un valor de 0 o 1. Un byte, por otro lado, es un conjunto de 8 bits y puede representar 256 valores diferentes (desde \(00000000\) hasta \(11111111\) en binario)4.
Este sistema binario no solo representa números, sino también texto, imágenes y cualquier tipo de dato. Por ejemplo, en el código ASCII (un estándar de codificación de caracteres), la letra “A” se representa como \(01000001\) en binario.
En un artículo posterior, vamos a hablar en detalle acerca del sistema binario y otro sistema muy utilizado en el ambiente de la computación, el hexadecimal.
Memoria y almacenamiento: donde residen nuestros datos
La memoria y el almacenamiento son dos conceptos cruciales en la informática. Si bien a menudo se usan indistintamente, tienen roles muy diferentes.
La memoria, específicamente la RAM, es volátil. Esto significa que la información se pierde cuando apagamos la computadora. La RAM es esencialmente el “espacio de trabajo” de la computadora, donde almacena datos e instrucciones mientras está en uso. Hay varios tipos de RAM, siendo DRAM y SRAM los más comunes5.
Por otro lado, tenemos la ROM (Memoria de Solo Lectura). A diferencia de la RAM, la ROM no es volátil y se utiliza para almacenar firmware, es decir, software que está íntimamente ligado al hardware y que no necesita cambios frecuentes.
En cuanto al almacenamiento, dispositivos como discos duros, SSDs y unidades flash nos permiten guardar información de manera permanente. Estos dispositivos forman parte de lo que se conoce como la jerarquía de memoria, que va desde la memoria caché (rápida pero pequeña) hasta el almacenamiento secundario (más lento pero con gran capacidad)6.
¡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 encontrá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
Patterson, D. & Hennessy, J. (2014). Arquitectura de Computadoras. Buenos Aires: Ediciones Omega. ↩︎
Silberschatz, A., Galvin, P. B., & Gagne, G. (2009). Fundamentos de Sistemas Operativos. Buenos Aires: Ediciones M. ↩︎
Tanenbaum, A. (2012). Estructura de Computadoras. Buenos Aires: Prentice Hall. ↩︎
Brookshear, J. G. (2011). Ciencia de la Computación: Una visión general. Buenos Aires: Pearson Educación. ↩︎
Jacob, B., Ng, S. W., & Wang, D. T. (2007). Arquitecturas de memoria en sistemas de computadoras. Buenos Aires: Wiley-Interscience. ↩︎
Siewiorek, D. P. & Swarz, R. S. (2017). Principios de diseño de sistemas computacionales. Buenos Aires: Morgan Kaufmann. ↩︎
1.2 - Sistemas de Numeración
El sistema decimal: la base de nuestra cotidianidad
Desde pequeños, nos enseñan a contar usando diez dígitos: del 0 al 9. Este sistema, conocido como decimal, es la base de casi todas nuestras actividades matemáticas y financieras, desde sumar cuentas hasta calcular intereses en el banco1. Tiene su origen en la cantidad de dedos que tenemos en las manos, lo que lo convierte en el sistema más intuitivo y natural para nosotros. Pero lo que lo hace especial es su naturaleza posicional.
Para comprender este concepto, consideremos el número 237:
- El 7, situado a la derecha, está en la posición de las unidades. Es decir, \(7 \times 10^0\) (cualquier número elevado a la potencia de 0 es 1). Por lo tanto, su valor es simplemente 7.
- El 3, en la posición del medio, representa las decenas, es decir, \(3 \times 10^1 = 3 \times 10 = 30\).
- El 2, el número más a la izquierda, está en la posición de las centenas, traduciéndose a \(2 \times 10^2 = 2 \times 100 = 200\).
Si sumamos estos valores,
El sistema binario: el lenguaje secreto de las computadoras
Si bien el sistema decimal domina nuestra vida cotidiana, las máquinas que usamos todos los días, desde nuestros celulares hasta las computadoras, operan en un mundo completamente diferente: el mundo binario. En este sistema, solo existen dos dígitos: 0 y 1. A primera vista, puede parecer limitante, pero este sistema es la esencia de la electrónica digital. Los dispositivos electrónicos, con sus millones de transistores, operan usando estos dos estados: encendido (1) y apagado (0)2.
A pesar de su aparente simplicidad, el sistema binario puede representar cualquier cantidad o información que el sistema decimal pueda expresar. Por ejemplo, el número decimal 5 se representa como 101 en binario.
El sistema binario, con sus unos y ceros, opera de manera similar al sistema decimal, pero en lugar de potencias de 10, usa potencias de 2.
Tomemos el número binario 1011:
- El bit más a la derecha representa \(1 \times 2^0 = 1\)
- El siguiente bit representa \(1 \times 2^1 = 2\)
- Luego viene \(0 \times 2^2 = 0\)
- El bit más a la izquierda en este número representa \(1 \times 2^3 = 8\)
Entonces, 1011 en binario se traduce a decimal de la siguiente manera:
El sistema hexadecimal: un puente entre humanos y máquinas
Mientras que el sistema binario es perfecto para las máquinas, puede ser un poco engorroso para nosotros, especialmente cuando tratamos con números binarios largos. Aquí es donde entra el sistema hexadecimal, que utiliza dieciséis dígitos distintos: 0-9 y A-F, donde A representa 10, B es 11, y así sucesivamente hasta F que es 153.
El hexadecimal es especialmente útil porque proporciona una forma más compacta de representar números binarios. Cada dígito hexadecimal corresponde a exactamente cuatro dígitos binarios (bits). Por ejemplo pensemos en la representación en binario del número 41279 y como el sistema hexadecimal consigue una representación más compacta:
Pero el sistema hexadecimal es más que una representación compacta de números binarios, es un sistema de numeración posicional como el decimal o binario con base 16 en lugar de 10 o 2. Veamos como conseguir la representación decimal del número del ejemplo anterior (A13F).
- El dígito más a la derecha representa \(F \times 16^0 = 15 \times 16^0 = 15\)
- El siguiente representa \(3 \times 16^1 = 48\)
- Luego viene \(1 \times 16^2 = 256\)
- El dígito más a la izquierda en este número representa \(A \times 16^3 = 10 \times 16^3 = 40960\)
Entonces, A13F en hexadecimal se traduce a decimal de la siguiente manera:
Conclusión
Los sistemas de numeración son como lentes a través de los cuales vemos y entendemos el mundo de las matemáticas y la computación. Aunque en nuestra vida diaria el sistema decimal sea el rey, es esencial apreciar y comprender los sistemas binario y hexadecimal, especialmente en esta era digital.
Así que, la próxima vez que estés frente a tu computadora o usando una app en tu celular, recordá que detrás de esa interfaz amigable, hay un mundo binario en pleno funcionamiento, y que el sistema hexadecimal actúa como un traductor entre ese mundo y nosotros.
¡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 encontrá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
1.3 - Lógica Booleana
La lógica booleana, nombrada en honor a George Boole, un matemático inglés del siglo XIX, es un sistema matemático que se ocupa de operaciones que tienen solo dos resultados posibles: verdadero o falso, representados generalmente como 1 y 0, respectivamente1. En su obra “An Investigation of the Laws of Thought”, Boole estableció las bases de esta lógica, presentando un sistema algebraico que podría utilizarse para representar estructuras lógicas.
Operaciones Booleanas
Dentro de la lógica booleana, existen operaciones fundamentales que permiten manipular y combinar estas expresiones binarias:
AND (Y): Esta operación devuelve verdadero (1) solo si ambas entradas son verdaderas. Por ejemplo, si tenemos dos interruptores, ambos deben estar en la posición encendido para que una luz se encienda.
OR (O): Devuelve verdadero si al menos una de las entradas es verdadera. Siguiendo con el ejemplo de los interruptores, con que uno de ellos esté encendido, la luz se iluminará.
NOT (NO): Es una operación unaria, lo que significa que solo tiene una entrada. Simplemente invierte el valor de entrada. Si le das un 1, devuelve un 0 y viceversa.
NAND (NO Y): Es la negación de AND. Solo devuelve falso si ambas entradas son verdaderas.
NOR (NO O): Es la negación de OR. Devuelve verdadero solo si ambas entradas son falsas.
XOR (O exclusivo): Devuelve verdadero si las entradas son diferentes. Si ambas son iguales, devuelve falso.
XNOR (NO O exclusivo): Es la negación de XOR. Devuelve verdadero si ambas entradas son iguales.
La importancia de esta lógica en computación y programación
La computación moderna, en su esencia, es la manipulación de bits, esos unos y ceros que mencionamos. Cada operación que realiza una computadora, desde simples cálculos hasta la renderización de gráficos complejos, implica operaciones booleanas en algún nivel2.
En programación, la lógica booleana se utiliza en estructuras de control, como condiciones (if, else) y bucles, permitiendo a los programas tomar decisiones basadas en ciertas condiciones.
Tablas de verdad: el mapa de la lógica Booleana
Una tabla de verdad es una representación gráfica de una operación booleana. Enumera todas las combinaciones posibles de entradas y muestra el resultado de la operación para cada combinación3.
Por ejemplo,
A | B | A AND B | A OR B | A XOR B | A NOR B | A NAND B | NOT A | A NXOR B |
---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
Conclusiones
La lógica booleana es mucho más que un conjunto de reglas matemáticas abstractas. Es el lenguaje fundamental de las máquinas, el código que subyace a la era digital en la que vivimos. Al comprender sus principios, no solo nos volvemos más adeptos a trabajar con tecnología, sino que también adquirimos una apreciación más profunda de las estructuras que sustentan nuestro mundo digital.
¡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 encontrá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
1.4 - Configura tu Entorno de Desarrollo
Elegir un lenguaje de programación
La elección del lenguaje de programación es el primer y quizás el más crucial paso en el proceso de aprendizaje. Hay varios factores a considerar al seleccionar un lenguaje, incluyendo:
- Propósito: ¿Para qué quieres programar? Si es para desarrollo web, JavaScript o PHP podrían ser buenas opciones. Si estás interesado en la ciencia de datos, R o Python podrían ser más adecuados.
- Comunidad: Un lenguaje con una comunidad activa puede ser esencial para los principiantes. Una comunidad vibrante generalmente significa más recursos, tutoriales y soluciones disponibles en línea.
- Curva de aprendizaje: Algunos lenguajes son más fáciles de aprender que otros. Es fundamental elegir uno que coincida con tu nivel de experiencia y paciencia.
- Oportunidades de trabajo: Si estás buscando una carrera en programación, investigar la demanda del mercado para diferentes lenguajes puede ser útil.
Aunque hay muchos lenguajes valiosos y poderosos, para este curso, hemos elegido Python. Este lenguaje es conocido por su simplicidad y legibilidad, lo que lo hace ideal para aquellos que están empezando. Además, Python cuenta con una comunidad activa y una amplia gama de aplicaciones, desde desarrollo web hasta inteligencia artificial1.
Instalación de Python
Para usuarios de Windows:
- Descargar el instalador:
- Visita el sitio web oficial de Python en https://www.python.org/downloads/windows/
- Haz clic en el enlace de descarga para la última versión de Python 3.x.
- Ejecuta el instalador:
- Una vez completada la descarga, localiza y ejecuta el archivo instalador
.exe
. - Asegúrate de marcar la casilla que dice “Agregar Python al PATH” durante la instalación. Este paso es crucial para hacer que Python sea accesible desde el Símbolo del Sistema.
- Sigue las indicaciones de instalación.
- Una vez completada la descarga, localiza y ejecuta el archivo instalador
- Verifica la instalación:
- Abre el Símbolo del Sistema y escribe:
python --version
- Esto debería mostrar la versión de Python que acabas de instalar.
- Abre el Símbolo del Sistema y escribe:
Para usuarios de Mac:
- Descargar el instalador:
- Visita el sitio web oficial de Python en https://www.python.org/downloads/mac-osx/
- Haz clic en el enlace de descarga para la última versión de Python 3.x.
- Ejecuta el instalador:
- Una vez descargado, localiza y ejecuta el archivo
.pkg
. - Sigue las indicaciones de instalación.
- Una vez descargado, localiza y ejecuta el archivo
- Verifica la instalación:
- Abre la Terminal y escribe:
python3 --version
- Esto debería mostrar la versión de Python que acabas de instalar.
- Abre la Terminal y escribe:
Para usuarios de Linux (Ubuntu/Debian):
- Actualiza los paquetes:
sudo apt update
- Instala Python:
sudo apt install python3
- Verifica la instalación:
- Después de la instalación, puedes comprobar la versión de Python instalada escribiendo:
python3 --version
- Después de la instalación, puedes comprobar la versión de Python instalada escribiendo:
Entornos de Desarrollo Integrado (IDEs)
Un IDE es una herramienta que facilita el desarrollo de aplicaciones al combinar comúnmente utilizadas en un solo software: editor de código, compilador, depurador, entre otros. Elegir el IDE adecuado puede hacer que el proceso de programación sea más fluido y eficiente.
Al evaluar IDEs, considera:
- Compatibilidad con el lenguaje: No todos los IDEs son compatibles con todos los lenguajes de programación.
- Características: Algunos IDEs ofrecen funcionalidades como autocompletado, resaltado de sintaxis y herramientas de depuración.
- Extensiones y plugins: La posibilidad de personalizar y extender tu IDE a través de plugins puede ser muy útil.
- Precio: Hay IDEs gratuitos y otros de pago. Evalúa si las características adicionales de un IDE de pago justifican el costo.
Para este curso, hemos seleccionado Visual Studio Code (VS Code). Es un IDE popular que es gratuito y de código abierto. Es conocido por su interfaz sencilla, amplia gama de plugins y capacidad para manejar múltiples lenguajes de programación2. Su comunidad activa garantiza actualizaciones regulares y una amplia gama de recursos de aprendizaje.
Instalación de Visual Studio Code
Para usuarios de Windows:
- Descargar el instalador:
- Visita el sitio web oficial de VS Code en https://code.visualstudio.com/
- Haz clic en el botón “Descargar para Windows”.
- Ejecuta el instalador:
- Una vez completada la descarga, localiza y ejecuta el archivo instalador
.exe
. - Sigue las indicaciones de instalación, incluyendo aceptar el acuerdo de licencia y elegir la ubicación de instalación.
- Una vez completada la descarga, localiza y ejecuta el archivo instalador
- Inicia VS Code:
- Tras la instalación, puedes encontrar VS Code en tu menú de inicio.
- Lánzalo, ¡y estarás listo para comenzar a programar!
Para usuarios de Mac:
- Descargar el instalador:
- Visita el sitio web oficial de VS Code en https://code.visualstudio.com/
- Haz clic en el botón “Descargar para Mac”.
- Instala VS Code:
- Una vez descargado, abre el archivo
.zip
. - Arrastra la aplicación Visual Studio Code
.app
a la carpetaAplicaciones
, para que esté disponible en el Launchpad.
- Una vez descargado, abre el archivo
- Inicia VS Code:
- Usa la búsqueda de Spotlight o navega hasta tu carpeta de Aplicaciones para iniciar VS Code.
Para usuarios de Linux (Ubuntu/Debian):
- Actualiza los paquetes e instala las dependencias:
sudo apt update sudo apt install software-properties-common apt-transport-https wget
- **Descarga e instala la claves necesarias:
wget -q https://packages.microsoft.com/keys/microsoft.asc -O- | sudo apt-key add -
- Añade el repositorio de VS Code:
sudo add-apt-repository "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main"
- Instala Visual Studio Code:
sudo apt update sudo apt install code
- Inicia VS Code:
- Puedes iniciar VS Code desde la terminal escribiendo
code
o encontrarlo en tu lista de aplicaciones instaladas.
- Puedes iniciar VS Code desde la terminal escribiendo
Escribe y ejecuta tu primer programa
Una vez que hayas configurado tu entorno de programación, es hora de sumergirse en la codificación.
¡Hola mundo!
Este es posiblemente el programa más icónico para principiantes. Es simple, pero te introduce al proceso de escribir y ejecutar código.
print("¡Hola mundo!")
Ejecución del programa Hola Mundo
Cálculo de área y perímetro de un triángulo
Este programa es un poco más complejo. No solo imprime un mensaje, sino que también realiza cálculos matemáticos.
# Entrada del usuario
lado1 = float(input("Introduce la longitud del primer lado: "))
lado2 = float(input("Introduce la longitud del segundo lado: "))
lado3 = float(input("Introduce la longitud del tercer lado: "))
# Cálculo del perímetro
perimetro = lado1 + lado2 + lado3
# Cálculo del área usando la fórmula de Herón
s = perimetro / 2
area = (s*(s-lado1)*(s-lado2)*(s-lado3)) ** 0.5
print(f"El perímetro del triángulo es: {perimetro}")
print(f"El área del triángulo es: {area:.2f}")
Ejecución del programa Triangulo
Conclusión
Configurar un entorno de programación puede parecer desalentador al principio, pero con las herramientas y recursos adecuados, se convierte en una tarea manejable y gratificante. Esperamos que este artículo te haya proporcionado una base sólida para comenzar tu viaje en programación. ¡Feliz codificación!
¡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 encontrá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
2 - Conceptos Iniciales
2.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 encontrá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.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 encontrá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. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
2.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 encontrá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. ↩︎ ↩︎
2.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 encontrá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. ↩︎
2.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 encontrá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
3 - Programación Orientada a Objetos
La Programación Orientada a Objetos (POO) es un paradigma de programación que se ha vuelto indispensable en la actualidad. Este enfoque modela elementos del mundo real como “objetos” que tienen propiedades y comportamientos, lo cual permite crear programas más intuitivos y fáciles de mantener. En este artículo veremos los conceptos básicos de POO y sus ventajas frente a otros paradigmas como la programación procedural. ¡Empecemos!
Este paradigma se basa en dos conceptos fundamentales:
- Objetos: entidades que combinan estado (datos) y comportamiento (operaciones) en una misma unidad. Por ejemplo, un objeto “coche” tendría propiedades como color, número de puertas, velocidad máxima, etc. Y comportamientos como acelerar, frenar, girar, etc.
- Clases: especificaciones que definen la estructura y comportamiento común de un grupo de objetos. La clase “coche” serviría como molde para crear objetos coche con las mismas características.
Como explica el programador Alan Kay, uno de los creadores de la POO:
“La idea central de POO es que los usuarios deben manipular objetos conceptuales más que máquinas de Turing. Las interfaces con el mundo real deben, por lo tanto, ser construidas en términos de objetos conceptuales.”1
Es decir, la POO modela conceptualmente elementos del mundo real para hacer la programación más intuitiva.
Paradigmas de programación
Antes de profundizar en la POO, conviene entender que existen diferentes paradigmas o enfoques para abordar la programación. Los principales son:
Programación procedural
Secuencia ordenada de instrucciones que el programa debe seguir paso a paso. El foco está en procedimientos y funciones. Por ejemplo, C es un lenguaje orientado a la programación procedural.
La programación procedural es mejor para:
- Problemas sencillos o algoritmos secuenciales.
- Código que no necesitará reusarse ni expandirse mucho.
- Casos donde el rendimiento y eficiencia son críticos.
Programación orientada a objetos
Modelo basado en objetos que contienen datos y código en unidades cohesivas. El foco está en las clases y en la interacción entre objetos. Por ejemplo, Java y Python son lenguajes orientados a objetos.
La POO permite modelar de forma más directa elementos del mundo real, encapsular mejor los datos y reutilizar código a través de la herencia entre clases.
Las principales ventajas de POO frente a la programación procedural son:
- Modularidad: los objetos agrupan datos y operaciones relacionadas, encapsulando la complejidad interna. Esto permite trabajar con módulos independientes.
- Ocultación de información: Los objetos pueden exponer una interfaz simple y ocultar detalles de implementación internos. Esto reduce acoplamientos.
- Reusabilidad: Las clases permiten reuse de código. Una clase abstracta puede heredar a múltiples subclases.
- Extensibilidad: Podemos extender el comportamiento de clases padres creando nuevas subclases.
- Mapeo conceptual: Los objetos representan entidades del mundo real, lo cual facilita la traducción de requerimientos a código.
Sin embargo, la POO también tiene desventajas. Según el programador Paul Graham:
“La programación orientada a objetos suele ser una molestia. Hace que las cosas sean más difíciles de lo que deberían ser.”2
Por ejemplo, para problemas simples la POO puede resultar excesiva. Y en proyectos grandes existe el riesgo de abusar de la herencia y el polimorfismo, volviendo el código difícil de seguir.
En definitiva, la POO es más adecuada cuando:
- El problema a modelar tiene entidades claras y estructuradas.
- Queremos reutilizar código encapsulado en clases modulares.
- Trabajamos en sistemas que deben extenderse y mantenerse con facilidad.
Conceptos básicos de POO
Ahora que conocemos las ideas generales detrás de la POO, veamos algunos de los conceptos clave:
Objetos
Un objeto es una combinación de datos (propiedades) y comportamientos (métodos). Por ejemplo, un objeto Coche tendría propiedades como marca, modelo, color y métodos como acelerar, frenar, etc.
# Clase Coche
class Coche:
def __init__(self, marca, color):
self.marca = marca
self.color = color
def acelerar(self):
print("Acelerando", self.marca)
# Crear Objeto
mi_coche = Coche("Toyota", "Rojo")
mi_coche
es ahora un objeto Coche con propiedades y métodos definidos en su clase.
Clases
Una clase define los atributos (propiedades) y métodos (funciones) comunes a un grupo de objetos. Funciona como un molde para crear objetos similares.
Por convención las clases se definen con la primera letra en mayúscula. Las propiedades y métodos de una clase reciben el prefijo self
para indicar que pertenecen a esa instancia de objeto.
class Circulo:
def __init__(self, radio):
self.radio = radio
def area(self):
return 3.1416 * (self.radio ** 2)
Circulo
define la clase con propiedadradio
y métodoarea()
.
Métodos
Los métodos son funciones que definen el comportamiento de un objeto. Se declaran dentro de la clase y pueden acceder a las propiedades del objeto mediante self
.
Un constructor es un método especial (__init__
) que se ejecuta al crear objetos para inicializar sus propiedades.
class Persona:
def __init__(self, nombre, edad):
self.nombre = nombre
self.edad = edad
def saludar(self):
print(f"Hola! Soy {self.nombre}")
juan = Persona("Juan", 30) # Ejecuta __init__
juan.saludar() # "Hola! Soy Juan"
- El constructor asigna
nombre
yedad
. El métodosaludar()
accede al atributonombre
.
Propiedades
Las propiedades son variables asociadas a un objeto que definen sus características o estado. Se declaran en la clase y se accede a ellas mediante la referencia del objeto.
class Rectangulo:
def __init__(self, alto, ancho):
self.alto = alto
self.ancho = ancho
r = Rectangulo(3,4)
print(r.alto) # 3
r.ancho = 8
print(r.ancho) # 8
Se recomienda declarar las propiedades como privadas y acceder mediante métodos getters/setters para respetar el encapsulamiento.
Encapsulamiento
Consiste en ocultar los detalles de implementación internos de un objeto exponiendo solo una interfaz pública. Esto se logra declarando métodos y propiedades con los modificadores public
o private
.
En Python se denota con guion bajo prefijo para métodos/propiedades privadas:
class CuentaBancaria:
def __init__(self, saldo=0):
self.saldo = saldo
def depositar(self, monto):
self._saldo += monto
def consultar_saldo(self):
return self._saldo
_saldo
es privado, solo se accede internamente o porconsultar_saldo()
.
El encapsulamiento facilita cambiar partes internas de una clase sin afectar su interfaz pública.
Conclusión
La Programación Orientada a Objetos modela elementos del mundo real como clases y objetos, priorizando la modularidad, ocultación de información y reuso de código para crear programas más robustos y fáciles de mantener.
Aunque puede resultar excesiva para problemas simples, la POO es ideal para sistemas de mediana/gran escala que necesitan expandirse y evolucionar en complejidad con el tiempo.
Conceptos como herencia, polimorfismo, abstracción e interfaces permiten aprovechar al máximo las ventajas de este paradigma. Con una comprensión sólida de sus fundamentos estamos listos para aplicar la POO en cualquier lenguaje y proyecto de programación.
Referencias
Kay, Alan. The early history of Smalltalk. http://gagne.homedns.org/~tgagne/contrib/EarlyHistoryST.html ↩︎
Graham, Paul. Why Arc Isn’t Especially Object-Oriented. http://www.paulgraham.com/noop.html ↩︎
3.1 - Clases y Objetos
Anatomía de una clase
Una clase actúa como un plano o molde para construir objetos similares, definiendo sus características comunes y funcionalidades. Es similar al plano para construir casas de un mismo barrio: todas comparten ciertos atributos clave.
Los componentes típicos de una clase son:
Atributos (propiedades): Variables que caracterizan al objeto. Por ejemplo, para una clase Persona
, atributos como nombre
, edad
, DNI
, etc.
class Persona:
dni = ""
nombre = ""
edad = 0
Métodos: Funciones que definen comportamientos. Por ejemplo, una Persona
puede caminar()
, hablar()
, comer()
, etc. Acceden a los atributos para implementar dicha funcionalidad.
Constructor: Método especial __init__()
que se ejecuta al instanciar la clase y permite inicializar los atributos.
Destructor: Método __del__()
que se ejecuta al eliminar la instancia liberando recursos. Opcional en algunos lenguajes.
Creando objetos
A partir de la clase generamos objetos, que son instancias concretas con sus propios atributos definidos. Digamos que la clase Casa es el plano, y una casa específica en una calle determinada es el objeto.
En código creamos un objeto invocando la clase como si fuera un método:
# Clase Persona
class Persona:
def __init__(self, n, e):
self.nombre = n
self.edad = e
# Objeto Persona específico
pepe = Persona("Pepe", 30)
juan = Persona("Juan", 35)
Cada objeto comparte la estructura y comportamiento general, pero puede almacenar distintos datos.
Utilizando Propiedades y Métodos
Ya tenemos una clase Persona
y un objeto pepe
de tipo Persona
. ¿Cómo interactuamos con el objeto?
- Propiedades: Es posible acceder al valor de un atributo del objeto utilizando la referencia al objeto (
pepe
) y el nombre del atributo.
pepe.nombre # "Pepe"
pepe.edad # 30
- Métodos: De la misma manera en la que se accede a los atributos pero agregando un paréntesis dentro del cual se pasan los argumentos si es que recibe alguno.
# Clase Persona
class Persona:
def __init__(self, n, e):
self.nombre = n
self.edad = e
def comer(self, comida):
print(f"Comiendo {comida}")
# Objeto Persona específico
pepe = Persona("Pepe", 30)
pepe.comer("pizza") # Imprime "Comiendo pizza"
El objeto pepe tiene ahora estado (propiedades) y comportamiento (métodos) propios.
Self vs This
Un detalle importante en los métodos es cómo acceden a los atributos y otros métodos del objeto. Aquí entra otra diferencia entre lenguajes:
- Self: En Python, los atributos y métodos se acceden dentro de la clase anteponiendo
self
. Esto apunta al objeto instanciado.
class Persona:
def __init__(self, nombre):
self.nombre = nombre
def saludar(self):
print(f"Hola! Soy {self.nombre}")
juan = Persona("Juan")
juan.saludar()
# Imprime "Hola! Soy Juan"
- This: En Java o C#, se utiliza
this
en lugar de self. Cumple la misma funcionalidad de apuntar a los miembros del objeto.
public class Person {
private String nombre;
public Person(String nombre) {
this.nombre= nombre;
}
public void saludar() {
System.out.println("Hola! Soy " + this.nombre);
}
}
Person juan = new Person("Juan");
juan.saludar();
// Imprime "Hola! Soy Juan"
Conclusión
Las clases y objetos son los conceptos clave de la POO, permitiendo modelar entidades de la realidad y generar componentes modulares y genéricos de nuestro sistema para construir programas más robustos y fáciles de entender y mantener.
3.2 - Encapsulamiento
Uno de los pilares fundamentales de la programación orientada a objetos es el encapsulamiento. Esta potente característica nos permite controlar el acceso a los miembros de una clase, ocultando los detalles de implementación y protegiendo el estado de nuestros objetos. En este artículo veremos en profundidad el concepto de encapsulamiento, la utilidad del uso de getters, setters, propiedades y métodos públicos/privados, y los importantes beneficios que esto nos brinda como desarrolladores.
El diccionario de la Real Academia Española define encapsulamiento como el “envuelto o contenido como dentro de una cápsula”. Esto es precisamente lo que buscamos, “empaquetar” datos y código dentro de una única cápsula (la clase) para ocultar su complejidad interna.
La definición formal sería:
“El encapsulamiento es el empaquetamiento de los datos y funciones que manipulan estos datos en una única entidad o módulo de programa.”1
Es decir, mantener juntos datos y comportamientos relacionados para restringir el acceso directo a dichos datos desde otras partes del programa, interactuando solo a través de una interfaz controlada (API pública).
Esto provee ventajas como las siguientes:
- Control sobre modificación de datos.
- Flexibilidad al poder cambiar partes internas sin afectar otras partes.
- Protección del estado consistente de los objetos.
- Ocultación de complejidad al usuario.
Veamos con ejemplos concretos cómo encapsular en POO.
Getters y Setters
Digamos que tenemos una clase CuentaBancaria
, con propiedades como nombre
, número de cuenta
y saldo
:
class CuentaBancaria:
nombre = ""
nro_cuenta = 0
saldo = 0.0
Podemos acceder directamente a los atributos así:
cuenta1 = CuentaBancaria()
cuenta1.nombre = "Juan"
cuenta1.nro_cuenta = 1234
cuenta1.saldo = 2500
El problema es que cualquier otro código puede modificar el saldo a valores inválidos:
cuenta1.saldo = -9900 # ¡Saldo no puede ser negativo es este banco!
Esto permite estado inconsistente. Para encapsular usamos getters y setters:
class CuentaBancaria:
def __init__(self):
self.__saldo = 0
def get_saldo(self):
return self.__saldo
def set_saldo(self, valor):
if valor < 0:
raise Exception("Saldo no puede ser negativo en este banco")
self.__saldo = valor
__saldo
es ahora privado. Solo se manipula mediante los getters y setters públicos.El setter controla que no se ingresen valores no válidos.
En Python, anteponer doble guion bajo __
denota un método o atributo privado de la clase. Con guion simple _
es por convención un elemento protegido, o sea, accesible desde la clase y subclases pero no desde fuera. Y sin guiones
, los métodos y atributos son públicos.
En Java es explícito utilizando las palabras claves public
, protected
y private
:
public class Persona {
private String nombre; // Privado
public String getNombre() { // Público
return this.nombre;
}
}
Esta notación nos ayuda a declarar adecuadamente la visibilidad deseada para aplicar encapsulamiento.
Beneficios del encapsulamiento
Esta poderosa técnica nos proporciona grandes ventajas:
- Ocultación de información: Los detalles de implementación son invisibles para otros objetos, reduciendo acoplamientos. Podemos cambiar código interno minimizando impacto.
- Control sobre datos: Se garantiza la integridad y validez de estado mediante los setters/validadores.
- Código flexible: El aislamiento entre interfaces y detalles específicos permite construir sistemas más extensibles y fáciles de mantener en el tiempo.
“Todo módulo oculta la complejidad de su contenido detrás de una fachada (interfaz) simple”, Gang of Four2.
En definitiva, cuando necesitamos controlar cómo se manipula el estado interno de una clase desde otras partes de la aplicación, el encapsulamiento es la mejor solución.
Conclusión
Aplicar encapsulamiento restringiendo el acceso directo a los datos y codificando cuidadosamente una interfaz de acceso pública, nos permite construir sistemas POO más robustos, seguros y sustentables en el tiempo.
Dominar estas técnicas requiere experiencia y buen criterio para encontrar el balance adecuado entre ocultación de información y provisión de flexibilidad. Pero sin dudas vale la pena el esfuerzo para exprimir los beneficios que hemos visto de este maravilloso principio de la POO.
Referencias
Byron, Jeff. Encapsulation in Java. https://stackify.com/encapsulation-in-java/ ↩︎
Gamma, Erich et al. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. 1994. ↩︎