Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Usa la rueda del ratón o gestos táctiles para hacer zoom • Arrastra para mover

Operadores de Identidad y Pertenencia

Los operadores de identidad y pertenencia son como los inspectores especializados de tu almacén. Mientras que los operadores de comparación verifican si dos productos son similares, estos operadores van más profundo: verifican si dos productos son exactamente el mismo objeto (identidad) o si un producto pertenece a una colección específica (pertenencia).

Imagina que tienes dos cajas idénticas en tu almacén. Aunque contengan lo mismo, son físicamente cajas diferentes. Los operadores de identidad te ayudan a distinguir entre “contenido igual” y “objeto exactamente el mismo”.

Operadores de Identidad

El Operador is

El operador is verifica si dos variables apuntan exactamente al mismo objeto en memoria.

# Ejemplo básico con números pequeños
a = 5
b = 5

print(f"a == b: {a == b}")  # True (mismo valor)
print(f"a is b: {a is b}")  # True (mismo objeto, Python optimiza números pequeños)

# Ejemplo con listas
lista1 = [1, 2, 3]
lista2 = [1, 2, 3]
lista3 = lista1

print(f"lista1 == lista2: {lista1 == lista2}")  # True (mismo contenido)
print(f"lista1 is lista2: {lista1 is lista2}")  # False (objetos diferentes)
print(f"lista1 is lista3: {lista1 is lista3}")  # True (mismo objeto)

Casos Prácticos del Operador is

# 1. Verificar valores especiales
def procesar_inventario(productos):
    if productos is None:
        print("⚠️ No se proporcionó lista de productos")
        return []
    
    if productos is []:  # ❌ INCORRECTO - no usar is con listas vacías
        print("Lista vacía")
    
    if productos == []:  # ✅ CORRECTO - usar == para comparar contenido
        print("Lista vacía")
        return []
    
    return productos

# Ejemplos
print(procesar_inventario(None))
print(procesar_inventario([]))
print(procesar_inventario(['producto1', 'producto2']))

# 2. Verificar singleton (objetos únicos)
class AlmacenCentral:
    _instancia = None
    
    def __new__(cls):
        if cls._instancia is None:
            cls._instancia = super().__new__(cls)
        return cls._instancia
    
    def __init__(self):
        if not hasattr(self, 'inicializado'):
            self.productos = []
            self.inicializado = True

# Verificar que siempre obtenemos la misma instancia
almacen1 = AlmacenCentral()
almacen2 = AlmacenCentral()

print(f"almacen1 == almacen2: {almacen1 == almacen2}")  # True
print(f"almacen1 is almacen2: {almacen1 is almacen2}")  # True (mismo objeto)

# 3. Verificar tipos específicos
import types

def analizar_variable(variable):
    if variable is True:
        return "Es exactamente True"
    elif variable is False:
        return "Es exactamente False"
    elif variable is None:
        return "Es None"
    elif type(variable) is int:
        return f"Es un entero: {variable}"
    elif type(variable) is str:
        return f"Es una cadena: '{variable}'"
    else:
        return f"Es de tipo: {type(variable).__name__}"

# Ejemplos
print(analizar_variable(True))
print(analizar_variable(1))  # Diferente de True
print(analizar_variable(None))
print(analizar_variable(""))  # Cadena vacía vs None

El Operador is not

El operador is not es la negación de is. Verifica si dos variables NO apuntan al mismo objeto.

# Verificaciones de seguridad
def validar_usuario(usuario):
    if usuario is not None:
        if usuario.activo is not False:  # No es exactamente False
            return True
    return False

class Usuario:
    def __init__(self, nombre, activo=True):
        self.nombre = nombre
        self.activo = activo

# Ejemplos
usuario1 = Usuario("Juan", True)
usuario2 = Usuario("María", False)
usuario3 = None

print(f"Usuario1 válido: {validar_usuario(usuario1)}")  # True
print(f"Usuario2 válido: {validar_usuario(usuario2)}")  # False
print(f"Usuario3 válido: {validar_usuario(usuario3)}")  # False

# Comparación con listas
productos_originales = ["leche", "pan", "huevos"]
productos_copia = productos_originales.copy()
productos_referencia = productos_originales

print(f"Copia is not original: {productos_copia is not productos_originales}")      # True
print(f"Referencia is not original: {productos_referencia is not productos_originales}")  # False

Operadores de Pertenencia

El Operador in

El operador in verifica si un elemento pertenece a una colección (lista, tupla, set, diccionario, cadena, etc.).

# Verificación básica de pertenencia
productos_disponibles = ["leche", "pan", "huevos", "queso", "yogur"]
producto_buscado = "pan"

if producto_buscado in productos_disponibles:
    print(f"✅ {producto_buscado} está disponible")
else:
    print(f"❌ {producto_buscado} no está disponible")

# Con cadenas
codigo_producto = "ABC123DEF"
if "123" in codigo_producto:
    print("Código contiene '123'")

# Con diccionarios (verifica llaves por defecto)
inventario = {
    "leche": 50,
    "pan": 25,
    "huevos": 100
}

if "leche" in inventario:
    print(f"Tenemos {inventario['leche']} unidades de leche")

Casos Prácticos del Operador in

# 1. Sistema de permisos por roles
class SistemaAcceso:
    def __init__(self):
        self.roles_admin = {"admin", "superusuario", "gerente"}
        self.roles_empleado = {"empleado", "vendedor", "operario"}
        self.areas_restringidas = {"almacen_seguro", "oficina_gerencia", "deposito_quimicos"}
    
    def puede_acceder(self, usuario_rol, area):
        if usuario_rol in self.roles_admin:
            return True  # Admins pueden acceder a todo
        
        if area in self.areas_restringidas:
            return False  # Empleados no pueden acceder a áreas restringidas
        
        return usuario_rol in self.roles_empleado

# Ejemplo de uso
sistema = SistemaAcceso()

print(f"Gerente puede acceder a almacén seguro: {sistema.puede_acceder('gerente', 'almacen_seguro')}")      # True
print(f"Empleado puede acceder a almacén seguro: {sistema.puede_acceder('empleado', 'almacen_seguro')}")    # False
print(f"Vendedor puede acceder a sala ventas: {sistema.puede_acceder('vendedor', 'sala_ventas')}")         # True

# 2. Validación de datos de entrada
def validar_codigo_producto(codigo):
    """Validar que el código de producto tenga el formato correcto"""
    caracteres_validos = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    caracteres_especiales = ["-", "_"]
    
    if len(codigo) < 3:
        return False, "Código muy corto"
    
    for caracter in codigo:
        if caracter not in caracteres_validos and caracter not in caracteres_especiales:
            return False, f"Caracter inválido: '{caracter}'"
    
    # Verificar que tenga al menos un número
    tiene_numero = any(char in "0123456789" for char in codigo)
    if not tiene_numero:
        return False, "Debe contener al menos un número"
    
    return True, "Código válido"

# Ejemplos de validación
codigos_prueba = ["ABC123", "XYZ", "ABC-123_DEF", "ABC@123", "ABCDEF"]

for codigo in codigos_prueba:
    valido, mensaje = validar_codigo_producto(codigo)
    print(f"'{codigo}': {mensaje}")

# 3. Filtros de búsqueda inteligente
class BuscadorProductos:
    def __init__(self):
        self.productos = [
            {"nombre": "Leche entera", "categoria": "lacteos", "precio": 2.50, "tags": ["lacteo", "bebida", "nutritivo"]},
            {"nombre": "Pan integral", "categoria": "panaderia", "precio": 1.80, "tags": ["cereal", "fibra", "saludable"]},
            {"nombre": "Yogur griego", "categoria": "lacteos", "precio": 3.20, "tags": ["lacteo", "proteina", "saludable"]},
            {"nombre": "Cerveza artesanal", "categoria": "bebidas", "precio": 4.50, "tags": ["bebida", "alcohol", "artesanal"]},
        ]
    
    def buscar(self, termino_busqueda, categoria=None, precio_max=None):
        """Buscar productos usando múltiples criterios"""
        resultados = []
        termino_lower = termino_busqueda.lower()
        
        for producto in self.productos:
            # Verificar si el término está en el nombre o tags
            nombre_match = termino_lower in producto["nombre"].lower()
            tag_match = any(termino_lower in tag for tag in producto["tags"])
            
            if not (nombre_match or tag_match):
                continue
            
            # Filtrar por categoría si se especifica
            if categoria and categoria not in producto["categoria"]:
                continue
            
            # Filtrar por precio máximo si se especifica
            if precio_max and producto["precio"] > precio_max:
                continue
            
            resultados.append(producto)
        
        return resultados

# Ejemplo de búsqueda
buscador = BuscadorProductos()

print("=== BÚSQUEDAS ===")
print("Búsqueda 'leche':")
for producto in buscador.buscar("leche"):
    print(f"  - {producto['nombre']} (${producto['precio']})")

print("\nBúsqueda 'saludable' con precio máximo $3:")
for producto in buscador.buscar("saludable", precio_max=3.0):
    print(f"  - {producto['nombre']} (${producto['precio']})")

print("\nBúsqueda 'bebida' en categoría 'lacteos':")
for producto in buscador.buscar("bebida", categoria="lacteos"):
    print(f"  - {producto['nombre']} (${producto['precio']})")

El Operador not in

El operador not in verifica si un elemento NO pertenece a una colección.

# Control de productos prohibidos
productos_prohibidos = ["tabaco", "alcohol", "medicamentos_controlados"]
productos_pedido = ["leche", "pan", "tabaco", "yogur"]

productos_validos = []
productos_rechazados = []

for producto in productos_pedido:
    if producto not in productos_prohibidos:
        productos_validos.append(producto)
    else:
        productos_rechazados.append(producto)

print(f"Productos válidos: {productos_validos}")
print(f"Productos rechazados: {productos_rechazados}")

# Verificación de disponibilidad
stock_actual = {"leche": 10, "pan": 5, "huevos": 20}
productos_solicitados = ["leche", "pan", "mantequilla", "huevos"]

for producto in productos_solicitados:
    if producto not in stock_actual:
        print(f"⚠️ {producto} no está en stock")
    elif stock_actual[producto] == 0:
        print(f"⚠️ {producto} está agotado")
    else:
        print(f"✅ {producto} disponible: {stock_actual[producto]} unidades")

Aplicaciones Avanzadas

Sistema de Control de Calidad

class ControlCalidad:
    def __init__(self):
        # Criterios de calidad por categoría
        self.criterios_calidad = {
            "lacteos": {"temperatura_max": 4, "dias_vencimiento_min": 3},
            "carnes": {"temperatura_max": 2, "dias_vencimiento_min": 1},
            "vegetales": {"humedad_max": 85, "dias_vencimiento_min": 2},
            "enlatados": {"abolladuras": False, "dias_vencimiento_min": 30}
        }
        
        # Defectos que causan rechazo automático
        self.defectos_criticos = {"moho", "mal_olor", "envase_roto", "fecha_vencida"}
        
        # Certificaciones requeridas
        self.certificaciones_requeridas = {"organico", "sin_gluten", "kosher", "halal"}
    
    def evaluar_producto(self, producto):
        """Evaluar si un producto pasa el control de calidad"""
        categoria = producto.get("categoria")
        defectos = producto.get("defectos", [])
        certificaciones = producto.get("certificaciones", [])
        
        # Verificar defectos críticos
        for defecto in defectos:
            if defecto in self.defectos_criticos:
                return False, f"Defecto crítico detectado: {defecto}"
        
        # Verificar criterios específicos de la categoría
        if categoria in self.criterios_calidad:
            criterios = self.criterios_calidad[categoria]
            
            for criterio, valor_requerido in criterios.items():
                if criterio in producto:
                    valor_actual = producto[criterio]
                    
                    if "max" in criterio and valor_actual > valor_requerido:
                        return False, f"Excede {criterio}: {valor_actual} > {valor_requerido}"
                    elif "min" in criterio and valor_actual < valor_requerido:
                        return False, f"Por debajo de {criterio}: {valor_actual} < {valor_requerido}"
                    elif isinstance(valor_requerido, bool) and valor_actual != valor_requerido:
                        return False, f"Criterio {criterio} no cumplido"
        
        # Verificar certificaciones especiales (si las tiene)
        for cert in certificaciones:
            if cert not in self.certificaciones_requeridas:
                print(f"⚠️ Certificación no reconocida: {cert}")
        
        return True, "Producto aprobado"

# Ejemplos de productos
productos_test = [
    {
        "nombre": "Leche orgánica",
        "categoria": "lacteos",
        "temperatura_max": 3,
        "dias_vencimiento_min": 5,
        "defectos": [],
        "certificaciones": ["organico"]
    },
    {
        "nombre": "Carne de res",
        "categoria": "carnes",
        "temperatura_max": 1,
        "dias_vencimiento_min": 2,
        "defectos": ["mal_olor"],
        "certificaciones": ["halal"]
    },
    {
        "nombre": "Lechuga",
        "categoria": "vegetales",
        "humedad_max": 80,
        "dias_vencimiento_min": 3,
        "defectos": [],
        "certificaciones": []
    }
]

# Evaluar productos
control = ControlCalidad()
print("=== CONTROL DE CALIDAD ===")

for producto in productos_test:
    aprobado, mensaje = control.evaluar_producto(producto)
    estado = "✅ APROBADO" if aprobado else "❌ RECHAZADO"
    print(f"{producto['nombre']}: {estado} - {mensaje}")

Cache Inteligente con Identidad

class CacheInteligente:
    def __init__(self):
        self.cache = {}
        self.objetos_cacheados = set()  # Para verificar identidad
    
    def obtener(self, clave, funcion_calculo, *args, **kwargs):
        """Obtener valor del cache o calcularlo si no existe"""
        
        # Verificar si ya está en cache
        if clave in self.cache:
            valor_cacheado, timestamp = self.cache[clave]
            
            # Verificar si el objeto sigue siendo el mismo (identidad)
            if id(valor_cacheado) in self.objetos_cacheados:
                print(f"📋 Cache hit para: {clave}")
                return valor_cacheado
            else:
                print(f"⚠️ Objeto cambió, recalculando: {clave}")
                del self.cache[clave]
        
        # Calcular nuevo valor
        print(f"🔄 Calculando: {clave}")
        nuevo_valor = funcion_calculo(*args, **kwargs)
        
        # Guardar en cache
        import time
        self.cache[clave] = (nuevo_valor, time.time())
        self.objetos_cacheados.add(id(nuevo_valor))
        
        return nuevo_valor
    
    def invalidar(self, clave):
        """Invalidar entrada específica del cache"""
        if clave in self.cache:
            valor, _ = self.cache[clave]
            self.objetos_cacheados.discard(id(valor))
            del self.cache[clave]
            print(f"🗑️ Cache invalidado para: {clave}")

def calcular_inventario_costoso(productos):
    """Simulación de cálculo costoso"""
    import time
    time.sleep(0.1)  # Simular trabajo pesado
    
    total = sum(p.get("precio", 0) * p.get("cantidad", 0) for p in productos)
    return {"total": total, "productos": len(productos)}

# Ejemplo de uso
cache = CacheInteligente()

productos_almacen = [
    {"nombre": "Leche", "precio": 2.5, "cantidad": 100},
    {"nombre": "Pan", "precio": 1.8, "cantidad": 50}
]

# Primera llamada (cálculo)
resultado1 = cache.obtener("inventario_total", calcular_inventario_costoso, productos_almacen)
print(f"Resultado 1: {resultado1}")

# Segunda llamada (cache hit)
resultado2 = cache.obtener("inventario_total", calcular_inventario_costoso, productos_almacen)
print(f"Resultado 2: {resultado2}")

# Verificar identidad
print(f"Mismo objeto: {resultado1 is resultado2}")

# Invalidar y recalcular
cache.invalidar("inventario_total")
resultado3 = cache.obtener("inventario_total", calcular_inventario_costoso, productos_almacen)
print(f"Resultado 3: {resultado3}")
print(f"Nuevo objeto: {resultado1 is not resultado3}")

Mejores Prácticas y Errores Comunes

✅ Casos Correctos

# 1. Usar 'is' con None, True, False
def procesar_datos(datos):
    if datos is None:
        return "Sin datos"
    if datos is True:
        return "Procesamiento activado"
    if datos is False:
        return "Procesamiento desactivado"
    return f"Procesando: {datos}"

# 2. Usar 'in' para verificar pertenencia
categorias_validas = {"lacteos", "carnes", "verduras", "frutas"}
if categoria in categorias_validas:
    print("Categoría válida")

# 3. Usar '==' para comparar valores
lista1 = [1, 2, 3]
lista2 = [1, 2, 3]
if lista1 == lista2:  # Comparar contenido
    print("Listas iguales")

❌ Errores Comunes

# 1. NO usar 'is' para comparar valores
numero1 = 1000
numero2 = 1000
if numero1 is numero2:  # ❌ INCORRECTO - puede ser impredecible
    print("Números iguales")

if numero1 == numero2:  # ✅ CORRECTO
    print("Números iguales")

# 2. NO usar 'is' con listas vacías
lista = []
if lista is []:  # ❌ INCORRECTO
    print("Lista vacía")

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

if not lista:    # ✅ MEJOR - más pythónico
    print("Lista vacía")

# 3. Cuidado con la mutabilidad
original = [1, 2, 3]
copia = original.copy()
referencia = original

print(f"original is copia: {original is copia}")           # False
print(f"original is referencia: {original is referencia}") # True
print(f"original == copia: {original == copia}")           # True

Tabla de Referencia Rápida

OperadorPropósitoEjemploResultado
isIdentidad (mismo objeto)a is bTrue si son el mismo objeto
is notNo identidada is not bTrue si son objetos diferentes
inPertenenciax in listaTrue si x está en lista
not inNo pertenenciax not in listaTrue si x no está en lista

Cuándo Usar Cada Operador

Usa is cuando:

  • Compares con None, True, o False
  • Verifices tipos específicos con type()
  • Trabajes con singletons
  • Necesites verificar identidad de objetos

Usa in y not in cuando:

  • Verifiques si un elemento está en una colección
  • Busques subcadenas en strings
  • Implementes filtros o validaciones
  • Trabajes con permisos o listas de control

Los operadores de identidad y pertenencia son herramientas precisas que te permiten hacer verificaciones específicas y eficientes. ¡Dominarlos te dará un control más fino sobre tu código!