0% encontró este documento útil (0 votos)
13 vistas66 páginas

APUNTE FINAL Curso Python 2025

El documento presenta un curso de Introducción a la Programación con Python, destacando la importancia del pensamiento computacional y la lógica detrás de la programación en un mundo digital. Se exploran conceptos fundamentales como la diferencia entre lenguajes de programación, la función de compiladores e intérpretes, y las ventajas de Python como primer lenguaje. Además, se enfatiza el proceso de desarrollo de software y la necesidad de un enfoque estructurado para resolver problemas complejos.

Cargado por

Mauricio mau
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
13 vistas66 páginas

APUNTE FINAL Curso Python 2025

El documento presenta un curso de Introducción a la Programación con Python, destacando la importancia del pensamiento computacional y la lógica detrás de la programación en un mundo digital. Se exploran conceptos fundamentales como la diferencia entre lenguajes de programación, la función de compiladores e intérpretes, y las ventajas de Python como primer lenguaje. Además, se enfatiza el proceso de desarrollo de software y la necesidad de un enfoque estructurado para resolver problemas complejos.

Cargado por

Mauricio mau
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 66

Introducción a la Programación con Python (ISPC 2025)

Introducción
¡Bienvenido/a a este viaje transformador por el "Curso de Introducción a la Programación con
Python 2025"! En un mundo cada vez más mediado por algoritmos y sistemas inteligentes,
comprender la lógica que subyace a la tecnología no es solo una habilidad técnica, sino una
profunda invitación a la reflexión sobre nuestra propia capacidad de crear, innovar y dar forma al
futuro. Este apunte ha sido concebido no solo como una guía para aprender Python, sino como una
brújula para despertar tu pensamiento computacional y algorítmico, pilares esenciales para
enfrentar los desafíos de la era digital con una mirada crítica y proactiva.
Desde las primeras líneas, nos adentraremos en el fascinante diálogo entre la simplicidad binaria de
las máquinas y la complejidad aparentemente "inteligente" de los sistemas de Inteligencia Artificial
que nos rodean. Nos preguntaremos cómo es posible que, a partir de meros "unos y ceros", surjan
aplicaciones capaces de asistir diagnósticos médicos o guiar vehículos autónomos. En este camino,
desmitificaremos el proceso de la programación, pasando de la idea humana al código binario que
las computadoras comprenden, y exploraremos por qué Python, con su sintaxis clara y su
naturaleza interpretada, se erige como el lenguaje ideal para iniciar esta travesía.
Más allá de la mera sintaxis, este curso te invita a un ejercicio de pensamiento y resolución de
problemas. La programación es, en esencia, un arte de diseñar "recetas perfectas" —algoritmos—
que, con precisión, finitud y efectividad, abordan desafíos de diversa índole, desde el cálculo de
promedios hasta el preprocesamiento de datos masivos en Ciencia de Datos. Recorreremos las
etapas del ciclo de vida del software, desde el análisis profundo del problema con el enfoque
Entrada-Proceso-Salida (EPS) y la descomposición Top-Down, hasta el diseño meticuloso con
pseudocódigo y diagramas de flujo.
Cada sección te brindará herramientas y prácticas para cultivar un estilo de programación
estructurado y legible, sentando las bases de una vocación por la justicia y la dignidad humana en
cada línea de código.
Que este apunte sea una fuente de inspiración y un catalizador para tu desarrollo integral,
recordándote que cada programa que creas es una oportunidad para impactar el mundo con
ingenio y ética. ¿Estamos listos para transformar ideas en realidad y, al hacerlo, transformarnos a
nosotros mismos?
Unidad 1: Introducción a la Programación y al Pensamiento Computacional

¡Te doy la bienvenida a la primera unidad de tu viaje en el mundo de la programación! Esta unidad
es la piedra angular de todo lo que construiremos. Aquí no solo definiremos qué significa
"programar", sino que también desmitificaremos cómo las computadoras, máquinas que en su nivel
más básico solo entienden "encendido" y "apagado", pueden realizar tareas tan asombrosas como
recomendarte una película o ayudar en un diagnóstico médico. ¡Prepárate para sentar las bases de
tu pensamiento como programador/a!

1.1. Conceptos Fundamentales de la Programación


La Gran Pregunta: ¿Cómo Piensan las Máquinas? 🤔
En nuestro día a día, interactuamos constantemente con sistemas de Inteligencia Artificial (IA) y
Ciencia de Datos. Desde los algoritmos que nos sugieren música en Spotify, los asistentes virtuales
como Alexa o Siri que responden a nuestras preguntas, hasta los complejos sistemas que guían
vehículos autónomos o ayudan a los médicos a detectar enfermedades. Todos estos sistemas, por
muy avanzados que parezcan, se ejecutan en computadoras que, en su nivel más fundamental, solo
entienden secuencias de ceros y unos (el sistema binario).
Esta realidad nos lleva a una pregunta crucial: ¿Cómo es posible que máquinas que operan con una
lógica tan simple puedan ejecutar tareas tan complejas y aparentemente "inteligentes"? ¿Qué
elementos y procesos intermedios permiten a los seres humanos comunicar instrucciones
sofisticadas a estas máquinas para que funcionen? Y, lo más importante para nosotros, ¿por qué
Python es el lenguaje ideal para comenzar este fascinante viaje?

Desvelando el Misterio: De la Idea al Código Binario


Para que un computador pueda ejecutar una tarea compleja, se requiere una serie de elementos y
procesos que actúan como puentes entre el pensamiento humano y la lógica binaria de la máquina.
● Computador y Codificación de la Información: Un computador es una máquina electrónica
capaz de procesar información siguiendo un conjunto de instrucciones. Toda la información y
las instrucciones deben representarse en un formato que la computadora pueda entender.
Este proceso se conoce como codificación de la información, y típicamente utiliza el sistema
binario (unos y ceros). Por ejemplo, el texto que lees ahora mismo se codifica usando
estándares como la tabla ASCII, que asigna a cada letra, número y símbolo una secuencia única
de bits .
● Programas y Lenguajes de Programación: Una secuencia de instrucciones que una
computadora puede ejecutar para cumplir una tarea se denomina programa. Para escribir
estos programas, utilizamos lenguajes de programación, que son sistemas de notación formal
diseñados para comunicar instrucciones a una computadora .
● Niveles de Lenguajes: Existen diferentes niveles de lenguajes:
○ Lenguajes de bajo nivel: Son aquellos que el procesador puede ejecutar directamente.
Incluyen el código de máquina (instrucciones en binario puro) y el lenguaje ensamblador
(que usa mnemónicos como ADD o MOV para representar instrucciones binarias,
haciéndolo un poco más legible). Son extremadamente rápidos pero muy difíciles de
escribir y entender para los humanos.
○ Lenguajes de alto nivel: Son mucho más cercanos al lenguaje humano, lo que los hace más
fáciles de leer, escribir y mantener. Python, Java, C++ y JavaScript son ejemplos de
lenguajes de alto nivel .
La Gran Traducción: Compiladores vs. Intérpretes
Dado que nosotros programamos en lenguajes de alto nivel y las computadoras solo entienden
lenguajes de bajo nivel, necesitamos un traductor. Aquí es donde entran en juego dos tipos de
programas: los compiladores y los intérpretes. Para entender la diferencia, usemos una analogía:
traducir un libro .
● El Compilador (El Traductor de Libros 📚): Un compilador es como un traductor que toma un
libro completo escrito en español, lo traduce íntegramente al inglés de una sola vez y te
entrega un nuevo libro completamente en inglés. Este nuevo libro (el archivo ejecutable)
puede ser leído directamente por cualquier persona que hable inglés (el procesador) sin
necesidad de que el traductor original esté presente. El trabajo de traducción se realiza todo
antes de que alguien empiece a leer. Lenguajes como C, C++ y Rust son típicamente
compilados.
● El Intérprete (El Traductor en Vivo 🗣️): Un intérprete es como tener un traductor humano a
tu lado. Le entregas el libro en español, y él lee la primera oración, la traduce en voz alta al
inglés para que la entiendas, luego lee la segunda, la traduce, y así sucesivamente. La
traducción ocurre durante la lectura, línea por línea. Si hay un error en una oración, te enteras
en el momento exacto en que el intérprete llega a ella. Python es un lenguaje interpretado, lo
que lo hace ideal para aprender, ya que puedes probar código y ver los resultados de
inmediato.

Para los Curiosos: ¿Y si el traductor tuviera memoria? 🤔 La Realidad Híbrida


La distinción entre compilado e interpretado es una excelente forma de empezar, pero la realidad
en la programación moderna es más matizada. Muchos lenguajes, incluido Python en algunas de
sus implementaciones, utilizan enfoques híbridos para obtener lo mejor de ambos mundos: la
velocidad de la compilación y la flexibilidad de la interpretación.
Imagina que nuestro intérprete en vivo es muy inteligente. Si nota que una oración se repite
muchas veces a lo largo del libro, en lugar de traducirla cada vez, la traduce una sola vez a un
"código intermedio" (llamado bytecode) y la "memoriza". La próxima vez que vea esa oración,
simplemente usa la versión memorizada, que es mucho más rápida. Este proceso se conoce como
compilación Just-In-Time (JIT). Esto nos enseña que, si bien clasificamos a Python como un lenguaje
interpretado por su funcionamiento principal, el ecosistema de la programación es increíblemente
innovador y siempre busca optimizar el rendimiento.
A continuación, una tabla que resume las diferencias clave:
Característica Compilador (Traductor de Libros) Intérprete (Traductor en Vivo)

¿Cuándo traduce? Antes de la ejecución, todo de Durante la ejecución, línea por


una vez. línea.

Resultado Un archivo ejecutable La ejecución directa de las


independiente (ej: .exe). acciones.

Velocidad de Ejecución Generalmente más rápida. Generalmente más lenta (pero


optimizada con JIT).

Flexibilidad (Multiplataforma) El ejecutable depende de la El código fuente funciona en


plataforma (Windows, macOS, cualquier plataforma que tenga el
etc.). intérprete.

Depuración de Errores Más complejo; se detectan Más sencillo; el error se reporta


errores en la fase de compilación. en la línea exacta donde ocurre.

Ejemplos C, C++, Rust Python, JavaScript, Ruby, PHP

¿Por Qué Python es tu Mejor Primer Lenguaje?


Python es un lenguaje moderno, simple y extremadamente potente, ampliamente utilizado en el
ámbito académico, científico y profesional. Sus ventajas como primer lenguaje son numerosas:
● Sintaxis Limpia y Legible: Su estructura es tan sencilla y cercana al lenguaje natural que facilita
enormemente el aprendizaje. Esto te permite concentrarte en la lógica del problema en lugar
de luchar con una sintaxis complicada.
● Tipado Dinámico y Fuerte: No es necesario declarar explícitamente los tipos de las variables (si
es un número, un texto, etc.), ya que Python lo infiere en tiempo de ejecución (tipado
dinámico). Sin embargo, es fuertemente tipado, lo que significa que no permite operaciones
ilógicas entre tipos distintos (como sumar un número a un texto) sin una conversión explícita,
lo que ayuda a prevenir errores .
● Interactivo y Rápido para Prototipar: Ofrece un entorno interactivo (la consola o REPL) donde
puedes ejecutar código línea por línea y ver los resultados al instante. Esto es ideal para
experimentar y aprender .
● Multiplataforma y Gratuito: Funciona en prácticamente cualquier sistema operativo (Linux,
Windows, macOS) y su uso es completamente gratuito .
● Vasta Biblioteca Estándar y una Comunidad Gigante: Python viene con una enorme colección
de "módulos" que extienden sus capacidades a casi cualquier área imaginable, desde
desarrollo web hasta IA. Su comunidad global ofrece una cantidad inagotable de soporte,
tutoriales y recursos.

Refuerzo y Práctica 🧠
● Criterio Clave: Recuerda que programar es, ante todo, un ejercicio de pensamiento y
resolución de problemas. La simplicidad de Python te permitirá enfocarte en desarrollar tu
pensamiento algorítmico sin la frustración de una sintaxis compleja.
● A Practicar: Abre la consola interactiva de Python. Ejecuta comandos simples, como sumas (2 +
2), y observa cómo Python responde. Esta experimentación directa es clave para desarrollar
una intuición sobre el lenguaje.
● A Memorizar: Comprende la diferencia fundamental entre lenguajes de alto y bajo nivel, y
entre compiladores e intérpretes. Entender que Python es interpretado es crucial para
comprender cómo se ejecuta tu código.

Aplicaciones en Ciencia de Datos e IA 🤖


● Preprocesamiento de Datos: En Ciencia de Datos, los datos rara vez vienen listos para usar. La
simplicidad de Python y su naturaleza interpretada permiten a los científicos escribir
rápidamente pequeños "scripts" para limpiar, transformar y formatear datos, haciendo que los
algoritmos de preparación sean fáciles de entender y depurar.
● Prototipado Rápido en IA: Antes de construir un modelo de IA complejo, es vital probar ideas
rápidamente. Al ser interpretado, Python facilita este proceso de prueba y error, permitiendo
a los investigadores iterar sobre sus diseños de forma ágil.

Autoevaluación ✅
1. Pregunta: Un compilador traduce y ejecuta el código fuente línea por línea.
○ Respuesta: Falso. Un compilador traduce todo el código a código máquina de una sola vez,
antes de la ejecución, generando un archivo ejecutable. El que traduce y ejecuta línea por
línea es el intérprete.
2. Pregunta: Python es considerado un lenguaje de bajo nivel porque su sintaxis es muy cercana
al lenguaje de máquina.
○ Respuesta: Falso. Python es un lenguaje de alto nivel. Su sintaxis es cercana al lenguaje
humano, lo que lo hace más fácil de leer y escribir. Los lenguajes de bajo nivel son los que
están más cerca del código de máquina.
3. Pregunta: Una de las ventajas de Python para los principiantes es que su sintaxis legible
permite enfocarse más en los conceptos de programación que en los detalles complejos del
lenguaje.
○ Respuesta: Verdadero. La sintaxis limpia y legible de Python es una de sus principales
fortalezas para el aprendizaje, ya que reduce la "carga cognitiva" y permite a los
estudiantes concentrarse en los fundamentos lógicos de la programación.

1.2. El Proceso de Desarrollo de Software


Más Allá del Código: El Mapa del Tesoro del Desarrollador 🗺️
A menudo, los desarrolladores novatos sienten la tentación de empezar a escribir código tan pronto
como entienden la superficie de un problema. Creen que el software es solo el código. Sin
embargo, los proyectos de software complejos, como un modelo para predecir la rotación de
clientes en una empresa o un sistema de recomendación para millones de usuarios, no pueden
simplemente "codificarse" de golpe. Intentarlo sin una planificación adecuada conduce a errores,
ineficiencias y sistemas que son imposibles de mantener o escalar.
Esto nos lleva a cuestionar: ¿Qué hay "más allá del código" en el desarrollo de software? ¿Por qué
es crucial seguir un proceso estructurado desde la concepción de una idea hasta la entrega de un
programa funcional?

Las Etapas del Viaje: El Ciclo de Vida del Software


El desarrollo de software es un proceso metódico. Para construir programas claros, funcionales y
eficientes, se sigue un ciclo de vida que se divide en etapas clave. Programar no es solo escribir
código; es, ante todo, pensar en soluciones .

Ejemplo Conductor: "Promedio y Aprobación de un Alumno"


Para ilustrar cada etapa, usaremos un problema concreto: Desarrollar un programa que calcule el
promedio de tres notas de un examen y determine si el alumno aprobó (promedio >= 4).
1. Análisis: Entender el Problema 🧐
Antes de pensar en cómo resolver un problema, es fundamental entender bien qué se pide.
Esta etapa busca clarificar el problema sin adelantarse a la solución. Usamos el enfoque EPS
(Entrada-Proceso-Salida).
○ Para nuestro ejemplo:
■ Entrada (E): Necesitamos tres notas numéricas (nota1, nota2, nota3).
■ Proceso (P): 1. Sumar las tres notas. 2. Dividir la suma por 3 para obtener el promedio.
3. Comparar el promedio con el valor 4.
■ Salida (S): Debemos mostrar dos cosas: el valor del promedio calculado y un mensaje
que diga "Aprobado" o "No Aprobado".
2. Diseño: Crear el Plan 📝
Una vez comprendido el problema, se diseña una solución general, pero ¡sin escribir código
aún! Aquí se definen los pasos y la lógica que seguirá el programa. Las herramientas comunes
son el pseudocódigo (una descripción del algoritmo en lenguaje casi natural pero
estructurado) y los diagramas de flujo (una representación gráfica).
○ Para nuestro ejemplo (en pseudocódigo):
INICIO
ESCRIBIR "Ingrese la primera nota:"
LEER nota1
ESCRIBIR "Ingrese la segunda nota:"
LEER nota2
ESCRIBIR "Ingrese la tercera nota:"
LEER nota3

promedio = (nota1 + nota2 + nota3) / 3

ESCRIBIR "El promedio es: ", promedio

SI promedio >= 4 ENTONCES


ESCRIBIR "¡Aprobado!"
SINO
ESCRIBIR "No Aprobado."
FIN SI
FIN

3. Implementación (Codificación): Escribir el Código ⌨️


En esta etapa, el diseño del algoritmo (el pseudocódigo) se traduce a un lenguaje de
programación específico, como Python. Aquí es donde las ideas y los diseños toman forma
como programas funcionales.
○ Para nuestro ejemplo (en Python):
Python
# Código de Python
nota1 = float(input("Ingrese la primera nota: "))
nota2 = float(input("Ingrese la segunda nota: "))
nota3 = float(input("Ingrese la tercera nota: "))

promedio = (nota1 + nota2 + nota3) / 3

print(f"El promedio es: {promedio:.2f}")

if promedio >= 4:
print("¡Aprobado! 👍")
else:
print("No Aprobado. 👎")

4. Traducción: Preparando para la Máquina ⚙️


El código que escribimos es comprensible para nosotros, pero no para la computadora. Debe
ser convertido a lenguaje de máquina. Como vimos, en Python, este proceso lo realiza el
intérprete línea por línea en el momento de la ejecución .
5. Ejecución y Prueba: Verificando que Funcione ✅
Finalmente, el programa se ejecuta y se verifica que funcione correctamente. Es fundamental
realizar pruebas con diferentes datos de entrada para asegurarse de que la solución es válida
en distintos escenarios. Si se encuentran errores, se procede a la depuración (debugging).
○ Para nuestro ejemplo:
• Prueba 1 (Aprobado): Entradas: 7, 8, 9. Salida esperada: Promedio 8.00, "¡Aprobado!".
• Prueba 2 (No Aprobado): Entradas: 2, 3, 4. Salida esperada: Promedio 3.00, "No Aprobado.".
Este ciclo a menudo es iterativo: las pruebas pueden revelar la necesidad de volver a etapas
anteriores para refinar el análisis o el diseño.

Refuerzo y Práctica 🧠
● Criterio Clave: El desarrollo de software es un proceso iterativo. No esperes que tu primera
solución sea perfecta. Una buena práctica es resolver manualmente algunos ejemplos
concretos para estar seguro de que entiendes bien el problema antes de codificar.
● A Practicar: Antes de escribir una sola línea de código, tómate el tiempo para analizar el
problema usando el enfoque EPS.
● A Memorizar: Las cinco etapas fundamentales: Análisis, Diseño, Implementación, Traducción
y Prueba.

Aplicaciones en Ciencia de Datos e IA 🤖


Este proceso estructurado es vital en proyectos de datos. Por ejemplo, al desarrollar un modelo de
Machine Learning:
1. Análisis: Comprender el objetivo de negocio (ej: "predecir qué clientes abandonarán la
empresa"), identificar los datos de entrada (características de los clientes) y la salida (una
predicción de "abandona" o "no abandona").
2. Diseño: Elegir el tipo de modelo (ej: clasificación), planificar cómo se tratarán los datos
faltantes y diseñar la estructura de las funciones que realizarán el preprocesamiento y la
evaluación.
3. Implementación: Escribir el código Python usando librerías como Pandas, Scikit-learn y
Matplotlib.
4. Traducción: El intérprete de Python ejecuta el script.
5. Prueba: Entrenar el modelo con datos históricos y evaluarlo con un conjunto de prueba para
medir su rendimiento. Si no es bueno, se vuelve al análisis o al diseño.

Autoevaluación ✅
1. Pregunta: La etapa de Implementación se enfoca principalmente en comprender el problema
y sus requisitos, sin escribir código.
○ Respuesta: Falso. La etapa de Implementación es donde se traduce el diseño a código. La
comprensión del problema ocurre en la etapa de Análisis .
2. Pregunta: El enfoque EPS (Entrada-Proceso-Salida) es una herramienta utilizada en la etapa de
Análisis para organizar las ideas sobre los datos, las operaciones y los resultados esperados.
○ Respuesta: Verdadero. El enfoque EPS es fundamental en la etapa de Análisis para
estructurar la comprensión del problema .
3. Pregunta: La depuración (debugging) es un proceso que solo se realiza una vez, al final del
ciclo de desarrollo.
○ Respuesta: Falso. La depuración es un proceso iterativo que puede ocurrir en cualquier
momento después de la implementación, especialmente durante las pruebas, y a menudo
requiere volver a etapas anteriores del ciclo .

1.3. Introducción a los Algoritmos


La Receta Perfecta: El Corazón de la Solución 🧑‍🍳
Consideremos tareas como buscar el camino más corto en Google Maps o el funcionamiento de un
algoritmo que clasifica imágenes. No importa cuántas veces uses Google Maps para la misma ruta,
siempre sigue una lógica consistente. Un modelo de clasificación, una vez entrenado, aplicará
siempre la misma secuencia de pasos para decidir si una imagen contiene un perro o un gato. Este
comportamiento predecible y consistente no es casualidad; es el resultado de que el software se
basa en algoritmos.
Esto nos lleva a una pregunta fundamental: ¿Qué es exactamente un algoritmo y por qué sus
características son tan críticas para garantizar que un programa funcione de manera correcta y
confiable?

Definiendo el Algoritmo: Más que una Lista de Pasos


Un algoritmo es un conjunto finito de instrucciones o pasos bien definidos, precisos, sin
ambigüedad y efectivos, diseñados para resolver un problema o realizar una tarea. Es la "receta"
que describe cómo resolver un problema, independientemente del lenguaje en que se vaya a
"cocinar" .
Para que una secuencia de pasos sea un algoritmo válido, debe cumplir con cinco características
esenciales. Reforzaremos nuestra analogía de la "receta de cocina" para entenderlas:
1. Entradas (Input): Debe tener cero o más entradas. Son los datos iniciales con los que
trabajará.
○ Analogía de la receta: Son los ingredientes listados al principio (ej: 250g de harina, 2
huevos, 100ml de leche).
2. Salidas (Output): Debe producir una o más salidas. Son los resultados después de su ejecución.
○ Analogía de la receta: Es la foto del plato terminado que se espera obtener.
3. Precisión/Exactitud: Cada paso debe estar definido con exactitud, sin ambigüedad. No debe
haber lugar a interpretaciones.
○ Analogía de la receta: Una mala receta (mal algoritmo) diría "añadir un poco de harina".
Una receta precisa diría "añadir 250 gramos de harina tamizada".
4. Finitud: Debe terminar después de un número finito de pasos. Un algoritmo no puede
ejecutarse indefinidamente.
○ Analogía de la receta: La receta debe tener un último paso. No puede decir "remover la
mezcla para siempre".
5. Efectividad: Cada paso debe ser realizable en un tiempo finito y con recursos razonables.
○ Analogía de la receta: Un paso que diga "precalentar el horno a 5000°C" no es efectivo,
porque una cocina estándar no puede hacerlo.
Es crucial entender que los algoritmos son independientes del lenguaje de programación. Una
buena receta se puede usar en cualquier cocina (Python, Java, C++). La calidad de tu código
dependerá directamente de la calidad de tu algoritmo.

Refuerzo y Práctica 🧠
● Criterio Clave: Piensa en un algoritmo como una receta de cocina perfecta. La calidad de tu
programa final depende de la calidad de esa receta.
● A Practicar: Describe en lenguaje natural los pasos para tareas cotidianas como "preparar una
taza de café" o "cambiar una rueda del coche". Enfócate en la precisión y la secuencia lógica.
● A Memorizar: Las cinco características de un algoritmo: entradas, salidas, precisión, finitud y
efectividad.

Aplicaciones en Ciencia de Datos e IA 🤖


● Algoritmos de Ordenamiento y Búsqueda: Antes de aplicar modelos de IA, los datos a menudo
necesitan ser organizados. Algoritmos clásicos como Merge Sort (para ordenar) o Binary Search
(para buscar eficientemente) son implementados en Python para manejar grandes volúmenes
de datos.
● Algoritmos de Optimización en Machine Learning: Los modelos de IA "aprenden" ajustando
sus parámetros. Este ajuste se realiza mediante algoritmos de optimización como el Descenso
de Gradiente, que es un proceso iterativo con pasos precisos que se repite hasta alcanzar una
condición de finitud (convergencia).

Autoevaluación ✅
1. Pregunta: Un algoritmo es efectivo si puede ejecutarse en un tiempo finito con recursos
determinados.
○ Respuesta: Verdadero. La efectividad garantiza que cada paso del algoritmo sea realizable
en la práctica.
2. Pregunta: Es posible que un algoritmo produzca una salida diferente cada vez que se ejecuta
con la misma entrada.
○ Respuesta: Falso. Un algoritmo debe ser preciso y determinista. Para la misma entrada,
siempre debe producir la misma salida.
3. Pregunta: Un algoritmo debe ser diseñado específicamente para un lenguaje de programación,
ya que no puede ser implementado en otros lenguajes.
○ Respuesta: Falso. Los algoritmos son independientes del lenguaje. Describen la lógica de la
solución, que luego puede ser implementada en cualquier lenguaje de propósito general.
Unidad 2: Análisis y Diseño de Problemas: El Arte de Pensar Antes de
Programar 🧠

¡Bienvenido/a a la Unidad 2! Aquí nos enfocaremos en la fase más importante de la resolución de


problemas: el análisis y el diseño. Antes de escribir una sola línea de código, es fundamental
aprender a "pensar como un programador", descomponiendo problemas complejos y organizando
nuestras ideas de manera lógica. Esta unidad te dará las herramientas para construir soluciones
robustas y eficientes.

2.1. Análisis del Problema: Enfoque EPS y Descomposición Top-Down


La Estrategia Detrás de la Estrategia: ¿Por Qué el Análisis es Clave?
Imagina que eres un científico de datos encargado de construir un sistema de recomendación de
películas. Si simplemente empiezas a escribir código para "recomendar películas" sin antes
entender: ¿qué datos de entrada tienes (historial de visionado, calificaciones)? ¿qué procesamiento
necesitas (algoritmos de filtrado, análisis de similitud)? y ¿qué salida esperas (una lista de 10
películas)? Es muy probable que tu proyecto fracase.
En la Ciencia de Datos y la IA, donde la complejidad es enorme, la fase de análisis no es un paso
opcional, ¡es la piedra angular! La capacidad de observar, describir y comprender un problema
desde su lógica y estructura es lo que distingue una solución rudimentaria de una inteligente y
escalable. Entonces, ¿cómo podemos abordar sistemáticamente la comprensión de un problema?

Desvelando el Problema: El Modelo EPS y el Pensamiento Top-Down


Para responder a esta pregunta, nos apoyamos en dos enfoques fundamentales:
1. El Modelo EPS (Entrada-Proceso-Salida): Como vimos en la Unidad 1, este modelo es la base
del análisis. Nos obliga a identificar con claridad :
○ Entrada (E): Los datos que el programa necesita para funcionar.
○ Proceso (P): Las operaciones y transformaciones que se deben realizar con los datos de
entrada.
○ Salida (S): El resultado esperado que el programa debe producir.
2. La Estrategia Top-Down (De Arriba hacia Abajo): Este enfoque nos permite descomponer un
problema complejo en partes más pequeñas y manejables. En lugar de intentar resolver todo
de una vez, dividimos el problema en subproblemas, y luego cada subproblema se
descompone a su vez, hasta que cada parte es simple y fácil de entender y resolver .

Aplicando los Enfoques: De lo Simple a lo Complejo


● Ejemplo Simple ("Promedio y Aprobación"):
○ Top-Down: El problema se puede ver como dos subproblemas: 1. Calcular el promedio. 2.
Determinar la condición de aprobación.
○ EPS (ya analizado): Entradas: 3 notas. Proceso: cálculo y comparación. Salidas: promedio y
mensaje.
● Ejemplo Complejo de Ciencia de Datos ("Análisis de Sentimiento de Reseñas de Películas"):
○ Top-Down: El gran problema "Analizar Sentimiento" se descompone en:
1. Cargar Datos de Reseñas.
2. Preprocesar el Texto de cada reseña.
3. Calcular una Puntuación de Sentimiento para cada reseña.
4. Generar un Reporte Final (ej. porcentaje de reseñas positivas vs. negativas).
○ EPS para el subproblema "Preprocesar Texto":
■ Entrada: Una reseña de texto crudo (ej: "¡La película fue increíble! Muy buena.").
■ Proceso: Convertir a minúsculas, eliminar signos de puntuación, dividir en palabras
(tokenización), eliminar palabras comunes e irrelevantes (stopwords como "el", "la",
"de").
■ Salida: Una lista de palabras limpias y significativas (ej: ['película', 'increíble', 'buena']).
Este método de descomposición nos permite abordar problemas de cualquier tamaño de una
manera estructurada y lógica.

Refuerzo y Práctica 🧠
● Criterio Clave: En la etapa de análisis, el foco está en qué se necesita hacer, no en cómo se
implementará en código. No te adelantes a las decisiones de diseño.
● A Practicar: Toma cualquier problema cotidiano (planificar un viaje, organizar un evento) y
trata de descomponerlo usando Top-Down y definir sus componentes con EPS.
● A Memorizar: Los seis pasos recomendados para el análisis: 1. Leer detenidamente. 2.
Determinar resultados y datos. 3. Hacer un diagrama si es necesario. 4. Asignar nombres
sugerentes. 5. Plantear relaciones. 6. Verificar con valores concretos .

Autoevaluación ✅
1. Pregunta: En la etapa de Análisis, el objetivo principal es escribir el pseudocódigo del
algoritmo que resolverá el problema.
○ Respuesta: Falso. En el Análisis, el foco está en comprender el problema (EPS, Top-Down).
El diseño del algoritmo con pseudocódigo viene en la siguiente etapa .
2. Pregunta: El modelo EPS es una herramienta útil para organizar ideas y precisar los elementos
de una situación problemática.
○ Respuesta: Verdadero. El enfoque EPS es la herramienta central del análisis para
identificar claramente qué datos se necesitan, qué se debe hacer con ellos y qué
resultados se esperan .
3. Pregunta: Según el enfoque Top-Down, es recomendable intentar resolver un problema
complejo directamente en su totalidad.
○ Respuesta: Falso. El enfoque Top-Down propone exactamente lo contrario: descomponer
un problema complejo en partes más pequeñas y manejables para facilitar su resolución .

2.2. Diseño de Algoritmos: Pseudocódigo y Diagramas de Flujo


De la Idea a los Pasos Concretos: ¿Cómo se Crea un Plan Ejecutable?
Una vez que hemos analizado un problema, nos enfrentamos a un nuevo desafío: ¿cómo traducir
esa comprensión abstracta en una serie de pasos concretos que una computadora pueda seguir?
Aquí es donde entra el Diseño de Algoritmos. Es el momento de convertir el "qué" del análisis en el
"cómo" detallado. Para ello, utilizamos notaciones estandarizadas que nos permiten definir la
solución de forma lógica y clara antes de escribir una sola línea de código.

La Arquitectura de la Solución: Pseudocódigo y Diagramas de Flujo


Las dos herramientas más comunes y efectivas para el diseño de algoritmos son:
1. Pseudocódigo: Es una descripción de alto nivel de un algoritmo que utiliza un lenguaje similar
al natural (en nuestro caso, español) pero con una estructura lógica que se asemeja a la de un
lenguaje de programación. Su gran ventaja es que nos permite concentrarnos en la lógica sin
preocuparnos por la sintaxis estricta de Python. Es como una "receta" detallada.
2. Diagramas de Flujo: Son una representación gráfica de un algoritmo. Utilizan símbolos
estandarizados para representar diferentes tipos de operaciones (inicio/fin, proceso, decisión,
etc.) y flechas para indicar el flujo de ejecución. Son excelentes para visualizar la lógica,
especialmente las bifurcaciones y bucles .

Visualizando la Lógica: Comparación Lado a Lado


Veamos cómo se representan nuestro problema de "calcular promedio y aprobación" con ambas
herramientas. Esta comparación te ayudará a ver cómo la representación textual y la gráfica se
complementan.
Pseudocódigo:
INICIO
ESCRIBIR "Ingrese la primera nota:"
LEER nota1
//... (leer nota2 y nota3)
promedio = (nota1 + nota2 + nota3) / 3
ESCRIBIR "Promedio: ", promedio
SI promedio >= 4 ENTONCES
ESCRIBIR "¡Aprobado!"
SINO
ESCRIBIR "No Aprobado."
FIN SI
FIN

Diagrama de Flujo:
(Imagina un diagrama con los siguientes símbolos conectados por flechas)
1. Un óvalo de "Inicio".
2. Tres paralelogramos para "Leer nota1", "Leer nota2", "Leer nota3".
3. Un rectángulo para el proceso "promedio = (nota1+nota2+nota3)/3".
4. Un paralelogramo para "Escribir promedio".
5. Un rombo de decisión con la pregunta "¿promedio >= 4?".
○ De la salida "Sí" del rombo, una flecha apunta a un paralelogramo "Escribir '¡Aprobado!'".
○ De la salida "No" del rombo, una flecha apunta a un paralelogramo "Escribir 'No
Aprobado.'".
6. Ambas rutas se unen y apuntan a un óvalo de "Fin".
Para que puedas crear tus propios diagramas, aquí tienes una tabla de referencia con los símbolos
estándar:
Símbolo (Forma) Nombre Función y Ejemplo

Óvalo Terminal Indica el Inicio o Fin del algoritmo.

Paralelogramo Entrada / Salida Para leer datos (LEER nota1) o mostrar resultados
(ESCRIBIR promedio).
Rectángulo Proceso Para realizar cálculos y asignaciones (promedio = (nota1 +
nota2 + nota3) / 3).

Rombo Decisión Representa una condición (¿promedio >= 4?) con dos
salidas (Sí/No).

Flecha Línea de Flujo Conecta los símbolos e indica el orden de ejecución.

Diseñar algoritmos antes de programar es una práctica fundamental, comparable a "hacer los
planos antes de construir una casa". Nos da claridad, previene errores y ahorra mucho tiempo.

Refuerzo y Práctica 🧠
● Criterio Clave: La legibilidad es un objetivo principal. Tu pseudocódigo y tus diagramas deben
ser claros y fáciles de entender para cualquier persona, no solo para ti.
● A Practicar: Elige uno de los problemas que analizaste en la sección anterior y trata de diseñar
la solución usando tanto pseudocódigo como un diagrama de flujo.
● A Memorizar: Las características de un buen diseño algorítmico: claridad, precisión, finitud,
efectividad y completitud.

Autoevaluación ✅
1. Pregunta: El pseudocódigo permite describir un algoritmo utilizando la sintaxis exacta de
Python.
○ Respuesta: Falso. El pseudocódigo utiliza un lenguaje similar al natural y está libre de las
restricciones sintácticas de un lenguaje de programación real, permitiendo enfocarse en la
lógica.
2. Pregunta: Los diagramas de flujo son una representación gráfica de un algoritmo que utiliza
símbolos estandarizados.
○ Respuesta: Verdadero. Los diagramas de flujo son una herramienta visual para
representar el flujo de ejecución y la lógica de un algoritmo .
3. Pregunta: La etapa de diseño de algoritmos ocurre antes del análisis del problema.
○ Respuesta: Falso. La secuencia correcta es Análisis -> Diseño -> Implementación. Primero
se comprende el problema (análisis) y luego se define cómo resolverlo (diseño) .

2.3. Herramientas de Diseño: PSeInt, Nuestro Laboratorio de Lógica 🔬


El Laboratorio de Ideas: ¿Cómo Probamos la Lógica Antes de Programar?
Hemos analizado el problema y diseñado el algoritmo. Pero, ¿cómo podemos estar seguros de que
nuestra lógica es correcta? ¿Y si nuestro algoritmo tiene un error que solo aparece con ciertos
datos? Construir todo el sistema en Python para luego descubrir un fallo lógico es costoso y
frustrante. Necesitamos un "laboratorio" donde podamos simular y verificar nuestras ideas
algorítmicas de forma sencilla.
La pregunta es: ¿Existe alguna herramienta que nos permita escribir, ejecutar y probar nuestros
algoritmos en pseudocódigo de manera intuitiva, ayudándonos a validar nuestra lógica antes de la
implementación final en Python?

PSeInt: Tu Simulador de Lógica a Prueba de Errores


La respuesta es PSeInt (Pseudo Intérprete). PSeInt es un programa gratuito diseñado
específicamente para aprender a diseñar algoritmos. Actúa como un intérprete de pseudocódigo
en español, lo que nos permite enfocarnos en el pensamiento algorítmico sin la carga de la sintaxis
de un lenguaje de programación formal.
PSeInt es una "vacuna" contra los errores de lógica. Su valor principal es que nos permite aislar el
pensamiento algorítmico (el "qué hacer") de la sintaxis de Python (el "cómo escribirlo"). Si tu
algoritmo no funciona en PSeInt, que usa español simple, es una señal clara de que la lógica tiene
un problema y tampoco funcionará en Python.
PSeInt nos proporciona un entorno donde podemos:
● Escribir Pseudocódigo: Permite redactar algoritmos con una sintaxis simple y en español, con
ayudas como el autocompletado.
● Generar Diagramas de Flujo: Puede crear automáticamente un diagrama de flujo a partir de tu
pseudocódigo, ayudándote a visualizar la lógica.
● Ejecutar y Probar (Prueba de Escritorio): Su funcionalidad más valiosa es la capacidad de
ejecutar el algoritmo paso a paso. Puedes introducir diferentes valores y observar cómo se
comportan las variables en cada línea, lo que te permite detectar errores lógicos de manera
interactiva.
Una vez que tu algoritmo funciona perfectamente en PSeInt, puedes incluso usar su función para
exportar el pseudocódigo a Python. Esto crea el puente perfecto entre la fase de diseño y la de
implementación.

Refuerzo y Práctica 🧠
● Criterio Clave: Usa PSeInt para validar la lógica de tu algoritmo, no para escribir código final. El
objetivo es entrenar tu pensamiento algorítmico.
● A Practicar: Descarga e instala PSeInt. Toma los ejercicios de la guía y resuélvelos primero en
PSeInt. Guarda los archivos .psc y comparte tus dudas en los foros.
● A Memorizar: PSeInt es un simulador de algoritmos, no un entorno de desarrollo profesional.
Es una herramienta de aprendizaje para la etapa de diseño.

Autoevaluación ✅
1. Pregunta: PSeInt es un lenguaje de programación profesional utilizado para crear aplicaciones
comerciales.
○ Respuesta: Falso. PSeInt es una herramienta educativa diseñada para aprender y simular
algoritmos en pseudocódigo, antes de pasar a un lenguaje de programación real como
Python.
2. Pregunta: Una de las principales ventajas de PSeInt es que permite realizar "pruebas de
escritorio" interactivas, simulando el funcionamiento de un algoritmo con diferentes datos de
entrada.
○ Respuesta: Verdadero. La capacidad de ejecutar y depurar algoritmos paso a paso es una
de las características más potentes de PSeInt para validar la lógica.
3. Pregunta: Después de probar un algoritmo en PSeInt, se recomienda ignorar ese diseño y
empezar de cero en Python.
○ Respuesta: Falso. Todo lo contrario. El diseño validado en PSeInt debe usarse como los
"planos" para la implementación en Python. Esto ahorra tiempo, previene errores y hace
que la codificación sea mucho más fluida .
Unidad 3: Primeros Pasos en Python: De la Idea al Código Funcional 💻

¡Felicidades por llegar hasta aquí! Esta unidad marca un punto de inflexión: comenzaremos a
traducir nuestras ideas y diseños algorítmicos en código Python real y funcional. Aprenderemos la
sintaxis básica, cómo manejar datos y cómo interactuar con el usuario. Es el momento de hacer que
nuestros programas cobren vida. ¡Vamos a ello!

3.1. Introducción a Python: Características y Entornos de Desarrollo


¿Por Qué Python? La Elección Estratégica
En el vertiginoso mundo de la informática, Python se ha consolidado como una herramienta
esencial, especialmente en campos como la Ciencia de Datos y la IA. Autores como Jeffrey
Rosenschein destacan que Python permite a los estudiantes "avanzar rápido con mejores
resultados" y experimentar un "bajo nivel de frustración". Esto es vital en áreas donde la velocidad
de prototipado y la facilidad de experimentación son cruciales.
La pregunta es: ¿por qué Python es el lenguaje elegido para sentar las bases en este curso? Las
características que ya mencionamos en la Unidad 1 son la respuesta:
● Interpretado: El código se ejecuta línea por línea, lo que permite ver resultados de inmediato y
reduce la frustración inicial.
● Tipado Dinámico y Fuerte: Simplifica la escritura del código (dinámico) pero mantiene la
coherencia de los datos (fuerte), previniendo errores comunes.
● Multiplataforma: Tu código funcionará en Windows, macOS o Linux sin cambios .

Nuestras Herramientas de Trabajo: Entornos de Desarrollo


Para programar en Python, utilizaremos dos entornos principales:
1. Sesiones Interactivas (REPL - Read-Eval-Print Loop): Es la consola de Python. Permite ejecutar
código línea por línea y ver los resultados al instante. Es ideal para experimentar y probar
pequeñas porciones de código.
2. Entornos de Desarrollo Integrados (IDEs): Para escribir programas más extensos y
organizados, utilizaremos un IDE. Recomendamos Visual Studio Code (VS Code) con la
extensión de Python. Un IDE ofrece herramientas avanzadas para escribir, depurar y ejecutar
código de manera mucho más eficiente.

¡Hola, Mundo! Nuestro Primer Programa en Python


El tradicional "¡Hola, Mundo!" en Python demuestra la simplicidad y legibilidad del lenguaje.
Python
# Este es nuestro primer programa en un archivo llamado 'hola_mundo.py'
print("¡Hola, Mundo! 👋")

Para ejecutarlo desde la terminal de VS Code, simplemente escribimos python hola_mundo.py y


veremos el saludo en pantalla. ¡Así de fácil!

Refuerzo y Práctica 🧠
● Practica con el intérprete: No te limites a leer. Abre la consola de Python y experimenta. Es la
mejor forma de "entrenar el pensamiento algorítmico".
● Familiarízate con tu IDE: Dedica tiempo a aprender a usar VS Code. Será tu herramienta
principal a lo largo del curso y en tu futura carrera profesional.

Autoevaluación ✅
1. Pregunta: Python es un lenguaje compilado, lo que significa que el código se traduce a código
máquina antes de su ejecución.
○ Respuesta: Falso. Python es un lenguaje interpretado. El código se ejecuta línea por línea
sin un paso de compilación previo.
2. Pregunta: Las sesiones interactivas de Python (REPL) son útiles principalmente para programas
complejos, no para experimentación básica.
○ Respuesta: Falso. Las sesiones interactivas son ideales para la experimentación, probar
sintaxis y ejecutar pequeñas porciones de código rápidamente.
3. Pregunta: Una ventaja de Python es su sintaxis limpia, que facilita el aprendizaje de los
conceptos de programación.
○ Respuesta: Verdadero. La legibilidad de Python permite a los principiantes enfocarse en la
lógica en lugar de en detalles sintácticos complejos.

3.2. Sintaxis Básica y el Arte del Código Legible (PEP 8)


Escribiendo Código que Otros Puedan Entender
A medida que los programas crecen, la legibilidad del código se vuelve tan crucial como su
funcionalidad. Martin Fowler, una figura prominente en el desarrollo de software, dijo: "Cualquier
tonto puede escribir código que una computadora pueda entender. Los buenos programadores
escriben código que los humanos puedan entender". Esto es especialmente cierto en proyectos de
Ciencia de Datos e IA, que a menudo son colaborativos y necesitan ser mantenidos a largo plazo.
La pregunta clave es: ¿cómo estructuramos el código Python para maximizar su claridad y cuáles
son las mejores prácticas para escribir un código que, idealmente, "se explique por sí mismo"?

Las Reglas del Juego: Sintaxis y Convenciones de Python


Un programa Python se compone de elementos como módulos, funciones, clases, variables y
expresiones. Para que este código sea legible, seguimos una serie de reglas y convenciones.
● Identificadores: Son los nombres que damos a variables, funciones, etc. Deben ser
descriptivos. Evita nombres de una sola letra (excepto para contadores en bucles, como i).
○ Mala práctica: a = "Leonel", b = 20
○ Buena práctica: nombre_usuario = "Leonel", edad_cliente = 20
● Palabras Reservadas: Son palabras con un significado especial en Python (como if, for, while,
True) y no pueden usarse como identificadores .
● Indentación: La indentación (los espacios al principio de una línea) no es opcional en Python;
es parte fundamental de la sintaxis y define los bloques de código. Un error de indentación
causará que el programa no funcione .
● Comentarios y Docstrings:
○ Comentarios (#): Se usan para aclaraciones rápidas. Empiezan con # y se extienden hasta
el final de la línea.
○ Docstrings ("""..."""): Son cadenas de documentación multilínea que explican el propósito
de módulos, funciones y clases. Son una práctica profesional esencial .
PEP 8: El ADN del Código Profesional 📜
La comunidad de Python ha adoptado una guía de estilo llamada PEP 8 que establece las
convenciones para escribir código limpio, consistente y legible. Seguir PEP 8 no es solo una "buena
práctica", es una de las primeras habilidades profesionales que un desarrollador aprende, ya que
facilita enormemente la colaboración y el mantenimiento del código.
Aquí tienes una "chuleta" con las reglas de PEP 8 más importantes para empezar
Regla Ejemplo Correcto ✅ Ejemplo Incorrecto ❌ Razón

Nombres de mi_variable, miVariable, MiVariable snake_case (minúsculas


Variables/Funciones calcular_promedio con guion bajo) para
legibilidad.

Nombres de TASA_INTERES = 0.21 tasa_interes = 0.21 ALL_CAPS para indicar


Constantes valores que no deben
cambiar.

Nombres de Clases class MiClaseDePrueba: class PascalCase (o


mi_clase_de_prueba: CapWords) es el
estándar.

Indentación if x > 5: if x > 5: 4 espacios (no


print("Mayor") print("Mayor") tabuladores) para
definir bloques. Es
obligatorio.

Espacios en x=y+1 x=y+1 Un espacio a cada lado


Operadores de los operadores (=, +,
-, etc.) mejora la
claridad.

Longitud de Línea (Líneas de hasta 79-99 (Líneas muy largas que Mantiene el código
caracteres) requieren scroll visible en una pantalla
horizontal) estándar.

Autoevaluación ✅
1. Pregunta: Según PEP 8, el nombre de una variable como miVariable es la convención
recomendada.
○ Respuesta: Falso. La convención para variables y funciones en Python es snake_case, es
decir, mi_variable.
2. Pregunta: En Python, la indentación es solo una sugerencia de estilo para hacer el código más
bonito.
○ Respuesta: Falso. La indentación es obligatoria y forma parte de la sintaxis del lenguaje.
Define la estructura de los bloques de código.
3. Pregunta: Los docstrings son comentarios que el intérprete ignora y no tienen ningún uso
práctico más allá de la lectura del código.
○ Respuesta: Falso. Los docstrings son cadenas de documentación especiales que pueden
ser extraídas automáticamente por herramientas para generar documentación, y son
accesibles en tiempo de ejecución .
3.3. Variables, Constantes y Tipos de Datos Primitivo
Almacenando Información: Variables y Constantes
En cualquier programa, necesitamos un lugar para almacenar la información con la que trabajamos.
Para esto, usamos variables. Una variable es esencialmente un nombre que le damos a un espacio
en la memoria de la computadora donde guardamos un valor .
● Declaración y Asignación: En Python, no necesitas "declarar" una variable antes de usarla.
Simplemente le asignas un valor con el operador = y la variable se crea automáticamente.
Python
# Asigna el valor entero 30 a la variable 'edad'
edad = 30
# Asigna la cadena "Juan" a la variable 'nombre'
nombre = "Juan"

Importante: Las asignaciones son "mudas", no muestran nada por pantalla. Para ver el valor,
debes usar print(nombre).
● Constantes: Python no tiene un mecanismo para crear constantes cuyo valor no pueda
cambiar. Sin embargo, por convención (siguiendo PEP 8), usamos nombres en MAYÚSCULAS
para indicar que una variable no debe ser modificada durante la ejecución del programa.
Python
PI = 3.14159
TASA_IVA = 0.21

Los Bloques de Construcción: Tipos de Datos Primitivos


Los tipos de datos primitivos son los tipos básicos incorporados en el lenguaje. Aquí están los más
importantes para empezar:
Tipo de Dato Nombre en Python Descripción Ejemplo de Uso en
Ciencia de Datos

Entero int Números enteros, sin Conteo de


decimales. observaciones, número
de épocas en un
entrenamiento.

Flotante float Números con parte Medidas de sensores,


decimal (se usa punto precios, métricas de
.). error (ej. MSE).

Cadena de Texto str Secuencias de Nombres, categorías,


caracteres. Son texto de documentos
inmutables (no se para NLP.
pueden cambiar una
vez creadas).

Booleano bool Representa verdad o Flags (ej: es_fraude),


falsedad (True o False). resultado de una
clasificación binaria.

Para saber el tipo de una variable, puedes usar la función type(). Por ejemplo, print(type(edad))
mostraría <class 'int'>.
Refuerzo y Práctica 🧠
● Tipado Dinámico, no Debilidad: Aunque Python no te exige declarar tipos, es crucial que sepas
qué tipo de dato estás manejando. Python es fuertemente tipado, lo que significa que no te
dejará, por ejemplo, sumar un int y un str directamente.
● Mutabilidad vs. Inmutabilidad: Este es un concepto clave. Las cadenas (str) y los números (int,
float) son inmutables. Más adelante veremos tipos mutables como las listas, y esta diferencia
es fundamental.

Autoevaluación ✅
1. Pregunta: En Python, una variable de tipo int puede almacenar un número con decimales
como 3.5.
○ Respuesta: Falso. El tipo int es solo para números enteros. Los números con decimales se
almacenan en variables de tipo float .
2. Pregunta: Las cadenas (str) en Python son mutables, lo que significa que puedes cambiar un
carácter individual después de crear la cadena.
○ Respuesta: Falso. Las cadenas son inmutables. Cualquier operación que parezca modificar
una cadena en realidad crea una nueva .
3. Pregunta: Para declarar una constante que no pueda cambiar, se usa la palabra clave const en
Python.
○ Respuesta: Falso. Python no tiene la palabra clave const. Se usa una convención de
nombrar la variable en mayúsculas (ej. MI_CONSTANTE) para indicar que no debe ser
modificada.

3.4. Operadores y Expresiones: El Lenguaje de la Lógica


Realizando Operaciones: De Datos a Resultados
Necesitamos más que solo almacenar datos; necesitamos operar con ellos. Las expresiones son
combinaciones de valores, variables y operadores que Python evalúa para producir un resultado .
● Operadores Aritméticos: Para cálculos matemáticos.
○ + (Suma), - (Resta), * (Multiplicación)
○ / (División, siempre devuelve un float)
○ // (División entera, redondea hacia abajo)
○ % (Módulo, devuelve el resto de la división)
○ ** (Exponenciación, potencia)
● Operadores de Comparación (Relacionales): Comparan dos valores y siempre devuelven un
booleano (True o False).
○ == (Igual a), != (Distinto de)
○ < (Menor que), <= (Menor o igual que)
○ > (Mayor que), >= (Mayor o igual que)
● Operadores Lógicos: Combinan o modifican expresiones booleanas.
○ and: True si ambos lados son True.
○ or: True si al menos un lado es True.
○ not: Invierte el valor booleano.
● Operadores de Asignación: Modifican el valor de una variable.
○ =: Asignación simple.
○ +=, -=, *=, etc.: Operadores compuestos (ej. x += 1 es lo mismo que x = x + 1).
El Orden Importa: Precedencia de Operadores
Python sigue las reglas matemáticas estándar (PEMDAS) para evaluar expresiones. La exponenciación (**)
tiene la mayor prioridad, seguida de la multiplicación/división, y finalmente la suma/resta. Puedes usar
paréntesis () para forzar un orden de evaluación diferente y mejorar la claridad .
2 + 3 * 4 se evalúa como 2 + 12 = 14.
(2 + 3) * 4 se evalúa como 5 * 4 = 20.

¡Cuidado! El Error Más Común: TypeError y los Tipos Incompatibles


Este es, con diferencia, el error más frecuente para un principiante. Un buen material didáctico
debe anticipar y resolver tus problemas.
El Problema:
Imagina que escribes este código:

Python
edad_texto = input("Dime tu edad: ") # El usuario escribe "25"
print("En 10 años tendrás: " + (edad_texto + 10))

Este código fallará y mostrará un error.


El Error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

La Causa Raíz:
El error TypeError significa que estás intentando realizar una operación entre tipos de datos incompatibles.
La función input() siempre devuelve una cadena de texto (str). En nuestro ejemplo, edad_texto no es el
número 25, sino el texto "25". Python no sabe cómo "sumar" (+) un texto ("25") y un número entero (10).
La Solución: Conversión Explícita de Tipos
Para solucionar esto, debemos convertir explícitamente la cadena de texto a un tipo numérico antes de
operar con ella. Usamos las funciones int() o float():

Python
edad_texto = input("Dime tu edad: ")
edad_numero = int(edad_texto) # Convertimos el str a int

print("En 10 años tendrás: " + str(edad_numero + 10)) # También convertimos el resultado a str para concatenar
# O mejor aún, usamos f-strings (ver siguiente sección)
print(f"En 10 años tendrás: {edad_numero + 10} años.")

Anticipar y corregir este tipo de errores es un paso fundamental para escribir código robusto.

Autoevaluación ✅
1. Pregunta: En la expresión 10 + 2 * 3, la suma se realiza antes que la multiplicación.
○ Respuesta: Falso. Debido a la precedencia de operadores, la multiplicación (*) se realiza
primero. El resultado es 10 + 6 = 16 .
2. Pregunta: El operador // realiza una división que siempre devuelve un float.
○ Respuesta: Falso. El operador // realiza una división entera, devolviendo un int. El
operador / es el que siempre devuelve un float .
3. Pregunta: El operador = se usa para comparar si dos valores son iguales.
○ Respuesta: Falso. El operador = es para asignación. Para comparar igualdad, se usa el
operador == .

3.5. Interacción con el Usuario: Entrada y Salida de Datos


Haciendo Programas Interactivos
Hasta ahora, nuestros datos estaban "cableados" en el código. Para que un programa sea
verdaderamente útil, debe poder recibir información del usuario y mostrarle los resultados de
manera comprensible.
● Entrada de Datos: La Función input()
La función input() pausa el programa, muestra un mensaje al usuario (prompt) y espera a que
ingrese datos por teclado. Recuerda: input() siempre devuelve una cadena de texto (str) .
Python
nombre = input("Por favor, ingresa tu nombre: ")
edad_str = input("Ahora, ingresa tu edad: ")
edad_num = int(edad_str) # ¡No olvides convertir!

● Salida de Datos: La Función print()


La función print() se usa para mostrar texto y valores de variables en la consola.

Formateo Profesional con F-Strings 🚀


Si bien puedes concatenar cadenas con + o usar comas en print(), la forma más moderna, legible y
potente de formatear la salida en Python es usando f-strings (Formatted String Literals), disponibles
desde Python 3.6. Simplemente prefija la cadena con una f o F.
Las f-strings son mucho más que una simple sustitución de variables. Te permiten hacer cosas
asombrosas directamente dentro de la cadena, haciendo tu código más limpio y profesional.
● Expresiones y Cálculos Directos:
Python
base = 10
altura = 5
print(f"El área de un rectángulo de base {base} y altura {altura} es {base * altura}.")
# Salida: El área de un rectángulo de base 10 y altura 5 es 50.

● Formateo de Números:
Python
precio = 49.956
print(f"El precio final es: ${precio:.2f}") # Formatea a 2 decimales
# Salida: El precio final es: $49.96

● Llamadas a Funciones y Métodos:


Python
nombre = "ana"
print(f"Hola, {nombre.capitalize()}!") # Llama al método.capitalize()
# Salida: Hola, Ana!
● Alineación y Relleno:
Python
texto = "Python"
print(f"|{texto:<10}|") # Alineado a la izquierda en 10 espacios
print(f"|{texto:>10}|") # Alineado a la derecha en 10 espacios
print(f"|{texto:^10}|") # Centrado en 10 espacios
# Salida:
# |Python |
# | Python|
# | Python |

● Depuración Rápida (Python 3.8+):


Python
variable_a = 123
print(f"Depurando: {variable_a=}") # Muestra el nombre y el valor
# Salida: Depurando: variable_a=123

● Manejo de Llaves: Para mostrar llaves literales, simplemente duplícalas.


Python
print(f"En Python, los diccionarios usan llaves: {{'clave': 'valor'}}")
# Salida: En Python, los diccionarios usan llaves: {'clave': 'valor'}

Autoevaluación ✅
1. Pregunta: Si un usuario ingresa "123" usando input(), el valor se almacena como un int.
○ Respuesta: Falso. input() siempre devuelve un str. Se necesita una conversión explícita:
int(input()) .
2. Pregunta: Las f-strings solo pueden usarse para insertar el valor de variables en una cadena.
○ Respuesta: Falso. Las f-strings son extremadamente potentes y permiten evaluar
expresiones, llamar a funciones y aplicar un formato complejo directamente dentro de la
cadena.
3. Pregunta: El código print(f"El resultado es {{2+2}}") mostrará "El resultado es 4".
○ Respuesta: Falso. Las llaves dobles {{...}} se usan para escapar las llaves y mostrarlas
literalmente. La salida sería "El resultado es {2+2}". Para evaluar la expresión, se usan
llaves simples: f"El resultado es {2+2}".
Unidad 4: El Paradigma Imperativo y las Estructuras de Control: Dotando de
Inteligencia a Nuestros Programas 🤖

¡Bienvenido/a a la Unidad 4! Ya hemos aprendido los conceptos básicos de Python. Ahora, es el


momento de dar un salto cualitativo: aprenderemos a controlar el flujo de ejecución de nuestros
programas. Esto significa que podremos hacer que tomen decisiones, elijan diferentes caminos y
repitan acciones automáticamente. Estas son las herramientas que nos permitirán pasar de simples
scripts lineales a soluciones complejas y flexibles.

4.1. Fundamentos del Paradigma Imperativo y Programación Estructurada


Dando Órdenes: El Paradigma Imperativo
Imagina que estás dando instrucciones a un robot para que prepare un café. Le dirías: "1. Coge la
taza. 2. Pon una cucharada de café. 3. Vierte agua caliente. 4. Remueve". Le estás dando una serie
de órdenes o instrucciones que debe ejecutar paso a paso, en un orden específico. Cada paso
modifica el estado del mundo (la taza ahora tiene café, luego tiene agua, etc.).
Esto es, en esencia, el Paradigma Imperativo de Programación. Los programas se conciben como
una secuencia de comandos que le dicen a la computadora qué hacer y en qué orden, modificando
el estado del programa (los valores de las variables) a medida que avanza.

Construyendo con Lógica: La Programación Estructurada


Dentro del paradigma imperativo, la Programación Estructurada es un estilo que busca crear
programas más claros, legibles y fáciles de mantener. Su idea central es que cualquier programa, sin
importar cuán complejo sea, puede construirse utilizando solo tres tipos de estructuras de control
de flujo :
1. Secuencia: Instrucciones que se ejecutan una tras otra, en orden.
2. Selección (o Condicional): Permite elegir entre diferentes caminos de ejecución basándose en
una condición.
3. Repetición (o Iterativa): Permite ejecutar un bloque de código múltiples veces.
Este enfoque, popularizado por Edsger W. Dijkstra, eliminó la necesidad de la caótica instrucción
goto (saltar a cualquier línea del código), que hacía que los programas fueran muy difíciles de
seguir. Python, por diseño, no tiene goto, lo que fomenta naturalmente un estilo de programación
estructurado .

Refuerzo y Práctica 🧠
● Criterio Clave: Piensa en la programación imperativa como dar una receta de cocina. Cada
paso es una instrucción y el orden es crucial. La programación estructurada nos da los bloques
de construcción (secuencia, selección, repetición) para escribir esa receta de forma clara y sin
enredos.

Autoevaluación ✅
1. Pregunta: En la programación imperativa, el programador especifica "qué hacer", pero el
orden de ejecución es aleatorio.
○ Respuesta: Falso. En la programación imperativa, el programador especifica tanto qué
hacer como en qué orden. El orden es fundamental .
2. Pregunta: La programación estructurada se basa en el uso de tres estructuras de control:
secuencia, selección y repetición.
○ Respuesta: Verdadero. Estas tres estructuras son los pilares de la programación
estructurada y permiten construir cualquier algoritmo de forma clara y ordenada .

4.2. Estructura de Control Secuencial


El Flujo por Defecto: Uno Tras Otro
La Estructura de Control Secuencial es la forma más básica y fundamental de controlar el flujo.
Simplemente significa que las instrucciones se ejecutan una después de la otra, en el mismo orden
en que están escritas en el código, de arriba hacia abajo . No hay saltos ni decisiones.

Python
# Ejemplo de Estructura Secuencial
print("Paso 1: Preparando los datos...")
nombre = input("Ingrese su nombre: ") # Se ejecuta primero

print("Paso 2: Procesando...")
saludo = f"Hola, {nombre}!" # Se ejecuta segundo

print("Paso 3: Mostrando el resultado.")


print(saludo) # Se ejecuta tercero

Aunque es simple, la estructura secuencial es la base sobre la cual se construyen todos los
programas. Incluso los programas más complejos están formados por secuencias de instrucciones
dentro de estructuras más elaboradas.

Refuerzo y Práctica 🧠
● Criterio Clave: El orden lógico es crucial. Si en el ejemplo anterior intentáramos crear el saludo
antes de pedir el nombre, el programa fallaría. Asegúrate de que tus secuencias de pasos
tengan sentido.

Autoevaluación ✅
1. Pregunta: En una estructura secuencial, el orden en que se escriben las instrucciones no afecta
el resultado final.
○ Respuesta: Falso. El orden es fundamental. Las instrucciones se ejecutan exactamente
como están escritas, y cambiar el orden puede alterar completamente el resultado o
causar errores .
2. Pregunta: Una asignación de valor como x = 10 produce automáticamente una salida por
pantalla.
○ Respuesta: Falso. Las asignaciones en Python son "mudas". Para ver el valor de una
variable, es necesario usar explícitamente la función print() .

4.3. Estructuras de Control Condicional (Selectivas)


Dotando de Inteligencia a los Programas: La Toma de Decisiones
Un programa que solo sigue una secuencia de pasos es limitado. El verdadero poder de la
programación surge cuando nuestros programas pueden tomar decisiones y reaccionar de manera
diferente según las circunstancias. Pensemos en un sistema de detección de fraude: debe decidir si
una transacción es normal o sospechosa basándose en ciertas condiciones (monto, ubicación, etc.).
Para lograr esto, utilizamos las Estructuras de Control Condicionales. Estas nos permiten ejecutar
bloques de código solo si una condición lógica es verdadera.

Las Herramientas de Decisión: if, elif y else


En Python, las sentencias condicionales principales son:
1. Sentencia if: Ejecuta un bloque de código solo si una condición es True.
Python
edad = 20
if edad >= 18:
print("Eres mayor de edad.") # Este bloque se ejecuta

2. Sentencia if-else: Proporciona un camino alternativo. Ejecuta un bloque si la condición es True


y otro bloque si es False.
Python
numero = 7
if numero % 2 == 0:
print("El número es PAR.")
else:
print("El número es IMPAR.") # Este bloque se ejecuta

3. Sentencia if-elif-else: Permite encadenar múltiples condiciones. elif es la abreviatura de "else


if". Es la forma limpia y eficiente de evaluar varias posibilidades.
Python
nota = 7.5
if nota >= 9:
print("Sobresaliente")
elif nota >= 7:
print("Notable") # Este bloque se ejecuta
elif nota >= 5:
print("Aprobado")
else:
print("Suspendido")

El Atajo Condicional: El Operador Ternario


Python, siendo un lenguaje que valora la concisión, ofrece una forma más corta de escribir
asignaciones if-else simples. Se conoce como el operador ternario.
Imagina este código estándar:

Python
edad = 20
if edad >= 18:
mensaje = "Es mayor de edad"
else:
mensaje = "Es menor de edad"
print(mensaje)

Con el operador ternario, podemos escribir lo mismo en una sola línea:


Python
edad = 20
mensaje = "Es mayor de edad" if edad >= 18 else "Es menor de edad"
print(mensaje)

La estructura es valor_si_true if condicion else valor_si_false. Es extremadamente útil y legible para


asignaciones condicionales sencillas.

Refuerzo y Práctica 🧠
● Lógica Booleana: La clave de los condicionales son las expresiones que evalúan a True o False.
Practica construirlas usando operadores de comparación (==, >, etc.) y lógicos (and, or, not).
● Indentación: ¡Recuerda! La indentación (4 espacios) que sigue a un if, elif o else es obligatoria.
Define qué código pertenece a ese bloque.

Autoevaluación ✅
1. Pregunta: La sentencia elif puede usarse sin una sentencia if previa.
○ Respuesta: Falso. elif y else siempre deben ser parte de una estructura que comienza con
if .
2. Pregunta: En la expresión edad > 18 and tiene_licencia == True, si edad es 15, Python seguirá
evaluando si tiene_licencia es True.
○ Respuesta: Falso. Python utiliza evaluación con cortocircuito. Para el operador and, si la
primera parte es False, el resultado ya es False, por lo que Python no se molesta en
evaluar la segunda parte. Esto es eficiente y puede prevenir errores .
3. Pregunta: El operador ternario es útil para manejar múltiples condiciones complejas con
muchos elif.
○ Respuesta: Falso. El operador ternario es ideal para asignaciones if-else simples. Para
múltiples condiciones, la estructura if-elif-else es mucho más clara y apropiada.

4.4. Estructuras de Control Iterativas (Repetitivas o Bucles)


Automatizando Tareas: El Poder de la Repetición
Imagina que necesitas procesar un archivo con millones de registros de clientes para calcular el
ingreso total. Sería imposible escribir una línea de código para cada cliente. Aquí es donde brillan
las Estructuras de Control Iterativas, más conocidas como bucles. Los bucles nos permiten ejecutar
un bloque de código repetidamente, automatizando tareas que de otro modo serían tediosas o
imposibles.
Python ofrece dos tipos principales de bucles:
1. Ciclo Condicional (while): Repite un bloque de código mientras una condición sea True. No
sabemos de antemano cuántas veces se ejecutará; depende de cuándo la condición se vuelva
False.
Python
# Contar hacia atrás desde 5
contador = 5
while contador > 0:
print(contador)
contador -= 1 # ¡CRÍTICO! Actualizar la variable de control
print("¡Despegue! 🚀")

Si olvidas actualizar la variable de control (contador en este caso), crearás un bucle infinito, y
tu programa se "colgará".
2. Ciclo Exacto (for): Repite un bloque de código un número de veces conocido de antemano,
iterando sobre los elementos de una secuencia (como una lista, una cadena de texto o un
rango de números).
Python
# Iterar sobre una lista de frutas
frutas = ["manzana", "banana", "cereza"]
for fruta in frutas:
print(f"Me gusta la {fruta}")

# Iterar un número fijo de veces con range()


for i in range(5): # range(5) genera los números 0, 1, 2, 3, 4
print(f"Repetición número {i}")

Patrón Crítico: Cómo Validar la Entrada del Usuario con un Bucle while
Una de las aplicaciones más poderosas y prácticas de los bucles es crear programas robustos que no
se bloqueen por una entrada incorrecta del usuario. Enseñar este patrón es más valioso que solo
enseñar la sintaxis.
El Problema: ¿Qué pasa si pedimos un número y el usuario escribe "hola"? Como vimos en la
Unidad 3, el programa crashea con un ValueError al intentar hacer int("hola").
La Solución (El Patrón de Validación):
Podemos usar un bucle while para insistir hasta que el usuario ingrese datos válidos.

Python
# Patrón de validación de entrada numérica
while True: # 1. Iniciar un bucle que, en principio, es infinito.
entrada_usuario = input("Por favor, ingrese su edad (solo números): ")

if entrada_usuario.isdigit(): # 2. Condición de validación: ¿son todos los caracteres dígitos?


edad = int(entrada_usuario) # 3. Si es válido, convertimos a entero.
break # 4. ¡IMPORTANTE! Rompemos el bucle para salir.
else:
# 5. Si no es válido, mostramos un error y el bucle vuelve a empezar.
print("Error: Entrada no válida. Por favor, ingrese solo dígitos numéricos. Intente de nuevo. 😠")

# El programa solo llega aquí cuando el bucle se ha roto


print(f"¡Excelente! Tu edad es {edad} años. 👍")

Este patrón combina un bucle while, un condicional if-else y la instrucción break para crear una
experiencia de usuario a prueba de errores. ¡Es una herramienta fundamental en tu arsenal de
programación!
¿Cuándo Usar for y Cuándo Usar while?
Esta es una de las dudas más comunes para los principiantes. Aquí tienes una guía rápida:

Situación Bucle Recomendado Razón Ejemplo de Código

Iterar un número for con range() El número de for i in range(10):


conocido de veces repeticiones es fijo y print(i)
claro.

Recorrer los elementos for Diseñado para iterar for fruta in mis_frutas:
de una colección sobre secuencias (listas, print(fruta)
cadenas) de forma
natural.

Repetir hasta que una while El número de while stock > 0:


condición cambie iteraciones es vender_producto()
desconocido y depende
de un estado que puede
cambiar.

Validar la entrada del while Se debe repetir la while not


usuario petición hasta que la entrada_valida: entrada
entrada sea válida; no = input(...) [28, 29]
se sabe cuántos
intentos tomará.

Autoevaluación ✅
1. Pregunta: Un bucle while es el más adecuado cuando se sabe de antemano el número exacto
de veces que se desea repetir un bloque de código.
○ Respuesta: Falso. Para un número exacto de iteraciones, el bucle for con range() es más
adecuado y claro. El bucle while se usa cuando la repetición depende de una condición .
2. Pregunta: Si la condición de un bucle while nunca se actualiza dentro del bucle para volverse
False, el programa se ejecutará una vez y luego se detendrá.
○ Respuesta: Falso. Si la condición nunca se vuelve False, se creará un bucle infinito y el
programa se quedará "colgado", consumiendo recursos indefinidamente .
3. Pregunta: La instrucción break dentro de un bucle hace que el programa salte a la siguiente
iteración, ignorando el resto del código de la iteración actual.
○ Respuesta: Falso. break termina completamente la ejecución del bucle. La instrucción que
salta a la siguiente iteración es continue .
Unidad 5: Modularización y Funciones: Construyendo Código Limpio y
Reutilizable
En esta unidad, exploraremos cómo organizar nuestro código de manera eficiente, promoviendo la
reutilización y mejorando la legibilidad y el mantenimiento de nuestros programas. Abordaremos el
diseño de soluciones estructuradas mediante funciones y la gestión de código a través de módulos
y paquetes en Python.

5.1. El Principio de "Divide y Vencerás" ♟️: La Esencia de la Modularización


Imaginemos un escenario común en el desarrollo de modelos de Inteligencia Artificial. Supongamos
que estamos construyendo un sistema de recomendación que requiere varias etapas: la recolección
de datos de usuarios, la limpieza y preprocesamiento, el entrenamiento de un modelo y la
visualización de las recomendaciones. A medida que el proyecto crece, cada etapa puede implicar
cientos de líneas de código. ¿Cómo podemos manejar esta complejidad para que el sistema sea
comprensible, fácil de depurar y adaptable a futuros cambios?.
El desarrollo de software no es solo escribir código; es, ante todo, pensar en soluciones. Aquí es
donde entra en juego la modularización: la práctica de dividir un problema grande en partes
funcionalmente independientes, llamadas módulos. Es como hacer los planos antes de construir
una casa: nos proporciona claridad, previene errores y nos ahorra tiempo. Aunque sus beneficios se
magnifican en proyectos grandes, la modularización es una práctica fundamental que fomenta la
claridad mental desde el principio, incluso en scripts pequeños. Fomenta la legibilidad, la
organización y la facilidad de mantenimiento sin importar el tamaño del programa.
Como afirmó el célebre científico de la computación Edsger Dijkstra, "el arte de la programación es
el arte de organizar la complejidad". La complejidad en el software no crece de forma lineal;
aumenta exponencialmente con cada nueva interdependencia. La modularización es nuestra
principal herramienta para mantener esa complejidad bajo control, permitiéndonos aplicar el
clásico principio de "divide y vencerás" para construir programas basados en la claridad, la
estructura y la eficiencia.

5.2. Funciones: Los Ladrillos Fundamentales del Código 🧱


Si la modularización es el "porqué" (la estrategia de diseño), las funciones son el "cómo" (la
implementación táctica). Son los bloques de construcción básicos que nos permiten encapsular una
tarea específica para reutilizarla, evitando la duplicación de código y simplificando el
mantenimiento.

Sintaxis y Componentes Clave


Una función puede ser vista como una "mini-fábrica" 🏭. Recibe materias primas (parámetros),
realiza un proceso específico en su interior y produce un resultado (valor de retorno).
En Python, una función se define con la palabra clave def, seguida de un nombre descriptivo,
paréntesis () que pueden contener los parámetros, y dos puntos :. El cuerpo de la función debe
estar indentado.1
Python
# Formato de código de programación
# 'def' define la función, 'sumar' es el nombre.
# 'a' y 'b' son los parámetros (la materia prima).
def sumar(a, b):
# El cuerpo de la función realiza el proceso.
resultado = a + b
# 'return' especifica el producto final.
return resultado

Para usar la "fábrica", la "invocamos" o "llamamos" por su nombre, pasándole los valores
concretos, llamados argumentos.
Python
# Formato de código de programación
# "5" y "3" son los argumentos.
total = sumar(5, 3)
print(total) # Output: 8

Es importante distinguir entre una función, que devuelve un valor útil usando return, y un
procedimiento, que realiza una acción (como imprimir algo en pantalla) pero no devuelve un valor
explícito. Si una función no tiene una sentencia return, Python devuelve implícitamente un valor
especial llamado None.

El Ámbito de las Variables: ¿Dónde Vive mi Variable? (Local vs. Global)


Este es uno de los conceptos más cruciales y una fuente común de errores para principiantes. 3 El
ámbito o
scope de una variable determina desde dónde se puede acceder a ella. Podemos imaginar los
ámbitos como "cajas de visibilidad" 📦.
● Variables Locales: Se crean dentro de una función y solo existen en la "caja" de esa función.
Cuando la función termina, la variable y su valor desaparecen.1 La asignación de un valor a una
variable dentro de una función (
=) la convierte en local por defecto.
● Variables Globales: Se definen fuera de cualquier función, en el nivel principal del script. Son
accesibles desde cualquier "caja" del programa.

Python
# Formato de código de programación
variable_global = "Soy visible en todas partes" # Vive en la caja global.

def mi_funcion():
variable_local = "Solo existo aquí dentro" # Vive en la caja de la función.
print(variable_global) # Puede ver hacia afuera, a la caja global.
print(variable_local)

mi_funcion()
# print(variable_local) # Esto daría un error (NameError), porque la caja local ya no existe.

Un error frecuente es el "sombreado" (shadowing). Si una variable local tiene el mismo nombre que
una global, dentro de la función, la local tendrá prioridad, "ocultando" a la global. Python sigue la
regla LEGB para buscar variables: Local, Enclosing (en funciones anidadas), Global, Built-in
(funciones propias de Python como print).

Paso de Argumentos en Python: Una Mirada Profunda a la Mutabilidad


A menudo se dice que Python pasa los argumentos "por valor" para unos tipos y "por referencia"
para otros. Esta simplificación es técnicamente incorrecta y puede llevar a confusión. Python utiliza
un mecanismo llamado "paso por asignación" o "paso por referencia de objeto". La clave para
entender su comportamiento no es la terminología, sino la mutabilidad del objeto que se pasa.
Pensemos en las variables como etiquetas 🏷️ que apuntan a cajas 📦 (objetos en memoria).
● Tipos Inmutables (números, strings, tuplas): Estos objetos no pueden ser cambiados in-place.
Cuando pasas un número a una función, la función recibe una copia de la etiqueta, y ambas
apuntan a la misma caja. Si dentro de la función modificas el valor (ej. num += 10), lo que
realmente sucede es que la etiqueta de la función se despega de la caja original y se pega a
una caja nueva con el nuevo valor. La etiqueta de fuera de la función sigue apuntando a la caja
original, que permanece intacta.
Python
# Formato de código de programación
def modificar_numero(num):
# 'num' ahora apunta a una nueva caja con el valor 15.
num += 10
print(f"Dentro de la función: {num}") # Output: 15

mi_numero = 5
modificar_numero(mi_numero)
print(f"Fuera de la función: {mi_numero}") # Output: 5 (la caja original no cambió)

● Tipos Mutables (listas, diccionarios): Estos objetos sí pueden ser cambiados in-place. Cuando
pasas una lista a una función, la función opera directamente sobre la caja original a través de
su propia etiqueta. Métodos como .append() modifican el contenido de esa caja, por lo que el
cambio es visible desde cualquier etiqueta que apunte a ella.1
Python
# Formato de código de programación
def modificar_lista(lista):
#.append() modifica la caja original.
lista.append(4)
print(f"Dentro de la función: {lista}") # Output:

mi_lista =
modificar_lista(mi_lista)
print(f"Fuera de la función: {mi_lista}") # Output: (la caja original cambió)

Este enfoque unifica el comportamiento y lo vincula directamente a la propiedad de mutabilidad,


que es un concepto central en Python.6

Técnicas Adicionales: Argumentos por Defecto, por Palabra Clave y Recursión


● Argumentos por Omisión: Permiten definir valores predeterminados para los parámetros,
haciendo las funciones más flexibles.
Python
# Formato de código de programación
def saludar(nombre, saludo="Hola"):
print(f"{saludo}, {nombre}!")

saludar("Ana") # Output: Hola, Ana!


saludar("Pedro", "Buenos días") # Output: Buenos días, Pedro!
¡Pro-tip! ⚠️ Nunca uses un tipo mutable (como una lista o diccionario) como valor por defecto
(def mi_funcion(lista=):). Este objeto se crea una sola vez y se comparte entre todas las
llamadas a la función, lo que puede causar resultados inesperados y difíciles de depurar.
● Argumentos por Palabra Clave: Permiten pasar argumentos especificando su nombre, lo que
mejora la legibilidad y elimina la necesidad de recordar el orden posicional.

Python
# Formato de código de programación
def crear_perfil(nombre, edad, ciudad):
print(f"Nombre: {nombre}, Edad: {edad}, Ciudad: {ciudad}")

crear_perfil(edad=30, ciudad="Madrid", nombre="Laura")

● Recursión: Es una técnica donde una función se llama a sí misma para resolver un problema,
dividiéndolo en subproblemas más pequeños del mismo tipo. Es crucial definir un caso base
que detenga las llamadas, de lo contrario, el programa entrará en un bucle infinito y lanzará un
error RecursionError.

Python
# Formato de código de programación
def factorial(n):
if n == 0: # Caso base
return 1
else:
return n * factorial(n - 1) # Llamada recursiva

5.3. Módulos y Paquetes: Organizando tu Código a Gran Escala


A medida que los proyectos crecen, mantener todo el código en un solo archivo se vuelve
insostenible. Para organizar el código y promover la reutilización a gran escala, Python utiliza
módulos y paquetes.
● Módulos: Un módulo es simplemente un archivo con extensión .py que contiene código
Python (funciones, clases, variables). La idea es agrupar elementos relacionados en un solo
archivo. Para usar las definiciones de un módulo, debes importarlo usando la sentencia import.
Python
# Formato de código de programación
# Suponiendo que tenemos un archivo llamado mi_modulo.py
# import mi_modulo
# resultado = mi_modulo.sumar(10, 5)

# O podemos importar solo una función específica


from math import sqrt
raiz = sqrt(25) # No necesitamos el prefijo 'math.'

● Paquetes: Son una forma de estructurar módulos relacionados en una jerarquía de directorios.
Un directorio se convierte en un paquete si contiene un archivo especial llamado __init__.py
(puede estar vacío). Esto permite organizar proyectos complejos de manera lógica y
comprensible.
5.4. Buenas Prácticas y Aplicaciones en Ciencia de Datos 🚀
Las funciones son la columna vertebral de cualquier proyecto de Ciencia de Datos. Para escribir
código profesional, sigue estas prácticas:
● Nombres Descriptivos: Usa nombres de funciones claros y en snake_case (minúsculas con
guiones bajos) que describan su propósito. Por ejemplo, calcular_precision_modelo() es
mucho mejor que calc().7
● Una Sola Responsabilidad (SRP): Cada función debe hacer una sola cosa y hacerla bien. Una
función que carga datos, los limpia y entrena un modelo es difícil de mantener y reutilizar. Es
mejor tener tres funciones separadas: cargar_datos(), limpiar_datos() y entrenar_modelo().
● Documentación (Docstrings): El mejor comentario es un código tan claro que no necesita
comentarios. Sin embargo, para funciones complejas, usa docstrings (cadenas de
documentación entre triples comillas) para explicar qué hace la función, qué parámetros
recibe y qué devuelve.
Ejemplos en Data Science:
● Preprocesamiento: def normalizar_columna(dataframe, nombre_columna):
● Evaluación de Modelos: def calcular_error_cuadratico_medio(y_real, y_prediccion):
● Visualización: def generar_histograma_distribucion(datos, titulo_grafico):

5.5. Glosario y Autoevaluación de la Unidad


● Algoritmo: Un conjunto finito de instrucciones o pasos bien definidos, precisos, sin
ambigüedad y efectivos, para resolver un problema o realizar una tarea.
● Argumento: El valor real o los valores que se pasan a una función cuando esta es invocada.
● def: Palabra clave de Python utilizada para definir una nueva función.
● Docstring: Cadenas de documentación multilínea en Python que se utilizan para describir el
propósito de módulos, clases y funciones, y son accesibles mediante la función help().
● Función: Un bloque de código reutilizable que realiza una tarea específica. Puede aceptar
parámetros y devolver un valor. Contribuye a la modularidad y legibilidad del código.
● Modularización: Técnica de diseño de software que consiste en dividir un problema grande
en partes funcionalmente independientes llamadas módulos, que encapsulan operaciones y
datos específicos.
● Módulo: Un archivo .py que contiene código Python (funciones, clases, variables) que puede
ser importado y reutilizado en otros programas.
● Paquete: Un directorio que contiene módulos y un archivo especial __init__.py, utilizado
para organizar lógicamente el código en aplicaciones más grandes y complejas.
● Parámetro: Un nombre de variable definido en la cabecera de una función que actúa como
un marcador de posición para los valores que se le pasarán a la función cuando sea
invocada.
● Paso por Referencia/Valor: Conceptos relacionados con cómo se manejan los argumentos
en la llamada a función, afectando la modificación de los valores originales. En Python, los
objetos mutables se pasan por referencia y los inmutables por valor.
● Procedimiento: Una función que no devuelve explícitamente un valor (retorna None por
defecto).
● Recursión: Una técnica de programación donde una función se llama a sí misma para
resolver un problema, descomponiéndolo en subproblemas más pequeños.
● return: Sentencia de Python utilizada en funciones para devolver un valor o conjunto de
valores al punto de la llamada.
● Variables Globales: Variables definidas fuera de cualquier función, cuyo alcance es todo el
programa.
● Variables Locales: Variables definidas dentro de una función, cuyo alcance se limita al
cuerpo de esa función.
● if __name__ == "__main__":: Bloque de código que se ejecuta solo cuando el script es el
programa principal, no cuando es importado como módulo.
Autoevaluación
Responde a las siguientes preguntas para evaluar tu comprensión de la Unidad 5. Las respuestas y
sus justificaciones se encuentran al final de la autoevaluación.
Cuestionario de Verdadero/Falso
1. V/F: El propósito principal de la modularización es aumentar la complejidad del código para
proyectos grandes.
2. V/F: En Python, una función se define usando la palabra clave function.
3. V/F: Una variable definida dentro de una función de Python es, por defecto, accesible desde
cualquier parte del programa.
4. V/F: Los argumentos por omisión en las funciones permiten que un parámetro tenga un
valor predefinido si no se le proporciona uno durante la llamada.
5. V/F: Un paquete Python se identifica como tal porque contiene al menos un archivo .py y
una carpeta lib/.
Preguntas de Opción Múltiple
1. ¿Cuál de las siguientes afirmaciones describe mejor el beneficio de la reutilización de código
en la programación modular? a) Hace que los programas sean más lentos. b) Permite
escribir la misma lógica en múltiples lugares. c) Evita la duplicación de código y simplifica
futuras correcciones. d) Reduce la legibilidad del programa.
2. ¿Qué tipo de dato Python se comporta de manera que, cuando se pasa como argumento a
una función, las modificaciones hechas dentro de la función afectan al objeto original? a) int
(entero) b) str (cadena) c) tuple (tupla) d) list (lista)
3. ¿Qué opción describe correctamente un módulo en Python? a) Un programa que solo puede
ejecutarse en la línea de comandos. b) Un directorio que contiene solo funciones. c) Un
archivo .py que agrupa código relacionado (funciones, clases, variables). d) Un tipo de dato
primitivo.
4. Si una función recursiva no tiene un caso base bien definido, ¿qué es probable que ocurra?
a) La función siempre retornará None. b) El programa entrará en un bucle infinito. c) La
función se convertirá automáticamente en un procedimiento. d) La función no podrá
aceptar argumentos.
Completar la Frase
1. La práctica de dividir un problema en partes más pequeñas y funcionales se conoce como
__________.
2. La palabra clave __________ se utiliza para que una función devuelva un valor en Python.
3. El archivo especial __________ dentro de un directorio indica que ese directorio es un
paquete de Python.

Clave de Respuestas de la Autoevaluación


Cuestionario de Verdadero/Falso
1. Falso. La modularización busca reducir y organizar la complejidad, no aumentarla, haciendo
el código más manejable y comprensible.
2. Falso. En Python, las funciones se definen usando la palabra clave def, no function.
3. Falso. Las variables definidas dentro de una función son por defecto locales a esa función y
su alcance se limita a su cuerpo. Solo pueden ser globales si se declaran explícitamente con
global.
4. Verdadero. Los argumentos por omisión (o valores predeterminados) permiten que un
parámetro tenga un valor asignado por defecto, que se usa si no se proporciona un
argumento para ese parámetro durante la llamada a la función.
5. Falso. Un paquete Python es un directorio que contiene módulos y, de forma crucial, un
archivo __init__.py (puede estar vacío) para ser reconocido como paquete.
Preguntas de Opción Múltiple
1. c) Evita la duplicación de código y simplifica futuras correcciones.
○ Justificación: La reutilización del código es un beneficio clave de la modularización,
que permite emplear las mismas funciones o módulos en diferentes lugares o
proyectos, lo que ahorra esfuerzo, evita escribir la misma lógica y facilita las
modificaciones.
2. d) list (lista)
○ Justificación: En Python, los objetos mutables como las listas se pasan por referencia
(o más precisamente, por "asignación de objeto"), lo que significa que las
modificaciones realizadas dentro de la función afectan al objeto original fuera de
ella. Los int, str y tuple son inmutables y se comportan como si fueran "pasados por
valor".
3. c) Un archivo .py que agrupa código relacionado (funciones, clases, variables).
○ Justificación: Un módulo es una unidad de organización de código en Python, que se
corresponde con un archivo .py que contiene definiciones de funciones, clases y
variables.
4. b) El programa entrará en un bucle infinito.
○ Justificación: Una función recursiva necesita un caso base para terminar su
ejecución. Sin un caso base, la función se llamará a sí misma indefinidamente, lo que
resultará en un bucle infinito y eventualmente en un error de desbordamiento de
pila (RecursionError en Python).
Completar la Frase
1. La práctica de dividir un problema en partes más pequeñas y funcionales se conoce como
modularización.
2. La palabra clave **return** se utiliza para que una función devuelva un valor en Python.
3. El archivo especial **__init__.py** dentro de un directorio indica que ese directorio es un
paquete de Python.
Unidad 6: Estructuras de Datos: La Columna Vertebral de tus Programas

Esta unidad se centra no sólo en qué es cada estructura de datos, sino en ayudarte a decidir cuál
usar en un escenario real de Ciencia de Datos. La elección correcta de la estructura de datos puede
tener un impacto masivo en la eficiencia y la legibilidad de tu código.

6.1. Listas [ ]: Colecciones Flexibles y Mutables 🗂️


Una lista es una colección ordenada y mutable de elementos. "Ordenada" significa que los
elementos mantienen su posición, y "mutable" significa que puedes cambiar su contenido (añadir,
eliminar o modificar elementos) después de su creación.
Son el caballo de batalla de las colecciones en Python por su flexibilidad. Se crean con corchetes ``.
Python
# Formato de código de programación
# Lista de mediciones de un sensor
temperaturas = [22.5, 23.1, 22.9, 24.0]

# Acceso por índice (empieza en 0)


print(temperaturas) # Output: 23.1

# Modificación
temperaturas.append(25.1) # Añade al final
temperaturas = 22.4 # Cambia un elemento
print(temperaturas) # Output: [22.4, 23.1, 22.9, 24.0, 25.1]

¡Advertencia sobre errores comunes! ⚠️


1. Modificar mientras se itera: Nunca elimines elementos de una lista mientras la recorres con
un bucle for. Esto puede causar que se salten elementos y conducir a errores difíciles de
rastrear.3 La solución correcta es iterar sobre una copia o crear una nueva lista.
Python
# Formato de código de programación
# INCORRECTO
numeros =
for n in numeros:
if n % 2 == 0:
numeros.remove(n) # ¡Peligroso!

# CORRECTO
numeros =
impares = [n for n in numeros if n % 2!= 0]
print(impares) # Output:

2. Aliasing vs. Copia: Recuerda que lista_b = lista_a no crea una nueva lista. Ambas variables
apuntan a la misma "caja" en memoria. Para crear una copia independiente, usa lista_b =
lista_a.copy() o lista_b = lista_a[:].3

6.2. Tuplas ( ): Registros de Datos Inmutables y Seguros 🔒


Una tupla es una colección ordenada e inmutable de elementos. "Inmutable" es su característica
clave: una vez creada, no puedes cambiarla. Se crean con paréntesis ().
Python
# Formato de código de programación
# Coordenadas geográficas que no deben cambiar
coordenadas_gps = (40.4168, -3.7038)

# Desempaquetado de tuplas
latitud, longitud = coordenadas_gps
print(f"Latitud: {latitud}") # Output: Latitud: 40.4168

Pero, ¿por qué necesitamos tuplas si ya tenemos listas? No son solo "listas que no se pueden
cambiar". Su inmutabilidad les confiere una propiedad fundamental: son "hasheables". Esto
significa que se puede calcular un valor numérico único (un hash) a partir de su contenido, y este
valor nunca cambiará.
Esta propiedad es la que permite que las tuplas se usen como claves de diccionario, algo que las
listas no pueden hacer. Un diccionario necesita que sus claves sean inmutables para garantizar un
acceso ultra-rápido a los valores. Si la clave pudiera cambiar, su hash también lo haría, y el
diccionario "perdería" el valor asociado. Sería como cambiar el número de una casa después de
haberla registrado en el catastro; el mapa ya no serviría para encontrarla. Las tuplas, al ser
inmutables, son candidatas perfectas para claves compuestas.

6.3. Diccionarios { }: Acceso Eficiente por Clave-Valor 🔑


Un diccionario es una colección no ordenada (históricamente) de pares clave-valor. Cada clave
debe ser única e inmutable (como un string, un número o una tupla), y se usa para acceder a su
valor asociado de forma extremadamente rápida. Se crean con llaves {}.
Python
# Formato de código de programación
# Perfil de un usuario
perfil_usuario = {
"id_usuario": 101,
"nombre": "Ana",
"email": "[email protected]",
"preferencias": ["ciencia ficción", "misterio"]
}

# Acceso por clave


print(perfil_usuario["nombre"]) # Output: Ana

# Añadir o modificar
perfil_usuario["edad"] = 34 # Añade un nuevo par
perfil_usuario["email"] = "[email protected]" # Modifica un valor existente

¡Importante! A partir de Python 3.7, los diccionarios garantizan el orden de inserción de las claves.
En versiones anteriores, no se podía confiar en el orden en que aparecían los elementos. Esto es un
cambio significativo que hace los diccionarios aún más útiles.

6.4. Cadenas de Texto "": Manipulación Experta de Texto ✍️


Una cadena (str) es una secuencia inmutable de caracteres. Al igual que las tuplas, una vez creada,
no se puede modificar in-place. Cualquier método que parezca modificarla (como .upper() o
.replace()) en realidad devuelve una nueva cadena con el cambio aplicado.
Python
# Formato de código de programación
texto_sucio = " Columna Uno "
# Limpieza de datos típica en Data Science
nombre_columna_normalizado = texto_sucio.lower().strip().replace(" ", "_")
print(nombre_columna_normalizado) # Output: columna_uno

Python ofrece una gran cantidad de métodos para manipular cadenas, como .split() para dividir un
texto en una lista de palabras (tokenización) y .join() para unir una lista de cadenas en un solo
texto.

6.5. Tabla Comparativa: ¿Qué Estructura de Datos Debo Usar?


Para ayudarte a tomar decisiones informadas, aquí tienes una tabla que resume y contrasta las
características clave de cada estructura de datos. Esta herramienta transforma el conocimiento de
"qué es" en "cuándo usarlo".6
Característica Lista (list) Tupla (tuple) Diccionario (dict)

Sintaxis [1, 'a', 2.0] (1, 'a', 2.0) {'clave1': 1, 'clave2': 'a'}

Orden Ordenada (mantiene el Ordenada (mantiene el Ordenada (desde


orden de inserción) orden de inserción) Python 3.7)

Mutabilidad Mutable (se puede Inmutable (no se puede Mutable (se puede
cambiar) cambiar) cambiar)

Indexación Por posición numérica Por posición numérica Por clave (ej.
(ej. lista) (ej. tupla) diccionario['clave'])

Uso como Clave de No (porque es mutable) Sí (porque es No aplicable (las claves


Diccionario inmutable) son parte de él)

Rendimiento Típico Eficiente para Ligeramente más rápida Extremadamente


añadir/eliminar al final y consume menos rápido para búsqueda,
memoria que las listas inserción y eliminación
por clave

Caso de Uso Principal Almacenar secuencias Proteger datos que no Mapear identificadores
en Data Science que necesitan ser deben cambiar (ej. únicos a datos (ej.
modificadas (ej. series coordenadas, claves perfiles de usuario,
temporales, resultados compuestas para configuraciones de
de un bucle). diccionarios). modelos, conteo de
frecuencias).

6.6. Autoevaluación y Glosario de la Unidad


Glosario de Términos Clave de la Unidad 6
● Lista (list): Colección ordenada y mutable de elementos en Python. Permite almacenar
elementos de cualquier tipo y modificarlos después de su creación.
● Mutable: Característica de un objeto que puede ser modificado después de su creación. Las
listas son mutables.
● Tupla (tuple): Colección ordenada e inmutable de elementos en Python. Sus elementos no
pueden ser cambiados, añadidos o eliminados una vez creada.
● Inmutable: Característica de un objeto que no puede ser modificado después de su
creación. Las cadenas y tuplas son inmutables.
● Diccionario (dict): Colección de pares clave-valor donde cada clave es única y se usa para
acceder a su valor asociado. Las claves deben ser inmutables.
● Clave-valor: Par de elementos en un diccionario donde la "clave" (única e inmutable) sirve
como identificador para acceder al "valor" asociado (que puede ser de cualquier tipo y
mutable).
● Cadena (str): Secuencia inmutable de caracteres de texto.
● Indexación: Proceso de acceder a un elemento individual en una secuencia (lista, tupla,
cadena) utilizando su posición numérica (índice), que comienza en 0. Se pueden usar índices
positivos o negativos.
● Slicing (Corte/Rebanado): Operación para extraer una porción o sub-secuencia de una lista,
tupla o cadena utilizando el operador : (ej., [inicio:fin:paso]).
● Matriz: Representación bidimensional de datos, comúnmente implementada en Python
como una lista de listas (listas anidadas), ya que no es un tipo de dato nativo específico.
● append(): Método de listas para añadir un elemento al final.
● insert(): Método de listas para añadir un elemento en una posición específica.
● pop(): Método de listas (y diccionarios) para eliminar un elemento por índice (o el último en
listas) y devolverlo.
● remove(): Método de listas para eliminar la primera ocurrencia de un valor específico.
● sort(): Método de listas para ordenar los elementos in-place (modifica la lista original).
● sorted(): Función incorporada de Python que devuelve una nueva lista ordenada de los
elementos de un iterable, sin modificar el original.
● Desempaquetado (Tuplas): Asignación de los elementos de una tupla a múltiples variables
en una sola sentencia.
● keys(): Método de diccionarios que devuelve una vista de las claves del diccionario.
● values(): Método de diccionarios que devuelve una vista de los valores del diccionario.
● items(): Método de diccionarios que devuelve una vista de los pares clave-valor como
tuplas.
● upper(): Método de cadenas que devuelve una nueva cadena con todos los caracteres en
mayúsculas.
● lower(): Método de cadenas que devuelve una nueva cadena con todos los caracteres en
minúsculas.
● strip(): Método de cadenas que devuelve una nueva cadena con los espacios en blanco (o
caracteres especificados) eliminados del principio y el final.
● split(): Método de cadenas que divide la cadena en una lista de subcadenas usando un
delimitador.
● join(): Método de cadenas que une los elementos de un iterable (generalmente una lista de
cadenas) en una sola cadena, usando la cadena sobre la que se llama como separador.
● replace(): Método de cadenas que devuelve una nueva cadena con todas las ocurrencias de
una subcadena reemplazadas por otra.

Autoevaluación de la Unidad 6
Elija la respuesta correcta, marque Verdadero/Falso o complete la frase, y justifique su elección.
Cuestionario V/F, Multiple Choice o Completar la frase
1. Pregunta: Una lista en Python se define como una colección ________ y ________ de
elementos.
○ a) desordenada y mutable
○ b) ordenada y mutable
○ c) desordenada e inmutable
○ d) ordenada e inmutable
2. Pregunta: Verdadero o Falso: Si tienen una lista A = y luego ejecutan B = A, modificar B =
100 hará que A también cambie.
○ Verdadero / Falso
○ Justificación:
3. Pregunta: Una tupla es la estructura de datos ideal para almacenar:
○ a) Una colección de datos que serán modificados frecuentemente.
○ b) Una secuencia de elementos que debe permanecer inalterada después de su
creación.
○ c) Datos a los que se accede mediante claves únicas.
○ d) Grandes volúmenes de texto no estructurado.
4. Pregunta: Verdadero o Falso: Para crear una tupla con un solo elemento entero, por
ejemplo, el número 5, se debe escribir mi_tupla = (5).
○ Verdadero / Falso
○ Justificación:
5. Pregunta: En un diccionario, el acceso a los valores se realiza a través de:
○ a) Índices numéricos secuenciales.
○ b) Sus posiciones en la memoria.
○ c) Claves únicas.
○ d) Métodos de búsqueda binaria.
6. Pregunta: Las claves de un diccionario deben ser:
○ a) Cualquier tipo de dato.
○ b) Solo números enteros.
○ c) Cualquier tipo de dato mutable.
○ d) Tipos de datos inmutables (ej., cadenas, números, tuplas).
7. Pregunta: Verdadero o Falso: El método my_string.lower() modifica la cadena my_string
original para convertirla a minúsculas.
○ Verdadero / Falso
○ Justificación:
8. Pregunta: Para dividir una cadena de texto en una lista de palabras, se utiliza el método:
○ a) join()
○ b) split()
○ c) strip()
○ d) replace()
9. Pregunta: La representación de matrices en Python se logra a través de:
○ a) Un tipo de dato matrix nativo.
○ b) Listas anidadas (listas de listas).
○ c) Tuplas de tuplas, que son más eficientes.
○ d) Diccionarios con claves numéricas.
10. Pregunta: Verdadero o Falso: Los diccionarios en Python siempre mantienen el orden de
inserción de las claves, sin importar la versión de Python.
○ Verdadero / Falso
○ Justificación:

Clave de Respuestas del Cuestionario


1. Respuesta: b) ordenada y mutable. Las listas son colecciones que preservan el orden de los
elementos y permiten su modificación.
2. Respuesta: Verdadero.
○ Justificación: Cuando se ejecuta B = A, B no crea una copia independiente de la lista,
sino que ambas variables A y B pasan a referenciar (apuntar a) la misma lista en la
memoria. Por lo tanto, cualquier cambio realizado a través de B se reflejará también
en A.
3. Respuesta: b) Una secuencia de elementos que debe permanecer inalterada después de su
creación.. Las tuplas son inmutables y, por lo tanto, garantizan que sus elementos no serán
modificados una vez definidas.
4. Respuesta: Falso.
○ Justificación: Si se escribe mi_tupla = (5), Python lo interpreta como una expresión
numérica entre paréntesis, resultando en un entero. Para definir una tupla con un
solo elemento, es necesario añadir una coma al final: mi_tupla = (5,).
5. Respuesta: c) Claves únicas.. Los diccionarios son colecciones de pares clave-valor, y se
accede a los valores utilizando sus respectivas claves.
6. Respuesta: d) Tipos de datos inmutables (ej., cadenas, números, tuplas).. Las claves de un
diccionario deben ser inmutables para que el diccionario pueda garantizar un acceso
eficiente y consistente a sus valores.
7. Respuesta: Falso.. Las cadenas en Python son inmutables. El método lower() devuelve una
nueva cadena con los caracteres en minúsculas, mientras que la cadena original (my_string)
no se modifica.
8. Respuesta: b) split().. El método split() es fundamental para la tokenización de texto,
dividiendo una cadena en una lista de subcadenas (palabras) basándose en un delimitador
(por defecto, espacios en blanco).
9. Respuesta: b) Listas anidadas (listas de listas).. Python no tiene un tipo de dato nativo
específico para matrices, por lo que se implementan comúnmente usando listas que
contienen otras listas.
10. Respuesta: Falso.. Los diccionarios en Python no siempre mantuvieron el orden de
inserción. Esta característica fue introducida y garantizada a partir de Python 3.7. En
versiones anteriores a la 3.7, el orden de los elementos en un diccionario no estaba
garantizado.
Unidad 7: Resiliencia y Persistencia: Manejo de Errores y Archivos

En esta unidad, damos un salto cualitativo: pasamos de escribir código que "funciona en el caso
feliz" a escribir código robusto que sobrevive y se comporta de manera predecible en el mundo
real, donde las cosas a menudo salen mal. Aprenderemos a manejar errores y a hacer que nuestros
datos perduren en el tiempo.

7.1. Manejo de Excepciones: Preparando tu Código para lo Inesperado 🛡️


Es crucial diferenciar entre dos tipos de problemas:
1. Errores de Sintaxis: Son errores en la "gramática" de tu código (ej. olvidar los dos puntos : en
un if). El programa ni siquiera llegará a ejecutarse.9
2. Excepciones: Son errores que ocurren durante la ejecución del programa, aunque la sintaxis
sea correcta (ej. intentar dividir por cero o abrir un archivo que no existe). Estos son los que
podemos y debemos manejar.9
El mecanismo para manejar excepciones es el bloque try...except.
Python
# Formato de código de programación
try:
# 1. Intenta ejecutar este código.
numero = int(input("Introduce un número: "))
resultado = 10 / numero
except ValueError:
# 2. Si ocurre un ValueError (ej. el usuario introduce "hola"), ejecuta esto.
print("Error: Debes introducir un número válido.")
except ZeroDivisionError:
# 3. Si ocurre un ZeroDivisionError, ejecuta esto.
print("Error: No se puede dividir por cero.")
except Exception as e:
# 4. (Opcional) Captura cualquier otra excepción no prevista.
print(f"Ha ocurrido un error inesperado: {e}")

¡El anti-patrón más peligroso! 🚨 Usar un except: o except Exception: genérico para todo es una
muy mala práctica.3 Hacerlo "silencia" los errores y oculta bugs. Imagina una alarma de incendios
que solo dice "¡Algo va mal!" sin especificar si es un incendio, una fuga de gas o un ladrón. No
sabrías cómo reaccionar. De la misma manera, debes capturar excepciones específicas para dar
respuestas precisas y solo usar una genérica al final para registrar un fallo totalmente inesperado.
El flujo completo puede incluir dos bloques opcionales más:
● else: Se ejecuta solo si no hubo ninguna excepción en el bloque try. Es perfecto para el código
que debe correr solo si la operación principal fue exitosa.
● finally: Se ejecuta siempre, sin importar si hubo una excepción o no. Es ideal para tareas de
limpieza, como cerrar recursos.

7.2. Archivos: Haciendo que tus Datos Perduren en el Tiempo 💾


Las variables viven en la memoria RAM, que es volátil; cuando el programa termina, los datos se
pierden. Para que la información persista, debemos guardarla en archivos. El protocolo básico es
abrir, leer/escribir y cerrar.
Sin embargo, el método manual file = open(...) seguido de file.close() es frágil. Si ocurre un error
entre la apertura y el cierre, el archivo podría no cerrarse nunca, llevando a la posible pérdida o
corrupción de datos.3
Por esta razón, la forma canónica y correcta de trabajar con archivos en Python es usando el gestor
de contexto with. No es una opción, es el estándar profesional que garantiza que el archivo se
cerrará automáticamente al salir del bloque, incluso si ocurre un error.

Python
# Formato de código de programación

# --- ESCRITURA EN UN ARCHIVO ---


# El modo 'w' (write) crea el archivo o sobrescribe el existente.
try:
with open("reporte.txt", "w") as f:
f.write("Resultados del análisis.\n")
f.write("Modelo A: 95% de precisión.\n")
# El archivo se cierra automáticamente aquí.
except IOError as e:
print(f"No se pudo escribir en el archivo: {e}")

# --- LECTURA DE UN ARCHIVO ---


# El modo 'r' (read) es para leer.
try:
with open("reporte.txt", "r") as f:
# La forma más eficiente de leer es línea por línea.
for linea in f:
print(linea.strip()) #.strip() elimina saltos de línea y espacios extra.
except FileNotFoundError:
print("El archivo 'reporte.txt' no se encontró.")
except Exception as e:
print(f"Ocurrió un error al leer: {e}")

Los modos de apertura más comunes son:


● 'r': Read (Lectura). Da error si el archivo no existe.
● 'w': Write (Escritura). Sobrescribe el archivo si existe, lo crea si no.
● 'a': Append (Añadir). Añade contenido al final del archivo si existe, lo crea si no. Ideal para
archivos de registro (logs).

Reforzar el Aprendizaje: Qué Practicar, Memorizar o tener como Criterio


1. Prioriza el with statement: Acostúmbrate a usar with open(...) as archivo: para manejar
ficheros. Esto simplificará tu código y, lo que es más importante, te protegerá de errores de
recursos no cerrados [384 (principio de uso para recursos)].
2. Manejo de \n: Recuerda que las operaciones de lectura de línea a menudo incluyen el
carácter \n y las operaciones de escritura lo requieren explícitamente si deseas nuevas
líneas. Usa strip() al leer para limpiar la cadena de espacios en blanco y saltos de línea al
inicio/final.
3. Modos de Apertura: Memoriza la diferencia entre 'r', 'w' y 'a'. La elección del modo es
fundamental para el comportamiento de tu programa con el archivo.
4. Rutas de Archivos: Presta atención a las rutas de los archivos (absolutas o relativas). Un
FileNotFoundError es común si la ruta es incorrecta o si el archivo no está en el directorio
esperado.

Ejemplos de Casos y Buenas Prácticas en Ciencia de Datos e IA


La interacción con archivos es omnipresente en Ciencia de Datos e IA para la persistencia y el
intercambio de información:
● Carga de Grandes Conjuntos de Datos: Leer archivos CSV o de texto grandes línea por línea
(usando for linea in archivo:) en lugar de cargarlos completamente con read() o readlines()
ayuda a evitar problemas de memoria (MemoryError) en sistemas con recursos limitados.
● Guardar Datos Preprocesados: Una vez que los datos crudos han sido limpiados y
transformados, es una buena práctica guardarlos en un nuevo archivo (CSV, JSON) utilizando
el modo de escritura ('w') para evitar tener que reprocesarlos en el futuro.
● Registro (Logging) de Experimentos y Métricas: Durante el entrenamiento de modelos de
Machine Learning, es común registrar el progreso, las métricas de rendimiento (precisión,
pérdida), los hiperparámetros utilizados y los mensajes de depuración en archivos de texto
(logs). El modo de adición ('a') es ideal para esto, ya que cada nueva entrada se añade al
final del archivo.
● Lectura de Configuraciones: Las aplicaciones de IA a menudo utilizan archivos de
configuración (por ejemplo, .ini, .json, .yaml) para almacenar parámetros del modelo, rutas
de datos o credenciales. Estos archivos se leen al inicio del programa para configurar su
comportamiento.
● Serialización y Deserialización de Modelos: Para guardar un modelo entrenado y poder
cargarlo más tarde sin tener que reentrenarlo, se utilizan módulos como pickle (mencionado
de pasada en) para serializar (guardar) el objeto del modelo en un archivo binario y luego
deserializarlo (cargarlo) cuando sea necesario. Aunque pickle es para binarios, el principio
de persistencia es el mismo.

7.3. Glosario y Autoevaluación de la Unidad


● Excepción: Un evento que interrumpe el flujo normal de ejecución de un programa debido a
un error o una situación anómala en tiempo de ejecución.
● try-except: Estructura de control en Python que permite definir un bloque de código (try)
donde pueden ocurrir excepciones y otro bloque (except) para manejar y responder a esas
excepciones de manera controlada, evitando la terminación abrupta del programa.
● finally: Bloque de código opcional dentro de una estructura try-except-else-finally que se
ejecuta siempre, independientemente de si se produce una excepción o no en el bloque try.
Es útil para tareas de limpieza de recursos.
● Ficheros (Archivos): Unidades de almacenamiento de información persistente en un sistema
de archivos. Permiten que los datos sobrevivan a la finalización del programa.
● Sistema de Ficheros: Estructura jerárquica (directorios y ficheros) utilizada para organizar la
información en un dispositivo de almacenamiento.
● open(): Función incorporada en Python utilizada para abrir un archivo y devolver un objeto
de archivo. Permite especificar el nombre del archivo y el modo de apertura ('r', 'w', 'a').
● close(): Método de un objeto de archivo que se utiliza para cerrar el archivo. Es crucial para
asegurar que los datos se guarden correctamente y evitar la corrupción del archivo.
● read(): Método de un objeto de archivo abierto en modo lectura que lee todo el contenido
del archivo como una sola cadena de texto.
● write(): Método de un objeto de archivo abierto en modo escritura o adición que escribe
una cadena de texto en el archivo. No añade automáticamente un salto de línea.
● Persistencia de Datos: La capacidad de los programas para "recordar" información entre
diferentes ejecuciones, almacenándola en dispositivos de almacenamiento como archivos o
bases de datos.
● Traceback (Traza Inversa): Lista de las funciones en curso de ejecución que Python imprime
cuando sucede un error en tiempo de ejecución. Es una herramienta esencial para la
depuración.
● Modos de Archivo: Caracteres que se pasan como segundo argumento a la función open()
para especificar el propósito de la apertura del archivo (ej., 'r' para lectura, 'w' para
escritura, 'a' para adición).
● with statement (Gestor de Contexto): Estructura en Python que garantiza la correcta
gestión de recursos (como archivos), asegurando que se cierren automáticamente al salir
del bloque, incluso si ocurren errores [384 (ejemplo con cursor de DB, aplicando el mismo
principio de gestión de recursos)].
Autoevaluación
Responde a las siguientes preguntas para afianzar tu comprensión de los conceptos de esta unidad.
Las respuestas y sus fundamentos se encuentran al final.
Cuestionario de Verdadero/Falso
1. Pregunta: La principal diferencia entre un error y una excepción en Python es que los
errores siempre detienen la ejecución del programa de forma incontrolada, mientras que las
excepciones pueden ser manejadas para permitir que el programa continúe.
○ Respuesta: Verdadero / Falso
2. Pregunta: Si un programa Python no cierra explícitamente un archivo después de escribir en
él, los datos siempre se perderán si el programa finaliza inesperadamente.
○ Respuesta: Verdadero / Falso
3. Pregunta: En Python, los datos almacenados en variables simples (int, float, str) son
persistentes por defecto y se conservan entre diferentes ejecuciones del programa.
○ Respuesta: Verdadero / Falso
Preguntas de Opción Múltiple
4. ¿Cuál de las siguientes es la forma más recomendada para abrir y asegurar que un archivo
se cierre correctamente en Python, incluso si ocurre un error? a) Usar open() y luego close()
en un bloque try-except. b) Usar open() y confiar en que el sistema operativo lo cierre
automáticamente. c) Usar la sentencia with open(...) as f:. d) Ninguna de las anteriores.
5. ¿Qué operador de modo de apertura utilizarías si quieres añadir nuevas líneas de texto al
final de un archivo existente sin borrar su contenido previo? a) 'r' b) 'w' c) 'a' d) 'x'
6. ¿Qué tipo de excepción se espera comúnmente si intentas convertir una cadena de texto
como "hola" a un número entero usando int()? a) ZeroDivisionError b) FileNotFoundError c)
TypeError d) ValueError
Completar la Frase
7. La capacidad de los programas para "recordar" información entre diferentes ejecuciones
guardando datos en archivos se conoce como ________________.
8. El bloque ________________ en una estructura try-except se ejecuta siempre, sin importar
si se produjo una excepción o no, y es ideal para liberar recursos.
9. Para obtener información detallada sobre la secuencia de llamadas a funciones que llevaron
a un error en tiempo de ejecución, se debe analizar la ________________ (o traceback).

Clave de Respuestas de Autoevaluación


Cuestionario de Verdadero/Falso
1. Pregunta: La principal diferencia entre un error y una excepción en Python es que los
errores siempre detienen la ejecución del programa de forma incontrolada, mientras que las
excepciones pueden ser manejadas para permitir que el programa continúe.
○ Respuesta: Verdadero.
○ Fundamento: Las excepciones son eventos de error que interrumpen el flujo normal
de ejecución, pero Python permite su captura y tratamiento (try-except) para evitar
que el programa termine abruptamente, a diferencia de errores sintácticos que
impiden la ejecución desde el inicio.
2. Pregunta: Si un programa Python no cierra explícitamente un archivo después de escribir en
él, los datos siempre se perderán si el programa finaliza inesperadamente.
○ Respuesta: Verdadero.
○ Fundamento: Si no se cierra un archivo explícitamente (o se usa un gestor de
contexto como with), los cambios pueden no ser guardados correctamente en el
disco, especialmente si el programa termina de manera inesperada o si el sistema
operativo no tiene tiempo de sincronizar los datos en el búfer.
3. Pregunta: En Python, los datos almacenados en variables simples (int, float, str) son
persistentes por defecto y se conservan entre diferentes ejecuciones del programa.
○ Respuesta: Falso.
○ Fundamento: Las variables simples almacenan datos en la memoria RAM, que es
volátil. Estos datos se pierden tan pronto como el programa finaliza su ejecución.
Para la persistencia, se deben guardar en un medio de almacenamiento como
archivos o bases de datos.
Preguntas de Opción Múltiple
4. ¿Cuál de las siguientes es la forma más recomendada para abrir y asegurar que un archivo
se cierre correctamente en Python, incluso si ocurre un error? a) Usar open() y luego close()
en un bloque try-except. b) Usar open() y confiar en que el sistema operativo lo cierre
automáticamente. c) Usar la sentencia with open(...) as f:. d) Ninguna de las anteriores.
○ Respuesta: c) Usar la sentencia with open(...) as f:.
○ Fundamento: La sentencia with es un "administrador de contexto" que garantiza
que el archivo se cierre automáticamente una vez que el bloque de código ha
finalizado, incluso si se produce un error, simplificando la gestión de recursos [384
(principio de uso para recursos, aunque no específicamente para open() en el
ejemplo), y buena práctica general en Python].
5. ¿Qué operador de modo de apertura utilizarías si quieres añadir nuevas líneas de texto al
final de un archivo existente sin borrar su contenido previo? a) 'r' b) 'w' c) 'a' d) 'x'
○ Respuesta: c) 'a'.
○ Fundamento: El modo 'a' (append) abre el archivo para añadir contenido al final. El
modo 'w' sobrescribe el contenido existente, y 'r' es solo para lectura.
6. ¿Qué tipo de excepción se espera comúnmente si intentas convertir una cadena de texto
como "hola" a un número entero usando int()? a) ZeroDivisionError b) FileNotFoundError c)
TypeError d) ValueError
○ Respuesta: d) ValueError.
○ Fundamento: Se lanza un ValueError cuando una función recibe un argumento del
tipo correcto pero con un valor inapropiado (como una cadena que no representa un
número para int()).
Completar la Frase
7. La capacidad de los programas para "recordar" información entre diferentes ejecuciones
guardando datos en archivos se conoce como persistencia de datos.
○ Fundamento: La persistencia de datos se refiere a la habilidad de almacenar
información de forma que sobreviva a la finalización del programa, lo cual se logra
comúnmente a través de archivos o bases de datos.
8. El bloque finally en una estructura try-except se ejecuta siempre, sin importar si se produjo
una excepción o no, y es ideal para liberar recursos.
○ Fundamento: El bloque finally garantiza la ejecución de su código, siendo
fundamental para tareas de limpieza como cerrar archivos o conexiones a bases de
datos, sin importar el resultado del bloque try.
9. Para obtener información detallada sobre la secuencia de llamadas a funciones que llevaron
a un error en tiempo de ejecución, se debe analizar la traza inversa (o traceback).
○ Fundamento: La traza inversa (traceback) proporciona una lista de las funciones en
curso de ejecución en el momento de un error, lo que es esencial para identificar el
origen y la causa de las excepciones.
Unidad 8: Programación Orientada a Objetos (POO): Modelando el Mundo
Real 🏗️

La Programación Orientada a Objetos (POO) es un paradigma que nos permite organizar el código
de una manera más intuitiva, modelando "objetos" del mundo real. En lugar de tener datos por un
lado y funciones por otro, la POO los agrupa en entidades coherentes, facilitando la creación de
sistemas complejos, escalables y mantenibles.

8.1. Conceptos Clave de POO: Clases y Objetos


a) Pregunta de Interés para la Ciencia de Datos e IA: En el ámbito de la Inteligencia Artificial, a
menudo trabajamos con conjuntos de datos complejos, como perfiles de clientes para un sistema
de recomendación, donde cada cliente tiene un nombre, un ID, un historial de compras y
preferencias. O en el desarrollo de modelos de Machine Learning, donde cada modelo tiene
parámetros, un estado de entrenamiento y la capacidad de hacer predicciones. ¿Cómo podemos
representar esta información estructurada y sus comportamientos asociados en un programa de
una manera que sea intuitiva y organizada, en lugar de usar listas o variables separadas y sin
conexión directa? ¿Cómo capturamos la esencia de una "entidad" con sus propios "datos" y
"acciones"?
b) Desarrollo de la Respuesta: Teoría, Procedimientos y Sintaxis de Python: La Programación
Orientada a Objetos (POO) surge como una respuesta a esta necesidad. En lugar de enfocarse
únicamente en procesos y funciones (como en el paradigma imperativo), la POO busca representar
las entidades o "objetos" del dominio del problema de la forma más natural posible.
1. Objetos: En POO, un objeto es una entidad modelada en el programa que representa un
elemento del mundo real. Imagina que es como un "muffin" individual que ha sido creado a
partir de un molde. Los objetos poseen:
○ Estados (o atributos): Son las características o propiedades que describen al objeto.
Por ejemplo, un objeto "Cliente" podría tener atributos como nombre, id_cliente,
historial_compras, preferencias.
○ Comportamientos (o métodos): Son las acciones que el objeto puede realizar o las
operaciones que se pueden aplicar sobre sus atributos. Siguiendo el ejemplo del
"Cliente", podría tener métodos como calcular_gasto_total() o
recomendar_productos().
○ Los objetos también soportan el encapsulamiento (que veremos más adelante,
significa que solo exponen lo necesario), tienen un tiempo de vida (se crean y se
destruyen durante la ejecución del programa) y, fundamentalmente, son instancias
de una clase. La ejecución de un programa en POO consiste en una serie de
interacciones entre estos objetos.
2. Clases: Una clase es la "plantilla", "molde" o "plano" a partir del cual se crean los objetos.
Piensa en la clase como el "molde de muffins" que define cómo serán todos los muffins que
se creen a partir de él. La clase define el conjunto de atributos y métodos que todos los
objetos (instancias) de esa clase tendrán.
○ Las clases ofrecen un alto nivel de abstracción, encapsulan detalles internos de
implementación y se relacionan entre sí mediante jerarquías (lo cual exploraremos
con la herencia). Permiten crear tipos de datos "a medida" que se ajustan a las
necesidades específicas de tu problema.
c) Refuerzo del Aprendizaje:
● Recuerda: En POO, piensa en los "sustantivos" (personas, cosas, conceptos) de tu problema
como posibles Clases u Objetos. Los "verbos" (acciones que esos sustantivos pueden
realizar) serán sus Métodos.
● Criterio Clave: La POO es especialmente útil cuando tus programas necesitan gestionar
entidades complejas que tienen tanto datos como lógicas operativas asociadas. Permite
modelar el mundo real de forma más intuitiva.
d) Ejemplos de Casos o Buenas Prácticas de Aplicación en Ciencia de Datos e IA:
● En Ciencia de Datos: Podrías definir una clase DataFramePersonalizado que no solo
almacene datos (atributos), sino que también tenga métodos como limpiar_datos(),
normalizar_columna() o filtrar_por_valor().
● En Inteligencia Artificial: Una clase AgenteIA podría tener atributos como estado_actual,
objetivos y métodos como tomar_decision(), aprender_de_experiencia(). Esto ayuda a
encapsular la complejidad de un agente inteligente en una unidad coherente.
● En Modelos Predictivos: Una clase ModeloML podría tener atributos como
hiperparametros, datos_entrenamiento, y métodos entrenar(), predecir(), evaluar(). Esto
permite manejar diferentes modelos de ML como objetos con comportamientos
estandarizados.
e) Autoevaluación (Verdadero/Falso):
1. Pregunta: Un objeto es una plantilla o molde para crear entidades.
○ Respuesta: Falso. Fundamento: Una clase es la plantilla o molde; un objeto es una
instancia concreta creada a partir de esa plantilla.
2. Pregunta: Los atributos de un objeto describen sus comportamientos.
○ Respuesta: Falso. Fundamento: Los atributos describen los estados o propiedades
de un objeto. Los métodos describen sus comportamientos o acciones.
3. Pregunta: La Programación Orientada a Objetos busca organizar el código de manera más
natural, representando entidades del mundo real.
○ Respuesta: Verdadero. Fundamento: Este es uno de los propósitos centrales de la
POO, que busca una visión más intuitiva y cercana al lenguaje humano para abordar
problemas complejos.

8.2. Definición de Clases e Instancias en Python


a) Pregunta de Interés para la Ciencia de Datos e IA: Ya comprendemos la teoría de las clases y los
objetos. Ahora, la pregunta práctica es: ¿cómo se traduce esto al código Python? Si queremos
simular una red de sensores en una ciudad inteligente, donde cada sensor debe tener una
identificación única, una ubicación y la capacidad de registrar lecturas, ¿cómo creamos el "plano"
(Sensor) en Python y luego "fabricamos" múltiples sensores individuales con sus propias
características?
b) Desarrollo de la Respuesta: Teoría, Procedimientos y Sintaxis de Python: En Python, la sintaxis
para definir clases es relativamente simple, buscando un equilibrio entre lo práctico y lo
conceptual.
1. Definición de Clases en Python:
○ Se utiliza la palabra clave class seguida del nombre de la clase, que por convención
suele empezar con una letra mayúscula (PascalCase), y finaliza con dos puntos (:).
○ El cuerpo de la clase (sus atributos y métodos) debe estar indentado.
# Ejemplo de sintaxis de definición de clase
class NombreDeMiClase:
# Atributos de clase (opcional)
atributo_clase = "Valor común"

# Métodos de la clase
def __init__(self, parametro1, parametro2):
# Constructor para inicializar instancias
pass
2. Constructores (__init__):
○ El método especial __init__ (conocido como constructor) es fundamental. Se invoca
automáticamente cada vez que se crea una nueva instancia (un objeto) de la clase.
○ Su propósito principal es inicializar los atributos del objeto recién creado con los
valores que se le pasen como argumentos.
○ Todos los métodos de instancia en Python, incluido __init__, deben tener self como
su primer parámetro. self es una convención que se refiere a la propia instancia del
objeto que se está creando o sobre el que se está operando.
○ Dentro de __init__, los atributos de la instancia se definen y asignan usando la
notación self.nombre_atributo = valor_del_parametro.
# Ejemplo de definición de una clase y su constructor
class Sensor:
def __init__(self, id_sensor, ubicacion, tipo_dato):
# Inicializamos los atributos de la instancia
self.id = id_sensor # Atributo de instancia: ID único del sensor
self.ubicacion = ubicacion # Atributo de instancia: Ubicación del sensor
self.tipo = tipo_dato # Atributo de instancia: Tipo de dato que mide (ej. "temperatura")
print(f"Sensor {self.id} creado en {self.ubicacion}")

3. Creación de Instancias (Objetos):


○ Una vez definida la clase, puedes crear tantos objetos (instancias) como necesites.
Para hacerlo, "llamas" a la clase como si fuera una función, pasando los argumentos
que corresponden a los parámetros definidos en el método __init__ (excluyendo
self).
○ Una instancia es un objeto real y concreto que se deriva del plano definido por la
clase.
# Ejemplo de creación de instancias de la clase Sensor
sensor_temp_1 = Sensor("S001", "Cocina", "temperatura")
sensor_hum_a = Sensor("H002", "Jardín", "humedad")
sensor_pres_b = Sensor("P003", "Exterior", "presion")

# Los objetos ya existen en memoria con sus atributos inicializados


print(f"El sensor 1 es de tipo: {sensor_temp_1.tipo}") # Acceso a un atributo del objeto
print(f"El sensor 2 se encuentra en: {sensor_hum_a.ubicacion}")
c) Refuerzo del Aprendizaje:
● Recuerda: self es un pilar en Python para los métodos de instancia. Siempre debe ser el
primer parámetro. No es una palabra reservada en sí misma, pero es la convención
universalmente aceptada y fundamental para referirse a la instancia actual.
● Practica: Define clases sencillas (como un Libro, Dado, CuentaBancaria) y crea varios objetos
de esas clases. Asegúrate de que puedes acceder a sus atributos después de la creación.
● Convención: Los nombres de clases en Python se escriben en PascalCase (cada palabra
empieza con mayúscula, sin guiones), mientras que los nombres de variables y funciones
(incluidas las instancias) se escriben en snake_case (todo en minúsculas, palabras separadas
por guiones bajos).
d) Ejemplos de Casos o Buenas Prácticas de Aplicación en Ciencia de Datos e IA:
● Creación de modelos personalizados: Podrías definir una clase PreprocesadorDatos con un
__init__ que reciba parámetros de configuración (ej., tipo de escalado, manejo de valores
nulos).
class PreprocesadorDatos:
def __init__(self, metodo_escalado="min_max", manejar_nulos="media"):
self.metodo_escalado = metodo_escalado
self.manejar_nulos = manejar_nulos
print(f"Preprocesador configurado con escalado '{self.metodo_escalado}' y manejo de nulos por
'{self.manejar_nulos}'.")

scaler_estandar = PreprocesadorDatos("estandar", "eliminar")


scaler_minmax = PreprocesadorDatos() # Usará los valores por defecto

● Simulación de Entidades en IA: Para la simulación de partículas en un entorno de física,


cada partícula podría ser una instancia de una clase Particula con atributos posicion_x,
posicion_y, velocidad, etc., inicializados en el constructor.
e) Autoevaluación (Verdadero/Falso):
1. Pregunta: Al definir una clase en Python, el método __init__ se invoca automáticamente al
crear una nueva instancia.
○ Respuesta: Verdadero. Fundamento: El método __init__ es el constructor y es
llamado de forma implícita cuando se instancia un objeto de la clase.
2. Pregunta: El primer parámetro self en un método de Python es opcional si la función no
recibe otros argumentos.
○ Respuesta: Falso. Fundamento: El parámetro self es siempre el primer parámetro en
los métodos de instancia y es necesario para referirse a la propia instancia del
objeto.
3. Pregunta: Para crear un objeto de una clase MiClase, se utiliza la sintaxis objeto = MiClase
sin paréntesis.
○ Respuesta: Falso. Fundamento: Para instanciar un objeto, la clase debe ser
"llamada" como una función, incluyendo paréntesis (ej., objeto = MiClase()), incluso
si no se pasan argumentos, para que el constructor __init__ se ejecute.

8.3. Atributos y Métodos (incluyendo Métodos Especiales)


a) Pregunta de Interés para la Ciencia de Datos e IA: Una vez que hemos definido nuestras clases y
creado objetos, como nuestros Sensor en la red inteligente, ¿cómo hacemos para que estos objetos
realmente almacenen los datos que recolectan (e.g., la temperatura actual) y realicen acciones
significativas, como obtener_lectura() o enviar_alerta()? Además, ¿podemos hacer que nuestros
objetos personalizados, como una clase Vector para operaciones matemáticas, se comporten de
manera tan intuitiva como los tipos de datos nativos de Python (por ejemplo, que len(mi_vector)
devuelva su dimensión o que vector1 + vector2 realice una suma vectorial)?
b) Desarrollo de la Respuesta: Teoría, Procedimientos y Sintaxis de Python: Las clases no solo
definen la estructura inicial de los objetos, sino también su funcionalidad a través de atributos y
métodos.
1. Atributos:
○ Los atributos son variables que pertenecen a una clase o a una instancia específica
de esa clase, y se utilizan para almacenar los datos que definen el estado o las
propiedades de los objetos.
○ Atributos de instancia: Son los más comunes. Se definen dentro del constructor
__init__ (o en otros métodos) usando self. y son únicos para cada objeto. Por
ejemplo, self.lectura_actual para un sensor.
○ Atributos de clase: Son variables que se definen directamente dentro del cuerpo de
la clase (no en un método) y son compartidas por todas las instancias de esa clase.
Pueden usarse para valores constantes o configuraciones comunes a todos los
objetos de ese tipo.
2. Métodos:
○ Los métodos son funciones definidas dentro de una clase que operan sobre los
atributos de los objetos de esa clase. Representan los comportamientos del objeto.
○ Se invocan utilizando la "notación de punto" sobre una instancia del objeto (ej.,
mi_objeto.mi_metodo()).
○ Al igual que __init__, el primer parámetro de cualquier método de instancia es
siempre self, que permite al método acceder y modificar los atributos de la instancia
sobre la que se llamó.
class Sensor:
# Atributo de clase: común a todos los sensores
MAX_LECTURA = 100

def __init__(self, id_sensor, ubicacion, tipo_dato):


self.id = id_sensor
self.ubicacion = ubicacion
self.tipo = tipo_dato
self.lectura_actual = None # Atributo de instancia: inicializado a None

# Método de instancia para simular la toma de lectura


def obtener_lectura(self, valor):
if valor <= self.MAX_LECTURA: # Acceso a atributo de clase
self.lectura_actual = valor
print(f"Sensor {self.id}: Nueva lectura - {self.lectura_actual} ({self.tipo})")
else:
print(f"Error: Lectura excede el valor máximo para el Sensor {self.id}.")

# Método de instancia para simular una alerta


def enviar_alerta(self):
if self.lectura_actual is not None and self.lectura_actual > 80:
print(f"¡ALERTA! El sensor {self.id} en {self.ubicacion} registra un valor alto: {self.lectura_actual}")

3. Métodos Especiales (Dunder Methods / Magic Methods):


○ Son métodos con nombres predefinidos que empiezan y terminan con doble guion
bajo (ej., __str__, __len__, __add__). Python los invoca automáticamente en ciertas
situaciones, permitiendo que tus clases personalizadas se comporten de manera
similar a los tipos de datos nativos del lenguaje. Esto contribuye a la "elegancia" del
código.
○ __str__(self): Define la representación en cadena de un objeto. Cuando usas print()
o str() con un objeto, Python busca este método. Debe devolver una cadena de
texto.
○ __len__(self): Permite que la función len() funcione con tu objeto, devolviendo su
"longitud" o "tamaño".
○ __add__(self, other): Define el comportamiento del operador de suma (+) cuando se
usa con objetos de tu clase. Similarmente, __sub__ para resta, __mul__ para
multiplicación, etc..
○ __getitem__(self, key) y __setitem__(self, key, value): Permiten acceder o modificar
elementos de tu objeto usando la notación de corchetes [], como si fuera una lista o
diccionario.
class Vector:
def __init__(self, componentes):
self.componentes = list(componentes) # Aseguramos que sea una lista mutable

# Método especial: representación legible del objeto


def __str__(self):
return f"Vector({self.componentes})"

# Método especial: permite usar len()


def __len__(self):
return len(self.componentes)

# Método especial: permite la suma de vectores (asumiendo misma dimensión)


def __add__(self, otro_vector):
if len(self) != len(otro_vector):
raise ValueError("Los vectores deben tener la misma dimensión para ser sumados.")
nueva_lista_componentes = [
self.componentes[i] + otro_vector.componentes[i]
for i in range(len(self.componentes))
]
return Vector(nueva_lista_componentes)

v1 = Vector()
v2 = Vector()
print(v1) # Llama a __str__
print(f"Dimensión de v1: {len(v1)}") # Llama a __len__
v3 = v1 + v2 # Llama a __add__
print(v3) # Output: Vector()
c) Refuerzo del Aprendizaje:
● Idea Clave: Los atributos son los sustantivos que describen la información de un objeto,
mientras que los métodos son los verbos que describen las acciones que puede realizar o las
operaciones sobre sus datos.
● Explora: Investiga los métodos __str__ y __len__ para hacer tus objetos más amigables.
Experimenta con __add__ si tu objeto representa una entidad numérica o combinable.
● Criterio: Utiliza los métodos especiales para que tus clases personalizadas se integren de
forma natural con las funciones y operadores incorporados de Python. Esto mejora la
legibilidad y la "pitonicidad" (Pythonicity) de tu código.
d) Ejemplos de Casos o Buenas Prácticas de Aplicación en Ciencia de Datos e IA:
● Clases de Datos Científicos: Una clase Punto (para coordenadas en un espacio) podría
implementar __add__ para la suma de puntos, o __str__ para una representación legible (x,
y).
● Representación de Modelos: En un modelo de Machine Learning, podrías tener un método
evaluar_rendimiento() que calcule y retorne métricas, o un __str__ para imprimir un
resumen de los parámetros del modelo y su estado de entrenamiento.
● Preprocesamiento de Texto: Una clase Documento podría tener atributos contenido_raw y
tokens, y métodos como tokenizar() o limpiar_texto(). El método __len__ podría devolver el
número de tokens en el documento.
e) Autoevaluación (Verdadero/Falso):
1. Pregunta: El método __str__ en Python es llamado automáticamente cuando se usa la
función print() con un objeto de esa clase.
○ Respuesta: Verdadero. Fundamento: Python invoca __str__ para obtener una
representación legible del objeto cuando se utiliza con print() o str().
2. Pregunta: Los atributos de una clase son funciones que operan sobre los datos del objeto.
○ Respuesta: Falso. Fundamento: Los atributos son las variables que almacenan el
estado o las propiedades del objeto. Los métodos son las funciones que operan
sobre esos atributos.
3. Pregunta: Los métodos especiales (dunder methods) permiten que los objetos de una clase
personalizada interactúen con operadores y funciones incorporadas de Python de manera
predefinida.
○ Respuesta: Verdadero. Fundamento: Métodos como __len__, __add__, __str__
permiten que las clases personalizadas se integren y respondan a funciones y
operadores nativos de Python de una manera esperada y consistente.

8.4. Principios Fundamentales de POO (Introducción)


a) Pregunta de Interés para la Ciencia de Datos e IA: Hemos logrado construir objetos individuales
y dotarlos de vida. Pero, ¿cómo escalamos esto a sistemas de IA y Ciencia de Datos masivos y
colaborativos? Por ejemplo, si tenemos diferentes tipos de modelos de Machine Learning
(clasificadores, modelos de regresión), todos son "modelos" y comparten comportamientos
comunes (entrenar, predecir). ¿Cómo evitamos duplicar código y diseñamos nuestro sistema de
manera que sea fácil de extender con nuevos tipos de modelos en el futuro? Además, ¿cómo
garantizamos que los detalles internos complejos de un algoritmo (ej., la implementación de un
árbol de decisión) no sean expuestos innecesariamente a otras partes del programa, manteniendo
el código organizado y protegiéndolo de modificaciones accidentales?
b) Desarrollo de la Respuesta: Teoría, Procedimientos y Sintaxis de Python: Para construir
sistemas robustos, escalables y mantenibles, la POO se basa en varios principios fundamentales que
guían el diseño de clases y objetos.
1. Encapsulamiento:
○ El encapsulamiento es el principio de ocultar los detalles internos de
implementación de una clase y exponer solo lo necesario a través de una interfaz
pública y bien definida. Esto significa que los datos (atributos) y la lógica interna de
una clase (métodos) se agrupan en una unidad, y el acceso a ellos se controla para
protegerlos de usos indebidos o modificaciones directas no intencionadas desde
fuera de la clase.
○ Python no tiene modificadores de acceso estrictos (como private o public en otros
lenguajes), pero utiliza convenciones (como prefijar atributos con un guion bajo _
para indicar que son "protegidos" o dos guiones bajos __ para "privados" - name
mangling) para sugerir que no deben ser accedidos directamente.
○ Está estrechamente relacionado con la cohesión, que es la característica deseable de
que una clase o módulo tenga una única y bien definida responsabilidad. Esto facilita
el mantenimiento, las pruebas unitarias y la escalabilidad.
# Ejemplo conceptual de encapsulamiento
class ProcesadorDatos:
def __init__(self, datos):
self.__datos_crudos = datos # Atributo "privado" por convención
self.__datos_limpios = [] # Otro atributo "privado"

def __limpiar_internamente(self): # Método "privado"


# Lógica compleja de limpieza de datos aquí
self.__datos_limpios = [d for d in self.__datos_crudos if d is not None]
print("Datos limpiados internamente.")

def obtener_datos_procesados(self): # Método público


if not self.__datos_limpios:
self.__limpiar_internamente()
return self.__datos_limpios

mi_procesador = ProcesadorDatos([1, None, 2, 3, None])


# mi_procesador.__datos_crudos # No debe accederse directamente
# mi_procesador.__limpiar_internamente() # No debe llamarse directamente

datos_listos = mi_procesador.obtener_datos_procesados()
print(datos_listos) # Output: Datos limpiados internamente.
2. Herencia:
○ La herencia es un mecanismo que permite a una clase (la subclase o clase hija)
adquirir todos los atributos y métodos de otra clase (la superclase o clase padre).
Esto se conoce como una relación "es un" (por ejemplo, un "Coche" es un
"Vehículo").
○ Su principal beneficio es la reutilización de código. Las subclases no necesitan
redefinir comportamientos o atributos que ya existen en la superclase, simplemente
los heredan. Esto facilita la extensión del software y el mantenimiento.
○ Python permite herencia múltiple (una clase puede heredar de varias superclases),
aunque a menudo se prefiere la herencia simple para evitar complejidades.
○ Sintaxis: class SubClase(SuperClase):.
class ModeloML: # Superclase (clase padre)
def __init__(self, nombre_modelo):
self.nombre = nombre_modelo
self.entrenado = False

def entrenar(self, datos):


print(f"Entrenando modelo {self.nombre} con datos...")
self.entrenado = True

def predecir(self, entrada):


raise NotImplementedError("Este método debe ser implementado por la subclase.")
class Clasificador(ModeloML): # Subclase que hereda de ModeloML
def __init__(self, nombre_modelo, tipo_clasificador):
super().__init__(nombre_modelo) # Llama al constructor de la superclase
self.tipo = tipo_clasificador

def predecir(self, entrada): # Sobrescribe el método predecir


if not self.entrenado:
print("El clasificador no ha sido entrenado.")
return None
print(f"Clasificando entrada con {self.nombre} ({self.tipo})...")
# Lógica de clasificación específica
return "Clase_X"

class Regresor(ModeloML): # Otra subclase que hereda de ModeloML


def __init__(self, nombre_modelo, tipo_regresor):
super().__init__(nombre_modelo)
self.tipo = tipo_regresor

def predecir(self, entrada): # Sobrescribe el método predecir


if not self.entrenado:
print("El regresor no ha sido entrenado.")
return None
print(f"Prediciendo valor con {self.nombre} ({self.tipo})...")
# Lógica de regresión específica
return 123.45 # Ejemplo de valor predicho
3. Polimorfismo:
○ El polimorfismo (del griego "muchas formas") es la capacidad de que objetos de
diferentes clases respondan al mismo mensaje (o método) de maneras distintas,
según su tipo específico.
○ Permite escribir código más genérico y flexible, ya que el mismo código puede
operar sobre objetos de distintas clases de forma unificada sin necesidad de conocer
el tipo exacto de cada objeto en tiempo de ejecución.
○ En el ejemplo de herencia anterior, tanto Clasificador como Regresor tienen un
método predecir(). Aunque el nombre del método es el mismo, su implementación
es diferente en cada subclase. Una función externa puede llamar a predecir() en
cualquier objeto que sea un ModeloML (o sus subclases), y Python ejecutará la
implementación correcta para ese tipo de objeto.
def ejecutar_prediccion(modelos, datos_nuevos):
for modelo in modelos:
print(f"\nProcesando modelo: {modelo.nombre}")
# El polimorfismo actúa aquí: se llama al método 'predecir'
# específico de cada objeto (Clasificador o Regresor)
resultado = modelo.predecir(datos_nuevos)
if resultado is not None:
print(f"Resultado de la predicción: {resultado}")

# Creamos instancias de las subclases


mi_clasificador = Clasificador("Clasificador de Spam", "Bayesiana")
mi_regresor = Regresor("Regresor de Precios", "Lineal")
# Entrenamos los modelos (usando el método heredado de ModeloML)
mi_clasificador.entrenar("emails de ejemplo")
mi_regresor.entrenar("datos de precios históricos")

# Ejecutamos las predicciones de forma polimórfica


ejecutar_prediccion([mi_clasificador, mi_regresor], "nueva entrada")
c) Refuerzo del Aprendizaje:
● Encapsulamiento: Piensa en ello como una "caja negra" donde solo se ven las entradas y
salidas, pero no los mecanismos internos. Promueve una "única responsabilidad" para cada
clase.
● Herencia: Identifica las relaciones "es un" en tu problema para aplicar la herencia. Por
ejemplo, un Perro es un Animal.
● Polimorfismo: Permite la flexibilidad. Si tienes una lista de diferentes tipos de objetos que
comparten una interfaz común (mismos nombres de métodos), puedes procesarlos de la
misma manera.
● Criterio: Dominar estos principios es esencial para diseñar software que sea robusto, fácil de
escalar y mantener a lo largo del tiempo, especialmente en proyectos complejos de Ciencia
de Datos e IA.
d) Ejemplos de Casos o Buenas Prácticas de Aplicación en Ciencia de Datos e IA:
● Encapsulamiento en pipelines de datos: Una clase ETLPipeline podría encapsular
internamente los pasos de Extracción, Transformación y Carga. Los métodos
_extraer_datos_brutos(), _transformar_formato(), _cargar_a_base_de_datos() podrían ser
internos (_ o __) y solo exponer un método público ejecutar_pipeline().
● Herencia para Algoritmos: Una clase base ClusteringAlgorithm podría definir una interfaz
(fit(), predict_clusters()). Luego, KMeansAlgorithm y DBSCANAlgorithm podrían heredar de
ella, implementando su lógica específica.
● Polimorfismo en evaluación de modelos: Una función reporte_rendimiento(lista_modelos)
puede recibir una lista que contenga diferentes objetos de modelos (ej., RegresionLineal,
RandomForest, SVM), y cada uno de ellos implementa su propio método
calcular_metricas(). El polimorfismo permite llamar modelo.calcular_metricas() para cada
elemento en la lista, obteniendo las métricas específicas para cada tipo de modelo.
e) Autoevaluación (Verdadero/Falso):
1. Pregunta: La herencia en POO permite que una subclase adquiera los atributos y métodos
de una superclase, facilitando la reutilización de código.
○ Respuesta: Verdadero. Fundamento: La herencia establece una relación jerárquica
"es un" y permite a las subclases heredar funcionalidades de sus superclases,
reduciendo la duplicación y promoviendo la reutilización.
2. Pregunta: El encapsulamiento implica que todos los atributos y métodos de una clase deben
ser directamente accesibles desde cualquier parte del programa.
○ Respuesta: Falso. Fundamento: El encapsulamiento, por el contrario, busca ocultar
los detalles internos de implementación de una clase y controlar el acceso a sus
atributos y métodos, exponiendo solo lo necesario a través de una interfaz pública.
3. Pregunta: El polimorfismo se refiere a la capacidad de que objetos de diferentes clases
respondan de la misma manera a métodos diferentes.
○ Respuesta: Falso. Fundamento: El polimorfismo se refiere a la capacidad de que
objetos de diferentes clases respondan al mismo método (o mensaje) de maneras
distintas, según su tipo específico.
Glosario de Términos Clave de la Unidad 8
● Programación Orientada a Objetos (POO): Paradigma de programación que organiza el
código alrededor de "objetos" que representan entidades del mundo real, con el objetivo de
hacer los programas más flexibles, mantenibles y escalables.
● Objeto: Una instancia concreta de una clase; una entidad en el programa que encapsula
datos (atributos) y comportamiento (métodos).
● Clase: Una plantilla o molde que define la estructura (atributos) y el comportamiento
(métodos) que tendrán los objetos creados a partir de ella.
● Atributo: Una variable que pertenece a una clase o a una instancia de una clase,
representando una propiedad o característica del objeto.
● Método: Una función definida dentro de una clase que opera sobre los atributos de los
objetos de esa clase, representando un comportamiento del objeto.
● Instancia: Un término que se utiliza indistintamente con "objeto", refiriéndose a una
ocurrencia individual y concreta de una clase.
● Constructor (__init__): Un método especial en Python que se invoca automáticamente
cuando se crea una nueva instancia de una clase, utilizado para inicializar los atributos del
objeto.
● self: El primer parámetro de un método de instancia en Python, que es una convención para
referirse a la propia instancia del objeto sobre la que se está operando.
● Métodos Especiales (Dunder Methods / Magic Methods): Métodos en Python con nombres
que comienzan y terminan con doble guion bajo (ej., __str__), que son invocados
automáticamente por el lenguaje en situaciones específicas, permitiendo a las clases
personalizar el comportamiento de operadores y funciones incorporadas.
● Encapsulamiento: Principio de la POO que consiste en ocultar los detalles internos de
implementación de una clase y exponer solo lo necesario a través de una interfaz pública,
protegiendo así la integridad de los datos y la lógica.
● Herencia: Mecanismo de la POO que permite a una clase (subclase) adquirir los atributos y
métodos de otra clase (superclase), promoviendo la reutilización de código y la creación de
jerarquías.
● Polimorfismo: Principio de la POO que permite que objetos de diferentes clases respondan
al mismo mensaje (método) de maneras distintas, según su tipo, facilitando la escritura de
código genérico y flexible.
● Cohesión: Una característica deseable en el diseño de software, donde una clase o módulo
tiene una única y bien definida responsabilidad, lo que mejora su comprensión,
mantenimiento y reutilización.
● Paradigma de Programación: Un estilo o filosofía fundamental para organizar y estructurar
el código de un programa (ej., imperativo, funcional, orientado a objetos).

Autoevaluación de la Unidad 8: Fundamentos de Programación Orientada a Objetos


Cuestionario:
1. Verdadero/Falso:
○ Pregunta 1: En Python, el método especial __add__ permite definir cómo una clase
personalizada interactúa con el operador de multiplicación (*).
○ Pregunta 2: La principal ventaja del polimorfismo es que obliga a cada subclase a
implementar métodos con nombres únicos.
○ Pregunta 3: Un atributo de clase es una variable compartida por todas las instancias
de esa clase.
○ Pregunta 4: Para ocultar detalles internos de una clase y exponer solo lo necesario,
se aplica el principio de herencia.
2. Selección Múltiple:
○ Pregunta 5: ¿Cuál de los siguientes términos describe una "plantilla" o "molde" para
crear objetos? a) Objeto b) Instancia c) Clase d) Método
○ Pregunta 6: Si una clase Vehiculo tiene un método arrancar(), y las clases Coche y
Moto heredan de Vehiculo y sobrescriben arrancar() con su propia lógica, esto es un
ejemplo de: a) Encapsulamiento b) Modularización c) Herencia y Polimorfismo d)
Cohesión
○ Pregunta 7: ¿Qué palabra clave se utiliza en Python para definir una nueva clase? a)
define b) class c) new d) object
3. Completar la Frase:
○ Pregunta 8: La capacidad de que un objeto mantenga sus datos y funciones
relacionadas juntas, y oculte los detalles internos de su implementación, se conoce
como _______________.
○ Pregunta 9: El primer parámetro de cualquier método de instancia en Python es
convencionalmente llamado _______________ y se refiere al propio objeto que
llama al método.
○ Pregunta 10: En POO, los datos que describen el estado o las propiedades de un
objeto se conocen como _______________.

Respuestas y Fundamento del Cuestionario:


1. Verdadero/Falso:
○ Pregunta 1: Falso. Fundamento: El método __add__ define el comportamiento del
operador de suma (+). Para el operador de multiplicación (*), se utiliza el método
__mul__.
○ Pregunta 2: Falso. Fundamento: La principal ventaja del polimorfismo es que
permite que objetos de diferentes clases respondan al mismo método (o mensaje) de
maneras distintas, según su tipo, lo que permite un código más genérico.
○ Pregunta 3: Verdadero. Fundamento: Un atributo de clase se define directamente
en el cuerpo de la clase y es accesible y compartido por todas las instancias de esa
clase, a diferencia de los atributos de instancia que son únicos para cada objeto.
○ Pregunta 4: Falso. Fundamento: Para ocultar detalles internos de una clase y
exponer solo lo necesario, se aplica el principio de Encapsulamiento, no de herencia.
La herencia se enfoca en la reutilización y la jerarquía.
2. Selección Múltiple:
○ Pregunta 5: c) Clase. Fundamento: Una clase es la plantilla o molde a partir del cual
se crean los objetos.
○ Pregunta 6: c) Herencia y Polimorfismo. Fundamento: Coche y Moto heredan de
Vehiculo (herencia). Al sobrescribir el método arrancar(), demuestran polimorfismo,
ya que diferentes objetos (Coche, Moto) responden de manera diferente al mismo
mensaje (arrancar()).
○ Pregunta 7: b) class. Fundamento: La palabra clave class es la sintaxis estándar en
Python para definir una nueva clase.
3. Completar la Frase:
○ Pregunta 8: La capacidad de que un objeto mantenga sus datos y funciones
relacionadas juntas, y oculte los detalles internos de su implementación, se conoce
como encapsulamiento.
○ Pregunta 9: El primer parámetro de cualquier método de instancia en Python es
convencionalmente llamado self y se refiere al propio objeto que llama al método.
○ Pregunta 10: En POO, los datos que describen el estado o las propiedades de un
objeto se conocen como atributos.
Unidad Transversal: Colaboración y Control de Versiones con Git y GitHub

Esta unidad no es un tema técnico más; es una habilidad profesional indispensable. Las
herramientas que veremos aquí son pilares en el desarrollo de software moderno y en la ciencia de
datos reproducible.

T.1. Git: La Máquina del Tiempo para tu Código ⏳


¿Alguna vez has tenido archivos como informe_final.py, informe_final_v2.py,
informe_AHORA_SI_FINAL.py? Este caos es el problema que resuelve un Sistema de Control de
Versiones (SCV). Git es el SCV más popular del mundo. Es una herramienta que registra los cambios
en tu código a lo largo del tiempo, permitiéndote "viajar" a cualquier versión anterior.
Para entender Git, usemos una analogía de oficina:
1. Área de Trabajo (Working Directory): Es tu escritorio, donde tienes los documentos y trabajas
en ellos. Los archivos que modificas aquí están en este estado.
2. Área de Preparación (Staging Area): Es una bandeja de "listos para archivar". Usas el comando
git add para mover los archivos de tu escritorio a esta bandeja. Es tu oportunidad de revisar
qué cambios quieres guardar exactamente.
3. Repositorio Local (Local Repository): Es tu archivador personal. Con el comando git commit -m
"mensaje", tomas una "foto" de todo lo que hay en la bandeja de preparación, la guardas en
una carpeta en el archivador y le pones una etiqueta con fecha y un mensaje descriptivo de lo
que hiciste. Este es tu historial local.
4. Repositorio Remoto (Remote Repository): Es una fotocopia de seguridad de tu archivador,
guardada en la nube (en un servidor como GitHub). Usas git push para enviar tus nuevas
carpetas archivadas a la nube y git pull para traer las actualizaciones de otros.
Escribir mensajes de commit claros y significativos es una habilidad de comunicación crucial en un
equipo. Un mensaje como "arreglos" no ayuda a nadie. Un mensaje como "Corrige bug en cálculo
de promedios para valores nulos" es infinitamente más útil.7

T.2. GitHub: El Ecosistema para la Colaboración en Proyectos 🤝


Si Git es el protocolo subyacente, GitHub es la plataforma que lo hace accesible y colaborativo. La
relación es similar a la del correo electrónico y Gmail: el protocolo permite enviar y recibir
mensajes, mientras que la plataforma (Gmail) añade una interfaz web, gestión de contactos, filtros
de spam y otras herramientas útiles.
GitHub aloja tus repositorios remotos y añade herramientas cruciales para el trabajo en equipo,
como Pull Requests (para revisar cambios antes de integrarlos) e Issues (para reportar bugs o
planificar tareas).
Simplificación para este curso: Git permite trabajar en "ramas" (líneas de desarrollo paralelas). Esta
es una característica muy potente, pero para no abrumar, en este curso nos centraremos en
trabajar únicamente en la rama principal, llamada main (o master). Aprender a manejar múltiples
ramas es un siguiente paso en tu formación profesional.

T.3. La Clave para la Ciencia Reproducible: ¿Por Qué Git es Indispensable? 🔬


Para un científico de datos, el mayor beneficio de Git no es solo la colaboración, sino la
reproducibilidad. Un resultado científico no es válido si otros no pueden reproducirlo. En la ciencia
computacional, esto significa poder ejecutar el mismo código sobre los mismos datos y obtener el
mismo resultado.
Sin Git, es imposible saber qué versión exacta del código generó los resultados de un informe o una
publicación. Con Git, cada commit tiene un identificador único, un hash, que actúa como una
"huella digital" 핑거프린트 para esa versión específica del proyecto. Al citar este hash en tu
trabajo, cualquier persona en el mundo puede "viajar en el tiempo" a ese estado exacto de tu
código, garantizando la total reproducibilidad de tus experimentos.
Por lo tanto, Git no es una tarea administrativa; es una herramienta científica fundamental que
aporta credibilidad, rigor y transparencia a tu trabajo en Ciencia de Datos e Inteligencia Artificial.

Glosario y Autoevaluación de la Unidad


● Control de Versiones: Un sistema que registra los cambios realizados en el código fuente de
un proyecto a lo largo del tiempo, permitiendo revertir a versiones anteriores y gestionar el
historial.
● Git: Un sistema de control de versiones distribuido que rastrea cambios, permite revertirlos,
soporta el desarrollo en ramas y proporciona un historial completo.
● Repositorio: Un directorio o ubicación donde se almacena todo el código fuente de un
proyecto junto con su historial de cambios completo. Pueden ser locales (en su máquina) o
remotos (en la nube).
● Área de Trabajo (Working Directory): La copia de los archivos del proyecto en la que se
realizan las modificaciones.
● Área de Preparación (Staging Area / Index): Un área intermedia donde se seleccionan los
cambios del área de trabajo para incluirlos en la próxima confirmación (commit).
● Commit: Una "instantánea" de los cambios en el repositorio local en un momento dado,
acompañada de un mensaje descriptivo.
● Push: El comando de Git para subir los commits del repositorio local a un repositorio
remoto.
● Pull: El comando de Git para descargar los cambios más recientes de un repositorio remoto
al repositorio local.
● GitHub: Una popular plataforma web para alojar repositorios Git remotos, facilitando la
colaboración y la gestión de proyectos.
● Rama (Branch): Una línea independiente de desarrollo dentro de un repositorio Git,
permitiendo trabajar en nuevas funcionalidades o correcciones de forma aislada.
● Merge: El proceso de combinar los cambios de una rama a otra.
● Pull Request (PR): (Implícito en el uso de GitHub) Una propuesta para fusionar cambios de
una rama a otra, que permite la revisión de código y la discusión antes de la integración
final.
● Trazabilidad: La capacidad de seguir el historial completo de cambios y las razones detrás de
ellos en un proyecto de software.
● Reproducibilidad: En ciencia de datos, la capacidad de obtener los mismos resultados de un
análisis o modelo utilizando el mismo código, datos y configuraciones.
Autoevaluación Final de la Unidad (Cuestionario)
Seleccione la opción correcta, o complete la frase, y justifique su respuesta.
1. Pregunta: ¿Cuál de los siguientes comandos de Git se utiliza para preparar los cambios en el
área de trabajo antes de realizar una confirmación (commit)? a) git commit b) git push c) git
add d) git status
Respuesta: c) git add Justificación: El comando git add se utiliza específicamente para llevar los
cambios del área de trabajo al área de preparación (staging area), indicando a Git qué archivos
incluir en la próxima instantánea del proyecto.
2. Pregunta: Un ___________ es un sistema que registra y gestiona los cambios realizados en
el código fuente de un proyecto a lo largo del tiempo.
Respuesta: Sistema de Control de Versiones (SCV) Justificación: La definición proporcionada
describe un Sistema de Control de Versiones, el cual es una herramienta esencial para el
seguimiento y la gestión del historial de cambios en el desarrollo de software.
3. Pregunta (Verdadero/Falso): Una de las principales ventajas de usar Git y GitHub en
proyectos de Ciencia de Datos es que facilitan la reproducibilidad de los experimentos al
permitir el versionado del código y las configuraciones.
Respuesta: Verdadero. Justificación: Git y GitHub son herramientas cruciales para la
reproducibilidad en Ciencia de Datos, ya que permiten registrar los cambios en el código, los
parámetros del modelo, y las configuraciones, lo que es esencial para replicar y verificar resultados
de experimentos.
4. Pregunta: ¿Cuál de los siguientes no es un beneficio directo de la modularización y el uso de
funciones en programación? a) Reutilización del código. b) Mejora de la legibilidad del
código. c) Aumento de la complejidad de la lógica del programa. d) Organización del código
en partes más pequeñas.
Respuesta: c) Aumento de la complejidad de la lógica del programa. Justificación: La
modularización y el uso de funciones buscan precisamente lo contrario: reducir la complejidad
aparente al dividir el problema en partes manejables, organizar el código y facilitar su reutilización y
legibilidad. La lógica interna de cada módulo puede ser compleja, pero la abstracción que ofrecen
las funciones simplifica la comprensión general del programa.
5. Pregunta (Verdadero/Falso): En Python, las asignaciones de valores a variables (=) son
"mudas", lo que significa que no producen una salida por pantalla automáticamente.
Respuesta: Verdadero. Justificación: Como se menciona en el material, las asignaciones en Python
son "mudas"; para ver el valor de una variable después de una asignación, es necesario evaluarla
explícitamente, por ejemplo, usando la función print().
Conclusión
Hemos transitado un camino fascinante, desde los fundamentos más elementales de la
programación hasta la edificación de arquitecturas complejas y la comprensión de la interacción
con el mundo real a través de los datos y los algoritmos. Este "Apunte FINAL curso Python 2025" ha
buscado ser más que una compilación de conocimientos técnicos; ha sido una invitación constante
a reflexionar sobre el profundo impacto de la programación en nuestra sociedad y sobre la
responsabilidad ética que conlleva cada línea de código que escribimos.
Exploramos la esencia del pensamiento computacional, desentrañando cómo las máquinas logran
realizar tareas asombrosas a partir de la lógica binaria. Nos sumergimos en la claridad y versatilidad
de Python, comprendiendo por qué es la puerta de entrada ideal a este universo, y asimilamos la
importancia de un proceso de desarrollo estructurado, donde el análisis y el diseño preceden a la
codificación. Dotamos a nuestros programas de inteligencia a través de estructuras de control que
permiten tomar decisiones y automatizar tareas, y aprendimos a construir código limpio y
reutilizable mediante la modularización y el poder de las funciones. Finalmente, abordamos la
resiliencia en el manejo de errores y la persistencia de datos en archivos, culminando con una
inmersión en la Programación Orientada a Objetos, un paradigma que nos permite modelar el
mundo real con una coherencia y escalabilidad sin precedentes.
Pero la travesía no concluye aquí. La programación, especialmente en los campos de la Ciencia de
Datos y la Inteligencia Artificial, es un ecosistema en constante evolución. Los conceptos y
herramientas aquí presentados son cimientos sólidos sobre los cuales podrás edificar un
conocimiento cada vez más profundo y especializado. Te invitamos a mantener viva la curiosidad, a
seguir explorando nuevas bibliotecas, paradigmas y desafíos. Recuerda que la depuración de
errores es una oportunidad para aprender, y que cada problema resuelto fortalece tu pensamiento
lógico y tu capacidad de resiliencia.
Más allá de las habilidades técnicas, este camino te convoca a una vocación por la justicia, la
dignidad humana y el desarrollo integral. En un tiempo donde la tecnología moldea nuestras vidas,
tu rol como programador o científica de datos te posiciona en un lugar de privilegio para construir
soluciones que no solo sean eficientes, sino también éticas, inclusivas y empáticas. Que cada
programa que crees refleje una ética del cuidado y la escucha, y que tu ingenio se ponga al servicio
de un futuro más equitativo y humanizado.
¿Qué nuevas preguntas nos haremos y qué soluciones construiremos para los desafíos del mañana,
armados con la potencia del código y la profundidad de una mirada consciente? La respuesta está
en tus manos, en tu creatividad y en tu compromiso inquebrantable con el aprendizaje y la
transformación.
Los 25 recursos más significativos
A continuación, se enumeran los 25 recursos más significativos, aquellos que por su alcance,
profundidad o valor estratégico, constituyen pilares fundamentales para el desarrollo integral de un
científico de datos, priorizando la claridad y la accesibilidad de los enlaces:
I. Cimientos del Conocimiento: Plataformas Interactivas y Cursos Fundamentales
1. freeCodeCamp: Plataforma integral con certificaciones basadas en proyectos, ideal para el
aprendizaje autodidacta y disciplinado, aunque requiere una experimentación activa para
una comprensión profunda. https://www.freecodecamp.org/learn
2. Codecademy: Pionera en el aprendizaje interactivo, con un entorno integrado y rutas de
carrera estructuradas, perfecta para aprendices visuales y kinestésicos.
https://www.codecademy.com/
3. Kaggle Learn: Micro-cursos concisos y prácticos, ideales para aplicar la sintaxis a problemas
de datos reales, aunque no es para principiantes absolutos. https://www.kaggle.com/learn
4. Coursera (Especializaciones Universitarias): Puente entre el aprendizaje en línea y la
educación universitaria formal, con cursos rigurosos como "Python for Everybody" de la
Universidad de Michigan. https://www.coursera.org/
5. edX (Harvard & MIT): Plataforma de nivel universitario que ofrece un enfoque profundo en
los fundamentos de la informática, destacando "CS50's Introduction to Programming with
Python" de Harvard. https://www.edx.org/
6. W3Schools: El diccionario de referencia de sintaxis por excelencia, invaluable para consultas
rápidas y concisas sobre funciones y métodos específicos de Python.
https://www.w3schools.com/python/
II. El Aula Visual: Canales de YouTube y Tutoriales en Video
7. MoureDev by Brais Moure (En Español): Referente en la comunidad hispanohablante, con
un ecosistema de aprendizaje completo y enfocado en construir proyectos reales.
https://www.youtube.com/@mouredev
8. Píldoras Informáticas (En Español): Un canal exhaustivo y metódico, ideal para una
explicación minuciosa de cada concepto de Python, desde la instalación hasta temas
avanzados. https://www.youtube.com/@pildorasinformaticas
9. HolaMundo (En Español): Guía moderna y dinámica con alta calidad de producción,
enfocada en las mejores prácticas del desarrollo de software profesional.
https://www.youtube.com/@HolaMundoDev
10. Dot CSV (En Español): Fundamental para comprender profundamente el universo de la
Inteligencia Artificial y la Ciencia de Datos desde una perspectiva teórica y conceptual.
https://www.youtube.com/@DotCSV
11. freeCodeCamp (Canal de YouTube - En Inglés): Cursos completos y masivos en videos
únicos, ideales para una inmersión total en un lenguaje o tecnología.
https://www.youtube.com/@freecodecamp
12. Corey Schafer (En Inglés): Altamente respetado por la claridad y calidad de sus tutoriales,
perfecto para superar los conceptos básicos y avanzar a niveles intermedios y avanzados.
https://www.youtube.com/@coreyms
13. Programming with Mosh (En Inglés): Sinónimo de alta calidad y profesionalismo, sus
tutoriales son impecables y descomponen conceptos complejos de forma accesible.
https://www.youtube.com/@programmingwithmosh
14. CS Dojo (En Inglés): Aporta una perspectiva valiosa al explicar conceptos fundamentales de
la ciencia de la computación como estructuras de datos y algoritmos.
https://www.youtube.com/@CSDojo
III. Conocimiento en Movimiento: Podcasts Esenciales de Python y Datos
15. Python en Español (Podcast Bit a bit - En Español): El podcast oficial de la comunidad
"Python en Español", ideal para conectar con el ecosistema hispanohablante y conocer a sus
protagonistas. https://hablemospython.dev/podcast.html
16. Talk Python To Me (En Inglés): Posiblemente el podcast de entrevistas más influyente,
ofreciendo acceso directo a la "historia detrás del código" de las librerías más importantes.
https://talkpython.fm/
17. Python Bytes (En Inglés): Un resumen semanal conciso de las noticias más importantes del
mundo Python, ideal para mantenerse actualizado sin sobrecarga de información.
https://pythonbytes.fm/
18. Data Skeptic (En Inglés): Podcast longevo y respetado en ciencia de datos e IA, con
episodios mini que explican conceptos fundamentales y entrevistas en profundidad.
https://dataskeptic.com/
IV. Lectura Profunda: Blogs y Publicaciones de Referencia
19. Real Python: Considerado el "estándar de oro" de los tutoriales de Python en línea, con
contenido de calidad, profundidad y claridad excepcionales. https://realpython.com/
20. Towards Data Science: La publicación de facto de la comunidad de ciencia de datos, un
espacio donde profesionales e investigadores comparten conocimientos y análisis.
https://towardsdatascience.com/
21. KDnuggets: Portal líder de noticias, análisis y opinión sobre la industria de la IA, Analytics,
Big Data y Ciencia de Datos, esencial para el contexto empresarial.
https://www.kdnuggets.com/
22. Full Stack Python: Un libro de código abierto que actúa como guía completa para construir,
desplegar y operar aplicaciones web con Python. https://www.fullstackpython.com/
23. DataCamp Blog (en español): Uno de los recursos de mayor calidad en español para el
aprendizaje de la ciencia de datos, con tutoriales y guías de valor excepcional.
https://www.datacamp.com/es/blog
V. La Fuente de la Verdad: Documentación Oficial y Guías de Estilo
24. Documentación Oficial de Python (en español): La fuente definitiva, precisa y actualizada
para el lenguaje Python, crucial para distinguir a un aficionado de un profesional.
https://docs.python.org/es/3/
25. PEP 8 - Guía de Estilo para el Código Python: El ADN del código Python profesional,
estableciendo las convenciones para escribir código limpio, consistente y legible.
https://peps.python.org/pep-0008/
Cada uno de estos recursos, en su singularidad, contribuye a la construcción de un conocimiento
sólido y aplicable. La elección y el orden en que se aborden, sin embargo, dependen de la vocación
y el momento existencial de cada aprendiz. ¿Cómo se entrelazan estos senderos del saber para
forjar una comprensión que no solo sea técnica, sino también éticamente comprometida con la
dignidad humana y el desarrollo integral en la era de la inteligencia artificial? Es una pregunta que
nos invita a la reflexión continua y a la acción transformadora.

También podría gustarte