Programación en Python.
Conceptos
Introducción
En nuestro día a día, a menudo nos enfrentamos a situaciones que demandan soluciones creativas y efectivas. Ya sea en la academia, en el trabajo o en nuestra vida personal, saber abordar un problema de manera estructurada es una habilidad invaluable.
¿Qué es un problema?
Imagina que te encuentras en un punto de partida (el estado inicial) y quieres llegar a un objetivo concreto (el estado deseado). Entre estos dos puntos, hay obstáculos o limitaciones (restricciones) que debes superar. Aquí es donde entra en juego la resolución de problemas, un proceso que transforma esa situación inicial en el resultado deseado mediante pasos claros y bien definidos.
El poder de los algoritmos
Los algoritmos son nuestros aliados perfectos en este viaje. Son conjuntos de instrucciones ordenadas y precisas diseñadas para resolver problemas específicos. Estas son sus características esenciales:
- Finitud: Un algoritmo siempre tiene un número finito de pasos.
- Claridad: Cada paso está claramente especificado, sin lugar a dudas.
- Entradas y salidas: Pueden manejar datos de entrada para producir resultados.
- Efectividad: Los pasos son realizables y prácticos.
Consideremos el problema de ordenar una lista de números de menor a mayor.
- Estado inicial: Una lista desordenada de números.
- Estado deseado: La misma lista de números, pero ordenada de menor a mayor.
Para lograr esto, podríamos utilizar el Algoritmo de Ordenamiento por Burbuja:
- Compara los primeros dos números de la lista.
- Si el primer número es mayor que el segundo, intercámbialos.
- Avanza a los siguientes dos números.
- Repite los pasos hasta que no se necesiten más intercambios.
Una vez que tenemos el algoritmo, podemos implementarlo en alguna forma de ejecución práctica o en un lenguaje de programación como Python.
Tipos de Datos
En el universo de la programación, cada dato es una pieza clave en el rompecabezas de algoritmos y soluciones.
Los datos pueden ser clasificados siguiendo diferentes criterios:
- Mutabilidad
- Mutables: su contenido puede ser modificado.
- Inmutables: su contenido no puede ser modificado.
- Orden
- Ordenada: estructuras en las que es posible acceder a un elemento específico dada su posición
- Desordenada: estructuras en las que no es posible acceder a un elemento específico ya que no contamos con su posición.
- Complejidad y estructura:
- Simples o primitivos: no pueden ser descompuestos.
- Estructuras de datos: contienen múltiples elementos, incluidos otros tipos de datos.
Principales tipos de datos primitivos:
int: representa los números enteros. Estos incluyen todos los números positivos y negativos, además del cero. Es inmutable.

float: representa los números de punto flotante, es decir, números que contienen una parte decimal. Estos incluyen todos los números positivos y negativos. Es inmutable.

str: representa una cadena de caracteres (string) encerrada entre comillas simples (') o dobles ("). Es inmutable.

bool: representa los valores booleanos (verdadero y falso), fundamentales para la lógica de programación. Es inmutable.

None: representa la ausencia de un valor o un valor nulo (NoneType). Es inmutable.

Principales tipos de estructuras de datos:
list: representa listas o colecciones ordenadas y mutables de elementos de cualquier tipo. Los elementos deben estar encerrados entre corchetes ([]) y separados por comas (,).

tuple: representa tuplas o colecciones ordenadas e inmutables de elementos de cualquier tipo. Los elementos deben estar encerrados entre paréntesis (()) y separados por comas (,).

dict: representa diccionarios o colecciones desordenadas y mutables de pares clave-valor. Las claves deben ser únicas, mientras que los valores pueden ser de cualquier tipo. Los pares deben estar encerrados entre llaves ({}) y separados por comas (,). Dentro de cada par, la llave debe ser separada del valor por dos puntos (:).

set: representa conjuntos o colecciones desordenadas y mutables de elementos únicos. Los elementos deben estar encerrados entre llaves ({}) y separados por comas (,).

Variables
En los lenguajes de programación, una variable es un contenedor, o caja, que nos permite guardar y recuperar información valiosa, permitiendo acceder a ella en cualquier momento con facilidad.
Características fundamentales
- Valor: Información almacenada en la caja, es lo que se guarda en la memoria del ordenador.
- Espacio en memoria: Cada variable tiene su propio lugar especial en la memoria del ordenador. Dependiendo del tipo de dato que almacena, se le asigna un espacio específico.
- Nombre o identificador: El nombre de una variable es su distintivo único con el que puedes acceder y manipular el valor almacenado con facilidad.
Imagina que necesitas almacenar en tu bodega dos tomos de un libro de Matemáticas, ocho tomos de un libro de Programación y dos lápices.
Si guardas todos estos objetos en una sola caja, cada vez que necesites acceder a uno tendrías que revisar todo el contenido. Sin embargo, si organizas cada tipo de objeto en cajas separadas y debidamente etiquetadas, podrías encontrar lo que buscas directamente en la caja correspondiente.
Además, utilizar cajas del mismo tamaño para todo podría resultar en un desperdicio de espacio. Por ello, es más eficiente usar cajas que se ajusten al tamaño de los objetos que contienen.

En este escenario, nombraríamos nuestras variables como box1, box2, y box3. Los valores correspondientes serían libros de Matemáticas en box1, lápices en box2, y libros de Programación en box3. Así, si necesitamos acceder a los lápices, simplemente revisaríamos directamente en box2.
Asignación
En Python, el proceso de asignación de valores a una variable se realiza utilizando el operador igual (=). Este operador actúa como un puente que toma el valor a su derecha y lo deposita en el nombre de la variable a su izquierda.
Regresando al ejemplo de almacenaje en la bodega, para el caso box1 tendríamos:

Ahora trasladémonos a Python!
Supongamos que queremos almacenar distintos tipos de datos en variables:
- El número 5 como un valor entero en la variable llamada
entero. - El número 3.5 como un valor real en la variable llamada
real. - La cadena de caracteres «casa» en la variable llamada
lugar.

Es importante destacar que el proceso de asignación sobreescribe los valores que anteriormente fueron asignados a una variable, incluyendo el tipo de dato. Si vamos nuevamente a la bodega, no podemos guardar dos tipos de cosas en una caja.
Regresando al ejemplo de almacenaje en la bodega, supongamos que ayer guardamos los libros de Matemática en box1:

Pero hoy en la mañana decidimos almacenar en box1 un par de zapatos, por tanto:

Si en este momento accedmos al contenido de box1 en este momento, solo encontraremos los zapatos, ya que no podemos guardar zapatos y libros juntos en la misma caja, a menos que estén agrupados de alguna manera específica, lo cual no ocurre en este escenario.

Ahora trasladémonos a Python!
Supongamos que realizamos las siguientes asignaciones a la variable llamada contenido:
- El número 5 como un valor entero.
- El número 3.5 como un valor real.
- La cadena de caracteres «casa».

Al acceder a la variable contenido luego de las asignaciones realizadas, su valor almacenado será la cadena de caracteres «casa».
Alcance
- Variables Locales: Puedes pensar en una variable local como una computadora de escritorio que tienes en tu habitación. Esta computadora solo se puede usar cuando estás en la habitación. De la misma manera, en Python, una variable local solo puede ser usada en el lugar donde fue creada, lo cual limita su acceso solo a ese «espacio» específico.
- Variables Globales: En contraste, una variable global es como una laptop que puedes llevar contigo a cualquier parte de la casa. Esto significa que puedes usarla en tu habitación, en la sala, en la cocina, donde sea que estés. En Python, una variable global puede ser utilizada en cualquier parte del programa, no importa dónde la hayas creado. Es accesible desde cualquier «espacio» dentro del programa.
Variable local

Variable global

Reglas para nombrar las variables
- Deben comenzar con una letra (a-z, A-Z) o un guión bajo (_).
- No pueden comenzar con un número.
- No pueden ser palabras clave reservadas de Python, por ejemplo,
for,if,while, etc..
Sugerencias para nombrar las variables
- Deben describir el proposito de la variable. Ejemplo: utilizar
edad = 20en lugar dex = 20. - Deben componerse de minúsculas, con las palabras separadas por guiones bajos.
Una mirada al futuro … 
Una función es un bloque de código reutilizable que realiza una tarea específica y puede recibir argumentos y devolver valores.
Este concepto lo estudiaremos en profundidad más adelante, pero por ahora veremos una función como una caja negra a la que le pasamos argumentos o información de entrada y nos devuelve valores de salida. Tanto la entrada como la salida tienen un formato y tipo de datos que se debe respetar.
Imaginemos que queremos tomar jugo y le pasamos tozos de frutas a una licuadora.
![]()
![]()
![]()
![]()
![]()
El formato e entrada son trozos de frutas y el formato de salida es jugo de frutas. Además, dependiendo del tipo de fruta que pasamos será el tipo de jugo que recibiremos.
Entrada y salida de datos
Python nos ofrece herramientas poderosas para lograr una comunicación fluida a través de la entrada y salida de datos.
Entrada de datos
Utilizaremos la función input() para capturar lo que el usuario escribe.
- Argumentos: La función
input()puede recibir como argumento opcional, una cadena de texto (prompt) que se muestra al usuario. - Valor de Retorno: Luego de que el usuario ingresa su respuesta, la función
nos devuelve ese valor como una cadena de caracteres.input()
input() puede recibir solo un argumento y su formato de entrada es str. Dado esto si necesitas combinar varios textos o variables en el mensaje debes hacerlo antes de pasar el mensaje a input()
input() siempre retorna un valor que debemos almacenar para su posterior uso o podemos utilizarlo en la linea donde lo usaremos directamene pero esto puede complicar el entendimiento del codifgo
input() siempre retorna un valor con formato de salida str. Dado esto, aunque el usuario inserte un número, nosotros recibiremos ese número como una cadena de caracteres, o sea un str.
Salida de datos
Utilizaremos la función print() para mostrar resultados y mensajes al usuario.
- Argumentos: La función
print()puede recibir múltiples argumentos separados por comas (,). Cada uno de estos puede ser de cualquier tipo de dato. Internamente, la función convierte cada dato a una cadena de caracteres (str) y los muestra al usuario, insertando automáticamente un espacio entre cada argumento. - Valor de Retorno: La función
print()no devuelve ningún valor.
Si necesitas mostrar varios datos juntos sin espacios entre ellos, puedes realizar las operaciones de conversión y combinación antes de pasar los datos al print() o directamente en la llamada a la función. Esta última opción puede complicarse si manejas muchos datos, resultando en líneas de código extensas y menos legibles.
Imaginemos que queremos pedirle al usuario su nombre, edad y altura (m), para luego mostrarle un mensaje que muestre sus datos en una oración respetando el siguiente formato:
- nombre tiene edad años y su altura es de altura metros.’


Siguiendo los puntos mencionados anteriormente:
- A la función input le pasamos solo un str como argumento.
- Asignamos cada valor retornado por cada llamado a la función input a una variable diferente.
- A la función print() le pasamos varios argumentos separados por coma y esta los unificó en un str mostrando un espacio entre cada uno de ellos.
- Al ejecutar el código dos veces, la salida mostrada al usuario dependió de la entrada de datos.
Secuencias de escape
En Python existen ciertos caracteres especiales que se utilizan para representar operaciones o funciones que no son directamente representables en texto. Estos caracteres se denominan «secuencias de escape» y comienzan con una barra invertida (\).
Este concepto lo estudiaremos en profundidad más adelante, pero por ahora veremos una función como una caja negra a la que le pasamos argumentos o información de entrada y nos devuelve valores de salida. Tanto la entrada como la salida tienen un formato y tipo de datos que se debe respetar.
Salto de línea (\\n)
Se utiliza para insertar una nueva línea en un texto. En el contexto de un archivo o una salida en la consola, el cursor se moverá al inicio de la línea siguiente.

Tabulación horizontal (\\t )
Se utiliza para insertar un carácter de tabulación horizontal (Tab) en un texto, lo que normalmente equivale a un espacio en blanco de varios caracteres de ancho (comúnmente 4 u 8 espacios, dependiendo del entorno).

Barra invertida (\\)
Como la barra invertida se usa para iniciar secuencias de escape, debes usar dos barras invertidas si deseas incluir una barra invertida real en tu texto.

Comilla simple (\')
Se usa para insertar una comilla simple en cadenas delimitadas por comillas simples.

Comilla doble (\")
Se usa para insertar una comilla doble en cadenas delimitadas por comillas dobles.

Operadores
Python ofrece una amplia gama de operadores, clasificados en varias categorías según su función. En esta sección, exploraremos los operadores más importantes aplicables a los tipos de datos primitivos.
Booleanos: En Python, True se comporta como el número 1, y False como el número 0 cuando se usan en contextos numéricos.
Operadores aritméticos
Suma (+)
| Operación | Resultado | Descripción |
| int + int | int | Suma de dos enteros. |
| float + float | float | Suma de dos flotantes. |
| int + float | float | Suma de un entero y un flotante. |
| bool + bool | int | Suma de dos booleanos como enteros (True es 1, False es 0). |
| int + bool | int | Suma de un entero y un booleano. |
| float + bool | float | Suma de un flotante y un booleano. |
| str + str | str | Concatenación de dos cadenas. |

Cuando se suman dos str, Python concatena el segundo al final del primero sin añadir espacio entre ambos.
Python no permite, por defecto, algunas combinaciones de tipos de datos sin una conversión explícita, como sumar directamente una cadena aunque esta contenga un número. Esto provocará un TypeError.
Resta (-)
| Operación | Resultado | Descripción |
| int - int | int | Resta de dos enteros. |
| float - float | float | Resta de dos flotantes. |
| int - float | float | Resta de un entero y un flotante. |
| bool - bool | int | Resta de dos booleanos como enteros (True es 1, False es 0). |
| int - bool | int | Resta de un entero y un booleano. |
| float - bool | float | Resta de un flotante y un booleano. |

Python no permite, por defecto, algunas combinaciones de tipos de datos sin una conversión explícita, como restar directamente una cadena aunque esta contenga un número. Esto provocará un TypeError.
Multiplicación (*)
| Operación | Resultado | Descripción |
| int * int | int | Multiplicación de dos enteros. |
| float * float | float | Multiplicación de dos flotantes. |
| int * float | float | Multiplicación de un entero y un flotante. |
| bool * bool | int | Multiplicación de dos booleanos como enteros (True es 1, False es 0). |
| int * bool | int | Multiplicación de un entero y un booleano. |
| float * bool | float | Multiplicación de un flotante y un booleano. |
| str * int | str | Repetición de una cadena un número entero de veces. |

Python permite multiplicar cadenas de caracteres por enteros, lo que resulta en la repetición de la cadena. Sin embargo, intentar multiplicar una cadena por un número no entero (como un flotante) o un booleano generará un TypeError.
División (/)
El resultado de una división siempre es un número de tipo float.

Al intentar dividir por cero lanzará un ZeroDivisionError.
División entera (//)
| Operación | Resultado | Descripción |
| int // int | int | División entera de dos enteros. |
| float // float | float | División entera de dos flotantes. |
| int // float | float | División entera de un entero y un flotante. |
| bool // bool | int | División entera de dos booleanos como enteros (True es 1, False es 0). |
| int // bool | int | División entera de un entero y un booleano. |
| float // bool | float | División entera de un flotante y un booleano. |

La división entera descarta la parte fraccionaria y retorna el resultado como un entero si ambos operandos son enteros. Si uno de los operandos es un flotante, el resultado será también un flotante, pero sin parte fraccionaria.
La división entera siempre redondea el resultado hacia abajo hacia el número entero más cercano. Esto es importante en casos negativos.
Módulo (%)
| Operación | Resultado | Descripción |
| int % int | int | Resto de la división de dos enteros. |
| float % float | float | Resto de la división de dos flotantes. |
| int % float | float | Resto de la división de un entero y un flotante. |
| bool % bool | int | Resto de la división de dos booleanos como enteros (True es 1, False es 0). |
| int % bool | int | Resto de la división de un entero y un booleano. |
| float % bool | float | Resto de la división de un flotante y un booleano. |

Al igual que con la división, intentar obtener el módulo por cero (como en x % 0) resultará en un ZeroDivisionError.
El signo del resultado del módulo sigue el signo del divisor, lo cual es importante recordar al trabajar con números negativos.
Exponente (**)
| Operación | Resultado | Descripción |
| int ** int | int | Un entero elevado a la potencia de otro entero. |
| float ** float | float | Un flotante elevado a la potencia de otro flotante. |
| int ** float | float | Un entero elevado a la potencia de un flotante. |
| bool ** bool | int | Un booleano elevado a la potencia de otro booleano. |
| int ** bool | int | Un entero elevado a la potencia de un booleano. |
| float ** bool | float | Un flotante elevado a la potencia de un booleano. |

Utilizar un exponente negativo resulta en el inverso del número elevado a la potencia positiva correspondiente.
Utilizar un exponente negativo resulta en el inverso del número elevado a la potencia positiva correspondiente.
Cualquier número elevado a False (0) es 1, y False (0) elevado a cualquier número es 0, excepto False ** False que es 1.
Operadores de comparación
| Operador | Descripción |
| == | Verifica si los valores de dos operandos son iguales. |
| != | Verifica si los valores de dos operandos no son iguales. |
| > | Verifica si el valor del operando izquierdo es mayor que el del operando derecho. |
| < | Verifica si el valor del operando izquierdo es menor que el del operando derecho. |
| >= | Verifica si el valor del operando izquierdo es mayor o igual al del operando derecho. |
| <= | Verifica si el valor del operando izquierdo es menor o igual al del operando derecho. |
Todos los operadores de comparación devuelven un valor booleano True o False sin importar entre qué tipos de datos se realice la comparación.
La comparación de cadenas se realiza alfabéticamente, carácter por carácter basándose en el orden de los caracteres ASCII.
Los booleanos True y False se pueden comparar, teniendo en cuenta que True se comporta como 1 y False como 0.
Python puede realizar la conversión entre int y float porque tienen una base numérica común, pero la comparación entre int y str, por ejemplo, no tiene una base semántica clara y significativa en la mayoría de los contextos.
Con base en el punto anterior, cuando intentas usar <, >, <=, o >= entre tipos incompatibles, int y str, por ejemplo, Python lanza un TypeError. Sin embargo, cuando usas == o != Python no lanza un TypeError pero el resultado es False. Por ejemplo, '5' == 5 entregará como resultado False.
Considera la siguiente asignación de variables para cada ejemplo relacionado a los operadores de comparación.

Operadores de asignación
| Operador | Descripción |
| = | Asigna un valor a una variable. |
| += | Suma el valor dado a la variable (incremento). |
| -= | Resta el valor dado de la variable (decremento). |
| *= | Multiplica la variable por el valor dado. |
| /= | Divide la variable por el valor dado. |
| %= | Asigna el resto de la división de la variable. |
| **= | Eleva la variable a la potencia del valor dado. |
| //= | Divide entero la variable por el valor dado. |
Los operadores de asignación se utilizan para asignar valores a las variables, facilitando la modificación de valores de variables sin repetir el nombre de la variable varias veces.

Operadores lógicos
| Operador | Descripción |
| and | Devuelve True si ambos operandos son verdaderos. |
| or | Devuelve True si al menos uno de los operandos es verdadero. |
| not | Invierte el valor del operando. |
Los operadores lógicos se utilizan para combinar declaraciones condicionales.
and solo evalúa el segundo argumento si el primero es True; or solo evalúa el segundo argumento si el primero es False.
Aunque estos operadores están destinados a ser usados con booleanos, Python permite su uso con cualquier tipo, donde 0, None, [], {}, y «» se consideran False y casi cualquier otro valor se considera True.

Operadores de pertenencia
| Operador | Descripción |
| in | Verifica si un valor se encuentra dentro de una secuencia. |
| not in | Verifica si un valor no se encuentra dentro de una secuencia. |
El rendimiento de in y not in puede variar significativamente entre diferentes tipos de datos.
in se puede usar para verificar subcadenas dentro de una cadena más grande, lo que lo hace extremadamente útil en procesamiento de texto.
Es importante recordar que la pertenencia es literal. Por ejemplo, 2 in [2.0] devuelve False porque 2 es un entero y 2.0 es un flotante, y son considerados diferentes.

Qué pasaría sí … ? 
No le pasamos argumentos a la función input().
No arrojará error, solamente el usuario debe conocer en qué orden deben ser gestionados los datos.

En muchos ejercicios y jurados online la entrada de datos no requiere que le pasemos el argumento a la función input().
No almacenamos en variables los datos que recibimos de cada llamado a la función input().
No arrojará error, lo que sucederá es que perderemos la información suministrada por el usuario.

No podríamos pasarle la información a la función print(), ya que no la almacenamos para su posterior uso.
Tratamos de acceder al valor de una variable entes de declararla e inicializarla.
Sí arrojará error ya que en la línea 1 estamos llamando a varias variables que no han sido creadas

La ejecución de este código no pasará de la línea 1.
Asignamos cada llamado a la función input() a una única variable.
No arrojará error, lo que sucederá es que la variable dato será reasiganada en cada llamada de la función input() por lo que al pasar esta variable como argumento a la función print() solo comtendrá como valor, la última asignación realizada, en este caso, la altura.

Una variable no puede almacenar dos datos de diferentes asignaciones.