Python 2 vs Python 3: la ruptura que importa conocer

Guido van Rossum tomó una decisión incómoda en 2008: romper la compatibilidad hacia atrás de forma intencional. Python 3 no era una actualización incremental — era una corrección de errores de diseño que habían acumulado deuda técnica durante años. El resultado fue casi una década de coexistencia entre dos versiones del mismo lenguaje, y ese período dejó huellas que siguen apareciendo hoy.

Lo que hace especial esta ruptura es que fue deliberada. No fue un bug ni un descuido. El equipo de Python identificó problemas estructurales que no podían arreglarse sin romper el código existente, y eligieron hacerlo correctamente en lugar de seguir parcheando. Eso es exactamente lo que necesitas entender para no confundirte cuando encuentres código de otra era.

Python 2.7 llegó a su fin de vida oficial en enero de 2020. No recibe parches de seguridad, no recibe actualizaciones, no existe soporte activo. Y aun así lo vas a encontrar: en tutoriales viejos indexados por Google, en repositorios corporativos que nadie ha migrado, en libros publicados antes de 2015, en scripts de administración de sistemas que “funcionan y nadie toca”. Saber reconocerlo te ahorra horas de confusión.

Las diferencias que realmente importan para un lector de código son cuatro. La primera y más visible: print. En Python 2, print era una sentencia — una construcción sintáctica especial del lenguaje, como if o for. En Python 3 es una función normal. Esa diferencia explica por qué ves dos formas de escribirlo:

# Python 2 — print como sentencia
print "Hola, mundo"
print "valor:", x

# Python 3 — print como función (la única forma válida hoy)
print("Hola, mundo")
print("valor:", x)

La segunda diferencia afecta cómo el lenguaje maneja texto. Python 2 tenía dos tipos separados: str (bytes crudos) y unicode (texto real). Mezclarlos sin cuidado producía errores crípticos que dependían del contenido en tiempo de ejecución. Python 3 corrigió esto: las cadenas literales son Unicode por defecto, siempre. No hay ambigüedad.

La tercera diferencia es aritmética. En Python 2, dividir dos enteros con / hacía división entera5 / 2 devolvía 2, no 2.5. En Python 3, / siempre produce un float, y para la división entera existe // de forma explícita. Este cambio ha atrapado a más de un programador que copió un snippet antiguo.

La cuarta: range(). En Python 2 existían dos versiones: range() devolvía una lista completa en memoria, y xrange() devolvía un iterador perezoso. En Python 3, range() ya es el iterador perezoso — xrange desapareció. Si ves xrange en código, estás mirando Python 2.

# ── Este archivo ilustra las diferencias clave entre Python 2 y Python 3 ──
# Ejecuta esto con Python 3. Los comentarios marcados con [PY2] muestran
# cómo se escribía lo mismo en Python 2.

# ── 1. print como función ──────────────────────────────────────────────
print("Python 3: print es una función normal")
# [PY2]: print "Python 2: print era una sentencia sin paréntesis"


# ── 2. Unicode por defecto ─────────────────────────────────────────────
greeting = "Héctor, ¿cómo estás?"          # str en PY3 siempre es unicode
print(type(greeting))                       # <class 'str'>
# [PY2]: necesitabas u"Héctor..." para garantizar unicode


# ── 3. División: / vs // ───────────────────────────────────────────────
resultado_float = 7 / 2                     # 3.5 en PY3
resultado_entero = 7 // 2                   # 3 en PY3 y PY2

print(resultado_float)                      # 3.5
print(resultado_entero)                     # 3
# [PY2]: 7 / 2 daba 3, no 3.5 — la trampa más silenciosa del lenguaje


# ── 4. range() es perezoso en Python 3 ────────────────────────────────
r = range(1_000_000)                        # no crea la lista en memoria
print(type(r))                              # <class 'range'>

for n in r:
    if n > 2:
        break
    print(n)
# [PY2]: range(1_000_000) creaba una lista de un millón de enteros ahora mismo
# [PY2]: xrange(1_000_000) era el equivalente perezoso — ya no existe en PY3


# ── Señales de alerta en código que encuentres ─────────────────────────
# Si ves cualquiera de estas líneas, estás ante Python 2:
#
#   print "algo"                  → sentencia print sin paréntesis
#   xrange(n)                     → range perezoso del pasado
#   unicode("texto")              → tipo unicode explícito
#   raw_input("Prompt: ")         → en PY3 es simplemente input()
#   except ValueError, e:         → sintaxis antigua para capturar excepciones
#   /usr/bin/env python            → ambiguo; busca si hay versión explícita

Fíjate en el bloque de la división. Es la trampa más silenciosa de todas porque no lanza ningún error — simplemente produce un resultado diferente. Un algoritmo que calcule porcentajes, probabilidades o promedios copiado de un tutorial de Python 2 puede dar resultados incorrectos en Python 3 y el programa seguirá ejecutándose sin quejarse. El // explícito es exactamente lo que necesitas cuando quieres división entera en código moderno.

El bloque de range() muestra por qué el cambio tiene importancia práctica más allá de renombrar una función. En Python 2, llamar range(1_000_000) en un loop trivial asignaba un millón de enteros en memoria de golpe. Python 3 hace lo sensible por defecto: range es un objeto que produce valores bajo demanda. Solo necesitas convertirlo en lista explícitamente (list(range(n))) si de verdad necesitas todos los valores a la vez.

El bloque de señales de alerta al final del código es quizás lo más útil. Cuando abras un repositorio antiguo o sigas un tutorial y algo no funcione, esas cinco líneas son tu checklist de diagnóstico rápido. raw_input en lugar de input, except ValueError, e en lugar de except ValueError as e, print sin paréntesis — cualquiera de esas es prueba suficiente de que el código no es Python 3 y necesitas o bien actualizarlo o bien ejecutarlo con el intérprete correcto.

Errores que debes conocer

Error: copiar un fragmento de Python 2 que usa división entera y asumir que / se comporta igual en Python 3.

# ❌ Intención: calcular la mitad de elementos a procesar
total = 7
mitad = total / 2
print(mitad)   # Esperas 3, obtienes 3.5 — y lo que venga después puede fallar

# ✅ Usa // cuando quieras división entera explícita
mitad = total // 2
print(mitad)   # 3, sin ambigüedad

// deja clara la intención y funciona igual en Python 2 y Python 3, lo que lo convierte en la forma defensiva de escribir división entera independientemente del contexto.

Error: encontrar print "mensaje" sin paréntesis, intentar ejecutarlo con Python 3 y no entender por qué falla.

# ❌ Python 2 — SyntaxError en Python 3
print "conexión establecida"

# ✅ Python 3
print("conexión establecida")

print sin paréntesis en Python 3 no es solo un error de estilo — es un SyntaxError que impide que el archivo entero se ejecute. Si tu archivo mezcla código moderno con una sola línea de print estilo Python 2, nada correrá.

5

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio