• Saltar al contenido principal
  • Skip to secondary menu
  • Saltar a la barra lateral principal
  • Saltar al pie de página
  • Inicio
  • Secciones
    • Ciencia de datos
    • Criptografía
    • Herramientas
    • Machine Learning
    • Noticias
    • Opinión
    • Productividad
    • Programación
      • JavaScript
      • Julia
      • Matlab
      • Python
      • R
  • Programación
    • JavaScript
    • Julia
    • Matlab
    • Python
    • R
  • Laboratorio
    • Estadística
      • Calculadora del Tamaño Muestral en Encuestas
      • Calculadora de estadísticos descriptivos
      • Test de normalidad
      • Calculadora de contrastes de hipotesis
      • Calculadora de tamano del efecto
      • Simulador de Regresión Lineal con Ruido
      • Visualizador de PCA
      • Visualizador de Series Temporales
      • Simulador de Regresión Logística
      • Simulador de K-Means
      • Simulador de DBSCAN
      • Detector de la Ley de Benford
      • Ajuste de Curvas
      • Calculadora de Matrices
    • Probabilidad
      • Calculadora de Probabilidad de Distribuciones
      • Calculadora de Probabilidades de Lotería
      • Simulador del Problema de Monty Hall
      • Simulador de la Estrategia Martingala
    • Finanzas
      • Calculadora de Préstamos e Hipotecas
      • Conversor TIN ↔ TAE
      • Calculadora DCA con ajuste por inflación
      • Calculadora XIRR con Flujos Irregulares
      • Simulador FIRE (Financial Independence, Retire Early)
    • Negocios
      • CLV
      • Scoring
    • Herramientas
      • Formateador / Minificador de JSON
      • Conversor CSV ↔ JSON
      • Comparador y Formateador de Texto y JSON
      • Formateador y Tester de Expresiones Regulares
      • Inspector de JWT
      • Generador y verificador de hashes
      • Codificador / Decodificador Base64 y URL
      • Conversor de bases numericas
      • Conversor de Timestamp Unix
      • Conversor de colores
      • Generador de UUIDs
    • Juegos
      • Tres en Raya
      • Nim con Q-Learning
    • Más
      • Método D’Hondt
      • Generador de Contraseñas Seguras
  • Noticias
  • Boletín
  • Contacto
  • Tienda
    • Libros
    • Equipamiento de oficina
    • Equipamiento en movilidad

Analytics Lane

Ciencia e ingeniería de datos aplicada

  • Ciencia de datos
  • Machine Learning
  • IA Generativa
  • Python
  • Pandas
  • NumPy
  • R
  • Excel

== vs is en Python: la diferencia que causa bugs misteriosos

noviembre 3, 2026 Por Daniel Rodríguez Deja un comentario
Tiempo de lectura: 7 minutos

Hay un bug que aparece una y otra vez en código Python, tan sutil que muchos no lo ven hasta que todo se quiebra:

if variable == None:
    print("Es nulo")

Esto parece correcto. Funciona. Pero un pythonista experimentado levantaría la mano y diría: “Deberías usar is, no ==.”

¿Por qué? ¿Acaso == None no funciona?

Funciona. Pero hay una diferencia entre “funciona” y “es lo correcto”. Y esa diferencia te causará dolores de cabeza si no la entiendes.

La confusión es real: == e is parecen hacer lo mismo (comparar dos cosas). Pero comparan cosas distintas. Y elegir la equivocada es la causa de algunos de los bugs más raros y difíciles de debuguear en Python.

En esta entrada vamos a ver:

  • Qué compara cada uno.
  • Por qué son distintos.
  • Dónde está la trampa (los números pequeños y None).
  • Cuál usar en cada situación.
  • Por qué en algunos casos dan resultados “raros”.

Tabla de contenidos

  • 1. La diferencia en una frase
  • 2. El ejemplo que muestra todo
  • 3. Entender la identidad: la dirección de memoria
  • 4. Las 3 comparaciones que importan
    • 4.1. Comparar con None (SIEMPRE usa is)
    • 4.2. Comparar booleanos (cuidado)
    • 4.3. Números pequeños (la trampa silenciosa)
  • 5. Tabla resumen: cuándo usar cada uno
  • 6. Casos donde is es correcto (y necesario)
    • 6.1. Verificar que dos variables apuntan al MISMO objeto
    • 6.2. Detectar cambios inesperados (por referencia)
    • 6.3. Singleton patterns
  • 7. La trampa visual: código que parece funcionar pero no
  • 8. Comparaciones especiales con is
    • 8.1. is not
    • 8.2. Comparar con listas vacías (INCORRECTO)
  • 9. El debugging: cómo encontrar estos bugs
  • 10. Resumen: la regla práctica
  • 11. Un ejemplo de bug real
  • 12. Conclusiones

La diferencia en una frase

  • == compara valor: “¿tienen el mismo contenido?”
  • is compara identidad: “¿son el mismo objeto en memoria?”

Eso es todo. Pero las consecuencias son enormes.

El ejemplo que muestra todo

a = [1, 2, 3]
b = [1, 2, 3]

print(a == b)  # True  ← mismo contenido
print(a is b)  # False ← diferentes objetos en memoria

Ahora lo mismo pero con tuplas:

Cómo Proyectar Cuándo Alcanzarás la Independencia Financiera
En Analytics Lane
Cómo Proyectar Cuándo Alcanzarás la Independencia Financiera

c = (1, 2, 3)
d = (1, 2, 3)

print(c == d)  # True  ← mismo contenido
print(c is d)  # True  ← ¡mismo objeto en memoria!

¿Qué? ¿Cómo es posible que dos tuplas distintas sean el mismo objeto?

Respuesta: Python cachea tuplas pequeñas por optimización. Crea una sola tupla en memoria y reutiliza la referencia.

Eso es el primer indicio de que == e is no son lo mismo, y que Python tiene trucos internos que hacen que el comportamiento sea raro a veces.

Entender la identidad: la dirección de memoria

Cada objeto en Python vive en una dirección de memoria. Dos objetos distintos tienen direcciones distintas, aunque tengan el mismo valor.

a = [1, 2, 3]
b = [1, 2, 3]

print(id(a))  # 140234567891520 (dirección de memoria)
print(id(b))  # 140234567891648 (dirección distinta)

print(a is b)  # False (diferentes direcciones)
print(a == b)  # True (mismo contenido)

id() te da la dirección de memoria del objeto. Si dos variables apuntan a la misma dirección, is devuelve True.

c = a

print(id(a))  # 140234567891520
print(id(c))  # 140234567891520 (¡misma dirección!)
print(a is c)  # True (mismo objeto)
print(a == c)  # True (y mismo contenido, obvio)

Cuando haces c = a, no copias el contenido. Haces que c apunte a la misma dirección que a. Son alias del mismo objeto.

Las 3 comparaciones que importan

Comparar con None (SIEMPRE usa is)

Este es el bug clásico y frecuente.

# Incorrecto (pero funciona a veces)
if variable == None:
    print("Es nulo")

# Correcto
if variable is None:
    print("Es nulo")

¿Por qué? Porque None es un singleton en Python. Solo existe UNA instancia de None en toda tu aplicación. Es un objeto especial:

a = None
b = None

print(id(a))  # 94653618254496
print(id(b))  # 94653618254496 (¡la misma dirección!)
print(a is b)  # True

Usar == None funciona, pero es conceptualmente incorrecto. Preguntas “¿este objeto tiene el mismo valor que None?” cuando lo que realmente quieres preguntar es “¿este objeto ES None?”

# Esto "funciona" pero es engañoso
class Raro:
    def __eq__(self, other):
        if other is None:
            return True  # Siempre dice que es igual a None
        return False

raro = Raro()
print(raro == None)  # True (aunque no sea None)
print(raro is None)  # False (que es la verdad)

Si usas ==, alguien podría sobrescribir el comportamiento y tu código fallaría silenciosamente. Con is, es imposible engañar.

Regla de oro: siempre compara None con is, nunca con ==.

if variable is None:
    pass

if variable is not None:
    pass

Comparar booleanos (cuidado)

Los booleanos True y False son también singletons:

a = True
b = True

print(a is b)  # True (mismo objeto)
print(a == b)  # True (y mismo valor, obvio)

Así que para booleanos, == e is suelen dar el mismo resultado. Pero usa == por consistencia, no is:

# Correcto
if variable == True:
    pass

if variable == False:
    pass

# Aunque funcione, es un antipatrón
if variable is True:
    pass

Por qué? Porque creas hábitos. Si siempre usas is para booleanos, uno de estos días lo harás con otro tipo de objeto donde is no es lo correcto.

Números pequeños (la trampa silenciosa)

Python cachea enteros pequeños (del -5 al 256) para optimización:

a = 5
b = 5

print(a is b)  # True (¡Python reutiliza el mismo objeto!)
print(a == b)  # True

# Pero números más grandes...
c = 257
d = 257

print(c is d)  # False (¡diferentes objetos!)
print(c == d)  # True (mismo valor)
a = 256
b = 256
print(a is b)  # True (cacheado)

c = 257
d = 257
print(c is d)  # False (no cacheado)

Esto es una trampa invisible. Tu código funciona con números pequeños porque is accidentalmente devuelve True. Pero falla con números grandes.

# Código tramposo
if numero is 5:
    print("Es 5")

# Funciona para 5 (cacheado), pero falla para 257 (no cacheado)
if numero is 257:
    print("Es 257")  # Probablemente falle

Nunca compares números con is. NUNCA.

# Correcto
if numero == 5:
    pass

if numero == 257:
    pass

Tabla resumen: cuándo usar cada uno

SituaciónUsaEjemplo
Comparar con Noneisif variable is None:
Comparar con True/False==if variable == True:
Comparar números==if numero == 5:
Comparar strings==if nombre == "Juan":
Comparar listas/dicts==if lista == [1, 2, 3]:
Verificar si es el MISMO objetoisif lista is variable:
Comparar con negación de Noneis notif variable is not None:

Casos donde is es correcto (y necesario)

Verificar que dos variables apuntan al MISMO objeto

original = [1, 2, 3]
copia = original  # No es copia, es alias

print(original is copia)  # True (mismo objeto)
print(original == copia)  # True (mismo contenido)

# Ahora modifica uno
original.append(4)
print(copia)  # [1, 2, 3, 4] ← cambió sin querer!

Usar is te ayuda a detectar cuándo dos variables son alias peligrosos.

Detectar cambios inesperados (por referencia)

def procesar_lista(lista):
    # Alguien pasó la lista correcta?
    if lista is None:
        print("Error: lista es None")
        return

    # Modificar la lista afecta al original
    lista.append(999)

datos = [1, 2, 3]
procesar_lista(datos)
print(datos)  # [1, 2, 3, 999] ← cambió

Singleton patterns

class Config:
    _instance = None

    def __new__(cls):
        if cls._instance is None:  # Usa is para verificar singleton
            cls._instance = super().__new__(cls)
        return cls._instance

c1 = Config()
c2 = Config()

print(c1 is c2)  # True (mismo objeto en memoria)

La trampa visual: código que parece funcionar pero no

def validar_usuario(edad):
    # Esto "funciona" para algunos números
    if edad is 18:
        print("Eres adulto")
    else:
        print("No eres adulto")

validar_usuario(18)   # Funciona (18 está cacheado)
validar_usuario(257)  # Parece funcionar pero es casualidad
validar_usuario(1000) # Probablemente falle

El bug no se ve porque parece funcionar. El problema aparece solo con números grandes, en producción, cuando nadie espera el error.

Comparaciones especiales con is

is not

if variable is not None:
    print("Es algo")

# Equivalente (pero menos legible)
if not (variable is None):
    print("Es algo")

Usa is not cuando quieras la negación clara.

Comparar con listas vacías (INCORRECTO)

# Incorrecto
if lista is []:
    print("Lista vacía")

# Correcto
if lista == []:
    print("Lista vacía")

# Aún mejor
if not lista:
    print("Lista vacía")

Cada [] es un objeto nuevo, así que is [] siempre devuelve False (comparas objetos distintos).

El debugging: cómo encontrar estos bugs

Si sospechas que alguien usó is donde debería usar ==:

# El código falla a veces pero no siempre
# Síntoma: funciona con números pequeños, falla con grandes

numero = 500
if numero is 500:  # Falla
    print("Es 500")
else:
    print("No es 500")  # Aquí entra, aunque numero == 500 es True

Busca comparaciones is que no sean con None, True, False, o variables. Probablemente sea un bug.

# Probables bugs
if edad is 18:
if contador is 0:
if resultado is "error":
if lista is []:

# Correcto
if edad == 18:
if contador == 0:
if resultado == "error":
if not lista:

Resumen: la regla práctica

Usa == por defecto. Solo usa is en estos casos:

  1. Comparar con None: if variable is None:
  2. Comparar con not None: if variable is not None:
  3. Verificar identidad: if variable is otra_variable: (cuando necesitas saber si son el MISMO objeto)
  4. Casos muy específicos como singletons.

Nunca uses is para:

  • Números
  • Strings
  • Listas, diccionarios, tuplas
  • Booleanos (usa ==)
  • Nada que no sea None

Un ejemplo de bug real

# Pipeline de procesamiento de datos
def procesar_datos(datos, estado_esperado):
    if datos is estado_esperado:  # Bug sutil
        print("Datos en el estado correcto")
        return procesar(datos)
    else:
        print("Estado incorrecto")
        return None

resultado = procesar_datos([1, 2, 3], [1, 2, 3])
# Falla: compara objetos (direcciones), no valores
# datos y estado_esperado son listas distintas, aunque iguales

Arreglo:

def procesar_datos(datos, estado_esperado):
    if datos == estado_esperado:  # Correcto
        print("Datos en el estado correcto")
        return procesar(datos)
    else:
        print("Estado incorrecto")
        return None

Conclusiones

== e is parecen lo mismo, pero comparan cosas muy distintas:

  • == → “¿tienen el mismo valor?” (lo que casi siempre quieres)
  • is → “¿son el mismo objeto en memoria?” (lo que rara vez quieres)

El 99% del tiempo, quieres ==.

Usa is solo para:

  1. None: siempre if variable is None:
  2. Identidad: cuando necesites saber si dos variables apuntan al MISMO objeto.
  3. Casos muy específicos y documentados.

La trampa es que Python cachea algunos valores (números pequeños, tuplas, None, True, False) para optimización. Así que is a veces devuelve True cuando no esperas, haciendo que parezca que tu código funciona cuando en realidad es incorrecto. Ese es el bug invisible.

Cuando estés debugueando algo raro y no encuentres la causa, busca is. Apuesto a que está ahí.

Imagen de Mediamodifier en Pixabay

¿Te ha parecido de utilidad el contenido?

¡Puntúalo entre una y cinco estrellas!

Puntuación promedio 0 / 5. Votos emitidos: 0

Ya que has encontrado útil este contenido...

¡Síguenos en redes sociales!

¡Siento que este contenido no te haya sido útil!

¡Déjame mejorar este contenido!

Dime, ¿cómo puedo mejorar este contenido?

Publicaciones relacionadas

  • Cómo Proyectar Cuándo Alcanzarás la Independencia Financiera
  • Docker Compose: el día a día. Cómo ver qué funciona y qué no
  • Cómo Verificar la Normalidad de tus Datos antes de un Test Estadístico
  • Cómo Elegir el Test de Hipótesis Correcto para tus Datos
  • Qué significa realmente “calidad de datos”
  • Cómo Detectar Pseudo-IDs y Data Leakage en el Constructor de Scorecards
  • Tuplas vs Listas en Python: parecen lo mismo, pero no
  • Curiosidad: El puerto 80 y los números de puerto

Publicidad

Publicado en: Python

Interacciones con los lectores

Deja una respuesta Cancelar la respuesta

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

I accept the Terms and Conditions and the Privacy Policy

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Barra lateral principal

Suscríbete a nuestro boletín

Suscríbete al boletín semanal para estar al día de todas las publicaciones.

Política de Privacidad

Analytics Lane en redes sociales

  • Amazon
  • Bluesky
  • Facebook
  • GitHub
  • Instagram
  • Mastodon
  • Pinterest
  • RSS
  • Telegram
  • Tumblr
  • Twitter
  • YouTube

Publicidad

Entradas recientes

== vs is en Python: la diferencia que causa bugs misteriosos

noviembre 3, 2026 Por Daniel Rodríguez

Curiosidad: El puerto 80 y los números de puerto

octubre 29, 2026 Por Daniel Rodríguez

Cómo Proyectar Cuándo Alcanzarás la Independencia Financiera

octubre 27, 2026 Por Daniel Rodríguez

Publicidad

Es tendencia

  • Creación de gráficos de barras y gráficos de columnas con Seaborn publicado el julio 18, 2023 | en Python
  • Cómo comparar datos con barras en Matplotlib: agrupadas, apiladas y porcentuales publicado el mayo 12, 2026 | en Python
  • Copiar y pegar Activar copiar y pegar en VirtualBox publicado el mayo 1, 2019 | en Herramientas
  • Cómo abrir una ventana de Chrome con tamaño y posición específicos desde la línea de comandos en Windows publicado el octubre 14, 2025 | en Herramientas
  • pandas Pandas: Contar los valores nulos en DataFrame publicado el agosto 12, 2021 | en Python

Publicidad

Lo mejor valorado

4.9 (24)

Seleccionar filas y columnas en Pandas con iloc y loc

4.6 (16)

Archivos JSON con Python: lectura y escritura

4.4 (14)

Ordenación de diccionarios en Python mediante clave o valor

4.7 (13)

Operaciones de filtrado de DataFrame con Pandas en base a los valores de las columnas

4.1 (11)

Aplicar el método D’Hondt en Excel

Comentarios recientes

  • Daniel Rodríguez en Curiosidad: La Paradoja de Simpson, o por qué no siempre debes fiarte de los promedios
  • Hatepi en Curiosidad: La Paradoja de Simpson, o por qué no siempre debes fiarte de los promedios
  • Daniel Rodríguez en Los récords con asterisco, o la épica del titular sin contexto – El bestiario de los indicadores económicos absurdos (parte 8 y final)
  • Juan en Los récords con asterisco, o la épica del titular sin contexto – El bestiario de los indicadores económicos absurdos (parte 8 y final)
  • bif en JSON en bases de datos: cuándo es buena idea y cuándo no

Publicidad


Footer

Analytics Lane

  • Acerca de Analytics Lane
  • Boletín de noticias
  • Contacto
  • Libros
  • Lo más popular
  • Noticias
  • Tienda
  • Tiendas afiliadas

Secciones

  • Ciencia de datos
  • Criptografía
  • Herramientas
  • Machine Learning
  • Opinión
  • Productividad
  • Programación
  • Reseñas

Sobre de Analytics Lane

En Analytics Lane tratamos de explicar los principales conceptos de la ciencia e ingeniería de datos con un enfoque práctico. Los principales temas tratados son ciencia de datos, ingeniería de datos, inteligencia artificial, machine learning, deep learning y criptografía. Además, también se habla de los principales lenguajes de programación y herramientas utilizadas por los científicos e ingenieros de datos.

Copyright © 2018-2026 Analytics Lane ·Términos y condiciones ·Política de Cookies ·Política de Privacidad ·Herramientas de privacidad ·Contacto