Python 3.x: La Revolución del Lenguaje

2008 - Actualidad | La excelencia técnica y el soporte Unicode total

La gran ruptura: Incompatibilidad necesaria

La primera versión de esta serie fue lanzada el 3 de diciembre de 2008, la serie 3 representa la etapa más ambiciosa en la historia del lenguaje, en la que Python se redefine a sí mismo para corregir limitaciones del pasado y adaptarse a un entorno tecnológico cada vez más exigente. Bajo la dirección de Guido van Rossum y una comunidad global muy activa, esta serie introduce cambiod profundos que priorizan la coherencia, la internacionalización y el rendimiento. A diferencia de la transición entre versiones anteriores, Python 3 supuso una ruptura deliberada con la compatibilidad hacia atrás, una decisión arriesgada pero necesaria para construir un lenguaje más limpio, consistente y preparado para el futuro.

Análisis de la serie 3.x

  • Python 3.0:

    Esta versión se lanzó el 3 de diciembre de 2008, es una versión nueva incompatible con las versiones anteriores. Esta versión introdujo cambios significativos e incompatibles con la serie 2.x para eliminar la rebundancia, incluyenfo la conversión de print en función, soporte nativo de Unicode para texto, división de enteros mejorada estando los decimales por defecto y reglas más estrictas en comparaciones. Las Principales novedades y cambios son los siguientes:

    • Unicode Nativo: Se eliminó la confusión entre texto y bytes, haciendo que Python fuera internacional por defecto.

    • Iteradores eficientes: Funciones como range() o zip() dejaron de crear listas pesadas en memoria, pasando a ser generadores que calculan valores bajo demanda.

    • División Lógica: División Lógica: Se estandarizó que la división entre enteros (/) devuelva siempre un decimal, moviendo la división entera al operador (//).

    También quiero destacar un cambio muy importante y ese es el de la función print(), la declaración de este paso de estar sin parentesis "print" a pasar con parentesis "print()", esto se hizo para aumentar la flexibilidad y legibilidad del lenguaje. Aquí muestro unos ejemplos de declaración.

    Old: print "La respuesta es", 2*2
    New: print("La respuesta es", 2*2)
    
    # La coma final suprime el salto de línea
    Old: print x,
    # Agrega un espacio en lugar de un salto de línea.
    New: print(x, end="")
  • Python 3.1:

    Esta versión se lanzó el 27 de junio de 2009, esta versión fue un pulido y optimización de su versión anterior. Aunque no introdujo cambios tan dramáticos como su versión anterior, si hay que decir que incluyó varias mejoras importantes. Trajo un Diccionario Ordenado (OrderedDict) en la biblioteca estándar siendo útil para mantener el orden de los elementos. Se realizaron Optimizaciones del tipo int lo que mejoró el rendimiento y un módulo io mucho más rápido y estable. También incorporó nuevas utilidades en el unittest donde se centró en facilitar el manejo de fixtures (configuración y limpieza de pruebas) y ofrecer un control más glandular sobre la ejecución de los test como el salto de tests.

  • Python 3.2:

    Esta versión se lanzó el 20 de febrero de 2011, esta versión se centró en algunos aspectos destacados continuando la línea de estabilización y añadió mejoras prácticas para entornos de producción. Se introdujo un nuevo módulo para el análisis de la línea de comandos, argparse, para superar las limitaciones de optparse que no brindaba soporte para argumentos posicionales, sub comandos, opciones requeridas y otros patrones comune para especificar y validar opciones. También se añadió otras utilidades en la biblioteca estándar, incorporó soporte mejorado para SSL/TLS y trajo Optimizaciones en el intérprete y en la implementación del recolector/GIL que beneficiaron el rendimiento en ciertas cargas.

    Aquí muestro un ejemplo: Imagina que deseas crear un programa llamado saludo.py que pueda ejecutarse así: python saludo.py John (saludo simple) python saludo.py Jane --formal (saludo formal)

    import argparse
    
    def main():
        # 1. Crear el objetivo ArgumentParser
        # El objetivo parser contendrá toda la información necesaria para analizar
        # La línea de comandos
        parser = argparse.ArgumentParser(
            description = "Un script simple para saludar a un usuario.",
            epilog = "¡Gracias por usar el programa de saludo!"
        )
    
        # 2. Agregar Argumentos
        # Argumento Posicional (Obligatorio)
        parser.add_argument(
            'nombre',
            type = str,
            help = 'El nombre de la persona a saludar.'
        )
    
        # Argumento Opcional (Flag o Bandera)
        # action = 'store_true' significa que si se incluye la bandera, su valor es True.
        # Y si se omite, su valor por defecto es False.
        parser.add_argument(
            '--formal',
            action = 'store_true',
            help = 'Si se incluye, el saludo será formal ("Estimado/a").'
        )
        
        # 3. Analizar los argumentos
        # Esto examina la línea de comandos, convierte los argumentos a los tipos
        # especificados, y los coloca como atributos del objeto 'args'.
        args = parser.parse_args()
    
        # 4. Usa los argumentos
        if args.formal:
            saludo = "Estimado/a {args.nombre}, ¡bienvenido/a al programa!"
    
        else:
            saludo = "Hola, {args.nombre}. ¡Qué gusto verte!"
    
        print(saludo)
    
    if __name__ == "__main__":
        main()
  • Python 3.3:

    Esta versión se lanzo el 29 de septiembre de 2012, esta versión introdujo mejoras conceptuales relevantes, especialmente en el manejo de generadores con la expresión "yield from". Esta característica permitió delegar la generación de valores en otros generadores, simplificando enormemente el código y mejorando su modularidad. Además, se unificó la representación interna de cadenas, eliminando diferencias entre implementaciones y facilitando el manejo de texto. Este tipo de cambios, aunque poco visibles, fueron clave para mantener la coherencia interna del lenguaje.

    Aquí muestro un ejemplo de como se aplicaria el yield:

    def mi_generador():
        print("Iniciar el generador")
        yield 1
        print("Después del primer yield")
        yield 2
        print("Después del segundo yield")
    

    Cuando llama al generador, el código que está dentro de la función no se ejecuta de inmediato. Simplemente crea y devuelve un objeto generador. La primera vez que solicitas un valor, el código dentro de la función se ejecuta hasta que encuentra el primer yield 1. La siguiente vez que se solicita, la función reanuda la ejecución desde la línea siguiente al último yield y así hasta la última. Esta mejora sirvió para trabajar con generadores de forma má limpia, una representación de cadenas más flexibles que eliminó la distinción entre “wide ” y “narrow”.

  • Python 3.4:

    Esta versión se lanzó el 16 de marzo de 2014, esta versión hizo que el lenguaje diese un paso decisivo hacia la programación moderna al incorporar el módulo asyncio. Este introdujo oficialmente el paradigma de programación asíncrona en Python, permitiendo gestionar múltiples tareas concurrentes sin bloquear la ejecución. Este avance resultó fundamental en aplicaciones que dependen de operaciones de entrada/salida, como servidore web o sistemas de red, donde la eficiencia depende de la capacidad de manejar múltiples procesos simultáneamente.

  • Python 3.5:

    Esta versión se lanzó el 13 de septiembre de 2015, esta versión consolidó este enfoque al introducir las palabras clave async y await, que proporcionaron una sintaxis clara y directa para trabajar con corutinas. Esto simplificó enormemente el desarrollo de código asíncrono, haciéndolo más legible y mantenible. Además, se ampliaron las capacidades del lenguaje en el manejo de estructuras de datos mediante operadores de desempaquetado, lo que permitió escribir código más expresivo y flexible. En la sintaxis introdujo los siguientes aspectos:

    • Mejoró enormemente el soporte de la programación asíncrona al agregar objetos aguardables, funciones corrutina, iteraciones asincrónicas y gestores asincrónicos de contexto.

    • import asyncio
      
      async def esperar (segundos):
          # 'await' pausa esta corutina para que el Event Loop pueda ejecutar otras
          await asyncio.sleep(segundos)
          return "Esperé {segundos} segundos."
      
      async def main():
          # 'async def' define una corutina
          print("Inicio la espera...")
          
          # Ejecuta el resultado y esperar el resultado
          resultado = await esperar(1)
          print(resultado)
          print("Fin.")
      
      # asyncio.run(main())
      # Forma moderna de ejecutarlo
    • Además se amplió la capacidad de usar los operadores de desempaquetado * (para iterables) y ** (para diccionarios) en más contextos, como en literales de listas.
    • lista_1 = [1, 2]
      lista_2 = [3, 4]
      # Antes: lista_total = lista_1 + lista_2
      lista_total = [*lista_1, *lista_2, 5]
      # Resultado : [1, 2, 3, 4, 5]
      
      d1 = {'a': 1}
      d2 = {'b': 2}
      d_total = {**d1, **d2, 'c' : 3}
      # Resultado: {'a': 1, 'b': 2, 'c': 3}
  • Python 3.6:

    Esta versión se lanzó el 23 de diciembre de 2016, esta versión se centró en mejorar la productividad del desarrollador. La introducción de las f-strings supuso una revolución en la forma de formatear texto, ofreciendo una sintaxis más clara, eficiente y fácil de leer. También se ampliaron las anotaciones de tipo, permitiendo especificar tipos no solo en funciones, sino también en variables. lo que facilitó el desarrollo de herramientas de análisis estático y mejoró la calidad del código en proyectos grandes.

    • Cadenas Literales Formateadas (f-Strings): Las "f-strings" proporcionan una sintaxis concisa, eficiente y legible para incrustar expresiones de Python dentro de cadenas de texto. Son la forma preferida de formateo de cadenas en la actualidad.
    • nombre = "Ana"
      saldo = 123.456
      
      # La 'f' inicial permite incrustar expresiones entre llaves {}
      mensaje = "Hola {nombre.upper{}}. Tu saldo es ${saldo:.2f}"
      # Resultado: "Hola, Ana. Tu saldo es $123.46"
    • También introdujo las Anotaciones de Tipo para Variables que estas permitieron anotar el tipo de las variables dentro del cuerpo de la función o módulo, no solo en los parámetros y valores de retorno de la función. Esto ayuda a herramientas de análisis de código (como Mypy) a realizar una mejor comprobación estática.
    • from typing import List
      
      # Anotación de tipo para una variable local
      datos: List[int] = [1, 2, 3]
      
      # Una constante con tipo
      LIMITE_INTENTOS: int = 5
      
  • Python 3.7:

    Esta versión se lanzó el 27 de junio de 2018, dio un paso importante hacia la simplificación del código repetitivo mediante la introducción de las dataclasses. Estas permiten definir estructuras de datos de forma concisa, generando automáticamente métodos comunes como inicialización o representación en texto. Este cambio redujo significativamente la cantidad de código necesario para tareas habituales, mejorando la claridad y mantenibilidad de los programas.

    • Una de las innovaciones que trajo fue las Clases de Datos, El decorador @dataclass permite crear clases con métodos comunes como __init__, __repr__ y __eq__ generados automáticamente, haciéndolas ideales para modelar estructuras de datos.
    from dataclasses import daraclass
    
    @dataclass
    class Punto:
        # Los campos son inferidos del tipo de dato anotado
        x: int
        y: int = 0
        # Se puede definir un valor por defecto
    
    # Uso:
    # Llama al __init__ autogenerado (x=10, y=0)
    p1 = Punto(10)
    p2 = Punto(0, 10)
    
    #True (Usa el __eq__ autogenerado)
    print(p1 == p2)
    
    # Punto (x=10, y=0) (Usa el __repr__ autogenerado)
    print(p1)
  • Python 3.8:

    Esta versión se lanzó el 14 de octubre de 2019, esta versión renovó python con una novedad sintáctica importante que permite asignar un valor a una variable y usar ese valor en una expresión, todo en una solo línea, mejorando la eficiencia y legibilidad en ciertas estructuras de control. Estas serían las Expresiones de Asignación (Operador Morsa :=). El operador morsa permite asignar un valor a una variable como parte de una expresión. Esto es útil para evitar recalcular un valor o para hacer más compactas ciertas comprobaciones de if o while.

    # Sin el Operador Morsa (típico: calcular, luego comprobar)
    # linea = archivo.readline()
    # while linea:
    #   procesar(linea)
    #   linea = archivo.readline()
    
    # Con el Operador Morsa (calcular y comprobar en el 'while')
    # La variable 'linea' se asigna y se comprueba en la misma expresión
    while (linea := archivo.readline()):
        procesar(linea)
  • Python 3.9:

    Esta versión se lanzó el 5 de octubre de 2020, esta versión continuó mejorando la experiencia de desarrollo con cambios en estructuras fundamentales como los diccionarios, introduciendo operadores más intuitivos para su combinación y actualización. También se añadieron métodos especifícos para trabajar con cadenas, simplificando tareas comunes y reduciendo la necesidad de soluciones manuales menos claras.

    • Se introdujeron Nuevos Operadores para Fusión y Actualización de Diccionarios, los siguientes operadores consiguieron simplificar la sintaxis para combinar y modificar diccionarios de forma concisa.

      • Fusión ( | ), Esté operador crea un nuevo diccionario a partir de dos o más. Si hay claves duplicadas, el valor del operador de la derecha se mantiene.

      • Actualización ( |= ), Esté operador modifica el diccionario original con las claves y valores de la derecha.
      • Aquí te dejo un ejemplo de su uso:

        dias_semana = {"L": "Lunes", "M": "Martes"}
        dias_finde = {"J": "Jueves", "V": "Viernes", "S": "Sábado"}
        
        # 1. Fusión (crea un nuevo diccionario)
        todos_dias = dias_semana | dias_finde
        print(todos_dias)
        # {"L": "Lunes", "M": "Martes", "J": "Jueves", "V": "Viernes", "S": "Sábado"}
        
        # 2. Actualización (modifica el diccionario original)
        dias_semana |= {"X": "Miércoles"}
        print(dias_semana)
        # {"L": "Lunes", "M": "Martes", "X": "Miércoles"}
    • También se añadieron Nuevos Métodos para cadenas de texto (removeprefix y removesuffix), donde se añadieron dos métodos muy solicitados a las cadenas (str) para eliminar prefijos y sufijos de manera explícita y segura. Si el prefijo o sufijo no está presente, simplemente devuelve la cadena original, a diferencia de los métodos manuales de slicing que podrían fallar o ser menos claros.
    • Aquí te dejo un ejemplo de su uso:

      cadena_url = "https://www.ejemplo.com/recurso"
      nombre_archivo = "documento.pdf.bak"
      
      # Elimina el prefijo si existe
      sin_protocolo = cadena_url.removeprefix("https://")
      print(sin_protocolo)
      # www.ejemplo.com/recurso
      
      # Elimina el sufijo si existe
      sin_backup = nombre_archivo.removesuffix(".bak")
      print(sin_backup)
      # documento.pdf
    • Y se añadió un Nuevo Módulo para zonas horarias, Python incluyó en su biblioteca estándar el módulo zoneinfo, que permite proporcionar soporte para las bases de datos de zonas horarias IANA. Esto significa que ya no es necesario depender de bibliotecas de terceros (como pytz) para trabajar con información de tiempo de precisa.
    • Aquí te dejo un ejemplo de su uso:

      from zoneinfo import Zoneinfo
      from datetime import datetime
      
      # Definir una fecha y hora con información de zona horaria IANA
      dt = datetime(2025, 12, 4, 15, 0, tzinfo=ZoneInfo("Europe/Madrid"))
      print(dt)
      # 2025-12-04 15:00:00+01:01
  • Python 3.10:

    Esta versión se lanzó el 4 de octubre de 2021, esta versión presentó la coincidencia de patrones estructurales, una característica poderosa de comparar valores contra diferentes estructuras y realizar acciones especifícas. Proporciona una nueva sintaxis para ejecutar código basado en el valor o la estructura de un objeto, similar a una declaración switch en otros lenguaje, pero mucho más potente. Aquí te dejo un ejemplo:

    punto = (0, 0)
    # Puede ser una tupla
    
    match punto
        case (0, 0): # Caso 1: Origen
            print("El punto está en el origen.")
        case (x, 0): # Caso 2: Patrón con variable 'x' (en el eje X)
            print("El punto está en el eje X, en x={x}.")
        case (x, y) if x == y # Caso 3: Patrón con condición
            print("El punto está en la diagonal (x=y).")
        case _: #Caso 'default' (similar al 'else')
            print("Otro punto cualquiera.")
  • Python 3.11:

    Esta versión se lanzó el 24 de octubre de 2022, el foco se desplazó hacia el rendimiento y la experiencia del desarrollador. Gracias al proyecto "Faster CPython", el intérprete se volvió significativamente más rápido, con mejoras notables en la ejecución de código. Además, los mensajes de error se hicieron mucho más precisos, indicando exactamente qué parte de una expresión causó el fallo. Esto supuso una mejora enorme en la depuración de programas. También se introdujeron los grupos de excepciones, que permiten manejar múltiples errores simultáneamente, algo especialmente útil en programación concurrente.

    • Rendimiento Aumentado (El Proyecto Faster CPython): Esta versión hizo que el intérprete de CPython fuera notablemente más rapido, prometiendo una mejor promedio del 25% respecto a Python 3.10 y hasta un 60% más rápido en ciertos benchmarks. Esto se logró gracias a Evaluation Adapters: Permiten que el intérprete reemplace algunas operaciones de bytecode genéricas con versiones más especializadas en tiempo de ejecución.

      • Inlining de llamadas a funciones: Optimiza la forma en que se ejecutan las llamadas a funciones.

    • Rastros de Errores Más Precisos (Exception Tracebacks): Una de las mejores características para la calidad de vida de los desarrolladores es que los rastros de errores ahora son mucho más específicos. El intérprete indica la expresión que causó un error, no solo la línea completa, facilitando la depuración en líneas complejas o aniadas. Aquí dejo un ejemplo:
    • def obtener_nombre(usuario):
          return usuario.nombre
      
      data = {"id": 1}
      
      # En 3.10, solo sabrías que falló esta línea.
      # En 3.11, el error apunta directamente a '.nombre'.
      # Error: AttributeError: 'dict' object has no attribute 'nombre'
      
      # Traceback de 3.11 (muestra con ^ dónde falló):
      # ... line 4, in 
      #     obtener_nombre(data)
      #     │              └─── dict object
      #     └── obtener_nombre
      # 
      # ... line 2, in obtener_nombre
      #     return usuario.nombre
      #            ^^^^^^^^^^^^^
    • También se introdujo grupos de excepciones (ExceptionGroup): Permite encapsular y lanzar múltiples excepciones no relacionadas simultáneamente. Esto es especialmente útil en código asíncrono o concurrente, donde varias tareas pueden fallar de forma independiente, y permite capturar un subconjunto específico de ellas con la nueva sintaxis except*. Aquí muestro un ejemplo de su uso:
    • class ErrorRed(Exception): pass
      class ErrorArchivo(Exception): pass
      
      try:
          # Lanza un grupo de errores
          raise ExceptionGroup`(
              "Errores de proceso",
              [ErrorRed("Fallo en DNS"), ErrorArchivo("Archivo no encontrado")]
          )
      # except* permite capturar errores especifícos dentro del grupo
      except* ErrorRed as eg:
          print("Error de red capturado: {eg}")
      except* ErrorArchivo as eg:
          print("Error de archivo capturado: {eg}")
  • Python 3.12:

    Esta versión se lanzó el 2 de octubre de 2023, esta versión continuó esta línea de optimización y simplificación, mejorando el soporte para tipado genérico y facilitando la escritura de código más seguro y mantenible. También se optimizó el rendimiento de asyncio y se añadieron mejoras en las f-strings, aumentando su flexibilidad. Estas mejoras reflejan una tendencia clara hacia un lenguaje más robusto y preparado para aplicaciones complejas.

    • Introdujo un soporte Nativo para Tipos Genéricos: Simplifica drásticamente la definición de clases y funciones que operan génericos, eliminando la necesidad de importar TypeVar de typing para casos sencillos.
    • # Antes (Python 3.11):
      # from tying import TypeVar, Callable
      # T = TypeVar('T')
      # def duplicar(x: T) -> T: ...
      
      # Ahora (Python 3.12): Sintaxis simplificada
      def duplicar[T](x: T) -> T:
          # La función es genérica en el tipo T.
          return x * 2
    • Mejoras en asyncio: Se mejoró la eficiencia del módulo asíncrono asyncio en muchos aspectos, con una notable mejora en el rendimiento de hasta un 75% en ciertas operaciones relacionadas con el Event Loop.

    • Comentarios en f-String: Permite usar comentarios (iniciados con #) dentro de una f-string, siempre y cuando estén dentro de una expresión de reemplazo delimitada por las llaves ({}).
    • resultado = 42
      mensaje = "El resultado final es: {resultado # Esto es un comentario + 1 # Añade uno}"
      
      print(mensaje) # El resultado final es: 43
  • Python 3.13:

    Esta versión se lanzó el 7 de octubre de 2024, esta versión se centró en uno de los mayores desafíos históricos del lenguaje: la concurrencia real. Los avances hacia la eliminación del GIL (Global Interpreter Lock) representan un cambio profundo en la arquitectura del intérprete, permitiendo aprovechar mejor los procesos multinúcleo. Aunque este cambio es progresivo, su impacto potencial es enorme en aplicaciones de alto rendimiento.

    • El cambio más ambicioso de esta versión due la eliminación del GIL (Global Interpreter Lock), El GIL ha sido un obstáculo para la ejecución paralela real del código Python ligado a la CPU. El GIL tradicionalmente garantiza que solo un hilo de Python pudiera ejecutar bytecode a la vez, incluso en máquinas con múltiples núcleos. Esta nueva versión permitirá que los hilos de Python se ejecuten simultáneamente en diferentes núcleos del procesador.

    • Novedad Clave:La implementación se basa en el trabajo experimental del modo "nogil" y busca ser la configuración por defecto, lo que resultará en un aumento masivo de rendimiento en aplicaciones que utilizan el módulo threading para realizar cálculos intensivos.
  • Python 3.14:

    Esta es la versión que tenemos actualmente (desde que publique esto), fue lanzada el 7 de octubre de 2025. En esta versión se introdujeron mejoras significativas en el rendimiento, un intérprete interactivo mejorado (PyREPL) y característica experimentales como un compilador JIT. Otras de las aportaciones más destacadas incluyen las Cadenas de Plantilla (T-String). Se introdujo el prefijo t"" para definir plantila de cadenas que, a diferencia de las f-strings, no se evalúan de forma inmediata. Esto permite a los desarrolladores manejar la sustituición de variables de forma diferida y segura, siendo una herramienta clave para evitar ataques de inyección de código en sistemas que generan SQL o HTML dinámicamente. También se añadió soporte nativo para UUID v7 en el módulo uuid. Estos identificadores son únicos pero están ordenados cronológicamente, lo que optimiza drásticamente el rendimiento de las bases de datos al insertar y buscar registros por tiempo.

    Aquí muestro un ejemplo de uso de los T-Strings:

    # Definición de una plantilla (no se evalúa al momento)
    nombre = "Usuario"
    plantilla = "Bienvenido, {nombre}"
    
    # La evaluación puede ocurrir más tarde con validaciones adicionales
    print(plantilla)
    # Salida: Bienvenido, Usuario
    
  • En conjunto, la serie Python 3.x no solo representa una evolución técnica, sino una redefinición completa del lenguaje. Cada versión ha contribuido a mejorar la coherencia, el rendimiento y la capacidad de adaptación de Python, consolidándolo como una herramienta fundamental en campos como el desarrollo web, la inteligencia artificial, la ciencia de datos y la automatización. Esta etapa demuestra cómo un lenguaje puede evolucionar sin perder su esencia, manteniendo como principio central la claridad y la simplificidad, incluso en su contexto tecnológico cada vez más complejo.