Operadores y Expresiones

¿Qué es un Operador?

Un operador es un símbolo reservado del lenguaje de programación que tiene la capacidad de realizar una acción o cálculo específico sobre uno o más valores (datos).

El ejemplo más intuitivo es el signo + (más), el cual, exactamente igual que en la aritmética tradicional, toma dos números a sus lados, los suma y devuelve el resultado de dicha adición. Los valores sobre los cuales actúa un operador reciben el nombre técnico de operandos.

  • + (Suma)
  • - (Resta)
  • * (Multiplicación)
  • / (División tradicional)
  • // (División de piso o entera)
  • % (Módulo o residuo)
  • **(Exponenciación o potencia)

El orden en el que aparecen listados estos operadores no es casualidad; responde a sus reglas jerárquicas de prioridad.

El Concepto de Expresión

Para comunicarte con Python de forma efectiva, necesitas estructurar tus instrucciones en forma de expresiones. Una expresión es la combinación e interconexión de datos (literales o variables) y operadores. Al ser evaluada por el intérprete, una expresión siempre se reduce a un único valor final.

Bajo esta regla, debes saber que el literal más simple por sí solo ya constituye una expresión (por ejemplo, el número 5 es una expresión cuyo valor es 5). Si conectas varios elementos mediante operadores (5 + 3 * 2), estás construyendo una expresión compleja que Python resolverá paso a paso siguiendo las leyes de la matemática.

Resumen de Terminología

  • Operador: El símbolo que ejecuta la acción (+, -, *).
  • Operando: El dato que sufre la acción del operador.
  • Expresión: El conjunto unificado de operandos y operadores (2 + 2).

Aquí tienes la adaptación de esta sección, enfocada en el operador de exponenciación (), introduciendo una de las reglas de tipado más importantes para el examen de certificación PCEP: cómo interactúan los tipos de datos en una operación aritmética.

La Exponenciación

El símbolo (doble asterisco) es el operador de exponenciación o potencia en Python. Su argumento de la izquierda actúa como la base, mientras que el argumento de la derecha es el exponente.

La matemática clásica prefiere la notación con superíndices (como \(2^3\)). Dado que los editores de texto plano no permiten escribir caracteres elevados de forma nativa, Python adoptó el doble asterisco para lograr el mismo efecto matemático:

print(2 ** 3)

La Regla de Propagación de Tipos

Si ejecutas diferentes combinaciones de números utilizando la exponenciación, notarás un comportamiento sumamente regular y predecible en las salidas de la consola. Este experimento nos permite formular la regla de oro de los operadores numéricos en Python:

  • Entero puro: Cuando ambos argumentos del operador son números enteros (int), el resultado final será obligatoriamente otro número entero.
  • Contaminación por flotante: Cuando al menos uno de los argumentos del operador es un número de punto flotante (float), el resultado final se convertirá automáticamente en un flotante, sin importar que el resultado matemático sea exacto.

Aquí tienes la adaptación de estas dos secciones fundamentales. Analizaremos el operador de multiplicación y entraremos de lleno en la primera gran excepción del tipado aritmético en Python: la división tradicional.

Multiplicación (*)

El símbolo * (asterisco de forma individual) es el operador de multiplicación. Pongamos a prueba el bloque de código sugerido por el curso para comprobar si la regla de propagación de tipos que aprendimos con la exponenciación se sigue manteniendo:

La regla funciona a la perfección y de manera idéntica. Multiplicar enteros puros (int * int) produce un entero (6). En el momento en que introduces un punto decimal en cualquiera de los operandos, el resultado se convierte automáticamente en un flotante (6.0).

División Tradicional (/)

El símbolo / (barra diagonal o slash) representa el operador de división estándar. El valor situado a la izquierda es el dividendo y el de la derecha es el divisor.

  • El resultado producido por el operador de división tradicional (/) SIEMPRE, sin excepción, será un número de punto flotante (float).

¿Por qué esto representa un problema?

En el desarrollo de software (y especialmente en algoritmos de ciencia de datos, machine learning o control de bucles), existen situaciones críticas donde necesitas estrictamente un número entero como resultado de una división (por ejemplo, si estás calculando el índice de una lista o una posición en una matriz). Un número con punto decimal provocaría un error de sintaxis en esos contextos.

División Entera (//)

Para solucionar la necesidad de obtener resultados numéricos sin decimales, Python incorpora el operador // (doble barra diagonal). Este operador se conoce formalmente como división entera o, de manera más precisa en ciencias de la computación, división de piso (floor division). Este difiere de la división estándar en dos características sintácticas y semánticas cruciales:

  1. Elimina la parte fraccionaria: El resultado carece por completo de decimales (si operas enteros) o su parte decimal es forzada a ser siempre cero (si operas flotantes).
  2. Cumple estrictamente la regla de propagación de tipos (int vs float).

La Regla del “Piso”: Redondeo hacia el Entero Menor

Llevemos el análisis a un nivel más avanzado. ¿Qué ocurre cuando el resultado matemático real no es exacto?

print(6 // 4)
print(6. // 4)

Si estuviéramos utilizando la división tradicional (/), el resultado matemático exacto sería 1.5. Sin embargo, al ejecutar este código con //, obtenemos:

1
1.0

Regla Crítica de Redondeo (PCEP)

El resultado de la división de piso siempre se redondea al número entero más cercano que sea MENOR o IGUAL al resultado real (no redondeado). No se trata de un truncamiento simple, sino de un desplazamiento hacia la izquierda en la recta numérica (hacia el valor más pequeño).

  • El resultado real es 1.5.
  • El entero menor más cercano a 1.5 es 1. Por lo tanto, el resultado es 1 (o 1.0 si hay un float).

El Peligro de los Números Negativos

Donde la mayoría de los estudiantes comete errores en el examen PCEP es al aplicar la división de piso con operandos negativos. Intenta predecir el resultado del siguiente bloque:

print(-6 // 4)
print(6. // -4)

A primera vista, podrías pensar: “Si en el caso anterior dio 1, aquí debería dar -1”. Vamos a ejecutarlo y observar la consola:

-2
-2.0

¿Por qué da -2 en lugar de -1?

Apliquemos estrictamente la regla del piso matemática:

  1. El resultado matemático real (exacto) de -6 / 4 es -1.5.
  2. La regla exige redondear hacia el entero menor en la recta numérica (moverse hacia la izquierda).
  3. En el universo de los números negativos, -2 es menor que -1 (está más alejado del cero).

Por esta razón, Python redondea hacia abajo, alcanzando el -2 (o -2.0).

Resumen

  • Definición: // calcula la división y aplica la función suelo (floor).
  • Positivos: 6 // 4 da 1 (el entero menor cercano a 1.5).
  • Negativos: -6 // 4 da -2 (el entero menor cercano a -1.5).
  • Tipado: int // int = int. Si hay algún float involucrado, el resultado será float (ej. 1.0 o -2.0).

Aquí tienes la adaptación de la sección sobre el operador de módulo o residuo (%) y las restricciones absolutas de la división en Python. Este es un concepto de gran peso en la lógica de programación y un blanco frecuente de preguntas en el PCEP.

El Residuo o Módulo (%)

El siguiente operador es bastante peculiar porque no tiene un símbolo equivalente directo en la aritmética tradicional de la escuela. En Python se representa con el signo de porcentaje (%).

Para que no te confunda su diseño, un buen truco visual es imaginarlo como la barra de la división (/) acompañada por dos pequeños círculos. Su función es crucial: devolver el residuo (lo que sobra) de una división entera.

En otras palabras, es el valor que queda remanente tras intentar encajar un número entero dentro de otro tantas veces como sea posible. En muchos otros lenguajes de programación se le conoce formalmente con el nombre de módulo.

Anatomía del Cálculo del Módulo

Analicemos el primer ejemplo básico propuesto por el curso. Intenta predecir el resultado antes de mirar la explicación:

print(14 % 4)

Al ejecutarlo, la consola nos devuelve un 2. ¿Por qué? El proceso matemático interno que sigue Python se divide en tres pasos automáticos:

  1. Realiza una división de piso para hallar el cociente entero: 14 // 4 da 3 (el 4 cabe 3 veces enteras en el 14).
  2. Multiplica ese cociente por el divisor original: 3 * 4 da 12.
  3. Resta ese valor intermedio al dividendo original para hallar el residuo: 14 - 12 da 2.

Elevando la Dificultad: Módulo con Flotantes

El operador % también cumple de forma estricta la regla de propagación de tipos (int vs float). Observa este caso más complejo:

print(12 % 4.5)

¿Cuál es el resultado?

Si hacemos el mismo desglose algorítmico:

  1. 12 // 4.5 da como resultado 2.0 (el 4.5 cabe dos veces enteras en el 12, acumulando 9.0).
  2. Multiplicamos el cociente por el divisor: 2.0 * 4.5 nos da 9.0.
  3. Restamos el dividendo original menos el resultado intermedio: 12 - 9.0 da 3.0.

Dado que uno de los operandos era un flotante (4.5), todo el resultado se “contamina”, devolviendo en la consola 3.0 en lugar de un 3 entero.

Operadores: Cómo NO debes dividir (Límites de la Máquina)

Existe una regla matemática universal que las computadoras defienden de forma agresiva debido a limitaciones físicas en la unidad de procesamiento aritmético: la división por cero es imposible.

En tus programas de Python, jamás debes intentar:

  • Realizar una división tradicional por cero (6 / 0).
  • Realizar una división de piso o entera por cero (6 // 0).
  • Encontrar el residuo de una división por cero (6 % 0).

Si el intérprete de Python topa con cualquiera de estas tres operaciones durante la ejecución de tu script, detendrá el programa de golpe y arrojará un error en tiempo de ejecución muy específico: ZeroDivisionError: integer division or modulo by zero.

Resumen

  • % extrae únicamente el residuo de una división.
  • Sigue las reglas de tipado: int % int da int. Si hay un float, devuelve float.
  • Cualquier variante de división donde el divisor sea 0 provocará un colapso del programa (ZeroDivisionError).

Aquí tienes la adaptación de esta sección, donde cerramos el catálogo de operadores básicos con la suma y la resta, e introducimos una distinción conceptual teórica que es pregunta de examen obligatoria en el PCEP: la diferencia entre operadores unarios y binarios.

El Operador de Suma (+)

El signo más (+) actúa como el operador de adición propagando el punto flotante en caso de que haya.

print(-4 + 4)
print(-4. + 8)
0
4.0
  • En la primera línea, int + int produce el entero 0.
  • En la segunda línea, la presencia del punto decimal en el -4. propaga el tipo flotante, devolviendo 4.0.

Operadores Binarios vs. Operadores Unarios

El signo menos (-) tiene una naturaleza dual en programación. Puede actuar de dos formas completamente distintas dependiendo de cuántos argumentos (operandos) lo acompañen. Esto nos permite clasificar los operadores en dos familias:

Operadores Binarios (Requieren DOS operandos)

Cuando utilizas el signo - para realizar una resta tradicional, el operador espera obligatoriamente dos argumentos: uno a la izquierda (el minuendo) y otro a la derecha (el sustraendo). Por esta razón, la resta es un operador binario, exactamente igual que la suma (+), la multiplicación (*) y la división (/).

Operadores Unarios (Requieren UN solo operando)

El signo menos también puede utilizarse para cambiar o invertir el signo de un número. Cuando escribes -4, el operador no está restando nada; simplemente está modificando la polaridad del operando que tiene a su derecha. Al actuar sobre un único argumento, se le considera un operador unario.

Aquí tienes la adaptación de esta sección fundamental sobre la prioridad (precedencia) y asociatividad (bindings) de los operadores. Estos conceptos matemáticos abstractos determinan el orden exacto en el que Python desglosa expresiones complejas y son una fuente garantizada de preguntas capciosas en el examen PCEP.

Jerarquía de Operadores: Prioridad y Asociatividad

Hasta ahora hemos analizado cada operador de forma aislada, como si no tuvieran relación entre sí. Sin embargo, en la programación real, lo habitual es encontrar expresiones complejas donde conviven múltiples operadores al mismo tiempo.

El fenómeno que provoca que algunos operadores actúen antes que otros se conoce formalmente como la jerarquía de prioridades o precedencia de operadores. Python define con precisión matemática el nivel de prioridad de cada símbolo, asegurando que los operadores con mayor prioridad tomen el control de los operandos antes que los de menor prioridad.

Asociatividad de Operadores (Bindings)

¿Qué sucede cuando en una misma expresión se encuentran dos operadores pegados que tienen exactamente la misma prioridad? Aquí es donde entra en juego la asociatividad (binding), la cual determina el orden de evaluación de izquierda a derecha o viceversa.

La gran mayoría de los operadores en Python tienen asociatividad a la izquierda (left-sided binding). Esto significa que el cálculo de la expresión se ejecuta estrictamente de izquierda a derecha. Analicemos este ejemplo con el operador de módulo:

Tabla de Precedencia y Asociatividad (De Mayor a Menor)

Nivel de PrioridadOperadorDescripciónAsociatividad (Binding)
1 (Máxima)**Exponenciación (Potencia)Derecha a Izquierda (La gran excepción)
2+, -Operadores Unarios (Signo positivo/negativo)Izquierda a Derecha
3*, /, //, %Multiplicación, División, División de piso y MóduloIzquierda a Derecha
4 (Mínima)+, -Operadores Binarios (Suma y Resta)Izquierda a Derecha

Unarios vs. Binarios

Un signo menos unario (-5) tiene más prioridad que una multiplicación o una división, pero un signo menos binario (5 - 2) tiene menos prioridad que ellas. Observa cómo cambia el orden en este ejemplo:

print(-3 ** 2)

Aquí, la potencia tiene un nivel de prioridad (1) más alto que el menos unario (2). Por lo tanto, Python calcula primero $3^2 = 9$, y después el menos unario entra en acción para cambiarle el signo, dando como resultado -9 (y no 9 positivo, como ocurriría si el menos operara primero). Si quisieras elevar el número negativo completo, estarías obligado a usar paréntesis: (-3) 2.

Operadores y Paréntesis: Rompiendo las Reglas Naturales

Por supuesto, no estás atado a las prioridades por defecto de Python. Tienes permitido utilizar paréntesis () en cualquier momento para alterar el orden natural de un cálculo. En perfecta sintonía con las reglas aritméticas universales, las subexpresiones que se encuentran dentro de los paréntesis siempre se calculan primero.

Puedes anidar tantos paréntesis como necesites. De hecho, muchos desarrolladores los utilizan incluso cuando no alteran el orden, simplemente para que el código sea más legible y fácil de entender para otros humanos.

Reto: El Desafío de los Paréntesis Anidados

Intenta resolver paso a paso la siguiente expresión compleja antes de mirar la solución. Es un excelente ejercicio de rastreo mental para el PCEP:

print((5 * ((25 % 13) + 100) / (2 * 13)) // 2)
  • El flujo va estrictamente desde adentro hacia afuera. Python no puede resolver el contenedor exterior sin saber primero qué valor numérico esconde el contenedor más interno.
  • Cuando tienes bloques independientes de paréntesis que no dependen el uno del otro, la regla que aplica Python es procesar de izquierda a derecha.

Desglose paso a paso desde el interior hacia el exterior:

  1. Primer bloque interno (25 % 13): El 13 cabe una sola vez en el 25 y genera un residuo de 12.
  2. Segundo bloque interno (12 + 100): Sumamos el residuo anterior, lo que nos da 112.
  3. Bloque del divisor (2 * 13): Resolvemos el paréntesis del extremo derecho, obteniendo 26.
  4. Multiplicación izquierda (5 * 112): Multiplicamos el resultado del segundo paso, lo que nos da 560.
  5. División tradicional (560 / 26): Al usar la barra diagonal /, el resultado se transforma obligatoriamente en un número flotante: 21.53846153846154.
  6. División de piso final (21.53846153846154 // 2): Aplicamos la división entera sobre el flotante. El 2 cabe exactamente 10 veces en el 21. Al haber un flotante involucrado, la parte decimal se fuerza a cero.

El resultado final que verás en la consola es 10.0.