uuid en Python: IDs únicos y ordenables

Publicado el: 30/07/2026
Tempo de leitura: 7 minutos
Código digital que representa identificadores UUID únicos y ordenables en Python

Los identificadores aparecen en usuarios, pedidos, archivos, eventos, mensajes, auditorías y sistemas distribuidos. Una secuencia numérica funciona dentro de una sola base de datos, pero exige coordinación cuando varios servicios crean datos al mismo tiempo. El módulo uuid en Python ofrece objetos inmutables de 128 bits que pueden generarse sin una autoridad central y transportarse como texto, bytes o enteros.

Esta guía explica cuándo usar UUIDv4, UUIDv5, UUIDv6 y UUIDv7, por qué UUIDv1 puede exponer información, cómo validar cadenas, almacenar valores en bases de datos, ordenar identificadores y evitar confundir unicidad con seguridad. Complementa nuestros contenidos sobre listas en Python, collections, copias de objetos, zonas horarias y archivos temporales.

Qué es un UUID

UUID significa Universally Unique Identifier. El formato contiene 128 bits y normalmente se muestra como 32 dígitos hexadecimales separados por guiones:

550e8400-e29b-41d4-a716-446655440000

El objetivo es permitir generación descentralizada con una probabilidad extremadamente baja de colisión. No representa una garantía matemática absoluta en todas las versiones e implementaciones, pero es suficiente para la mayoría de sistemas.

Generar un UUIDv4

Para un identificador aleatorio general, la opción más habitual es uuid4().

import uuid

identificador = uuid.uuid4()
print(identificador)
print(type(identificador))
print(identificador.version)

El valor devuelto es un objeto UUID, no solo una cadena. Python genera UUIDv4 con aleatoriedad criptográficamente segura, lo que reduce colisiones y evita exponer directamente tiempo o dirección de red.

Cadena, bytes, entero y URN

Una misma instancia tiene varias representaciones útiles.

import uuid

valor = uuid.uuid4()

print(str(valor))
print(valor.hex)
print(valor.bytes)
print(valor.int)
print(valor.urn)

str() produce el formato estándar con guiones. hex devuelve 32 caracteres sin guiones. bytes ocupa 16 bytes. int expone los 128 bits como entero. urn genera una URI como urn:uuid:....

Validar entrada con UUID()

Cuando una API o la línea de comandos recibe un identificador, conviértelo antes de usarlo.

from uuid import UUID


def validar_uuid(texto: str) -> UUID:
    try:
        return UUID(texto)
    except (ValueError, AttributeError, TypeError) as error:
        raise ValueError("UUID inválido") from error

El constructor acepta cadenas con guiones, texto hexadecimal compacto, llaves y prefijos URN. Después de convertir, las comparaciones y verificaciones de versión son consistentes.

UUIDv1 y privacidad

uuid1() combina tiempo, una secuencia de reloj y un identificador de nodo. Históricamente, el nodo puede derivarse de la dirección MAC de la máquina.

import uuid

valor = uuid.uuid1()
print(valor)
print(valor.node)
print(valor.time)

La documentación oficial de uuid advierte que UUIDv1 puede comprometer la privacidad al incluir la dirección de red. Evítalo en sistemas nuevos salvo que exista una necesidad de compatibilidad.

UUIDv3 y UUIDv5: IDs deterministas

UUIDv3 y UUIDv5 devuelven el mismo identificador para la misma combinación de namespace y nombre. La versión 3 usa MD5; la versión 5 usa SHA-1 y normalmente es preferible entre ambas.

import uuid

id_url = uuid.uuid5(
    uuid.NAMESPACE_URL,
    "https://ejemplo.com/productos/42",
)

print(id_url)

Este comportamiento resulta útil en migraciones, importaciones idempotentes, claves derivadas de URLs y sincronización entre sistemas. Los nombres deben canonicalizarse: mayúsculas, barras finales, normalización Unicode y codificación diferente producen UUIDs distintos.

Namespaces predefinidos

El módulo incluye cuatro namespaces conocidos:

  • NAMESPACE_DNS para dominios;
  • NAMESPACE_URL para URLs;
  • NAMESPACE_OID para OIDs ISO;
  • NAMESPACE_X500 para nombres X.500.

Una organización también puede generar y conservar un namespace propio. Trátalo como parte del contrato de datos, porque cambiarlo modifica todos los identificadores derivados.

UUIDv6: campos temporales reordenados

UUIDv6 reorganiza los campos temporales de UUIDv1 para mejorar la localidad en índices de base de datos. Se añadió a Python 3.14.

import uuid

valor = uuid.uuid6()
print(valor)
print(valor.version)

Es útil para sistemas que ya dependen de la semántica de UUIDv1 y necesitan un orden de bytes más amigable con el banco. Los proyectos nuevos sin legado deberían evaluar UUIDv7.

UUIDv7: tiempo Unix y ordenación

UUIDv7 incorpora un timestamp Unix en milisegundos en los bits más significativos y combina el espacio restante con aleatoriedad y mecanismos de monotonicidad.

import datetime as dt
import uuid

valor = uuid.uuid7()
creado_en = dt.datetime.fromtimestamp(
    valor.time / 1000,
    tz=dt.timezone.utc,
)

print(valor)
print(creado_en)

Los valores generados en secuencia tienden a ordenar cronológicamente, mejorando la localidad de índices B-tree frente a UUIDv4. Esto no convierte el identificador en una marca de auditoría confiable; conserva una columna datetime explícita.

Monotonicidad dentro del mismo milisegundo

Una aplicación puede generar muchos UUIDv7 dentro del mismo milisegundo. La implementación de Python usa un contador para conservar monotonicidad cuando la plataforma no ofrece mayor precisión.

La ordenación útil no es una secuencia sin huecos. Procesos distintos, reinicios y ajustes de reloj pueden requerir reglas adicionales. Usa números de secuencia explícitos cuando el dominio necesite orden total estricto.

UUIDv8: formatos personalizados

UUIDv8 reserva campos para diseños experimentales o específicos de proveedor.

import uuid

valor = uuid.uuid8(
    0x12345678,
    0x9ABC,
    0x11223344,
)
print(valor)

Los argumentos tienen límites de bits y los bits excedentes se truncan. Por defecto, los componentes no se generan con un generador criptográficamente seguro. UUIDv8 no sustituye a UUIDv4 en contextos sensibles.

UUID NIL y MAX

Python 3.14 también incluye valores especiales:

import uuid

print(uuid.NIL)
print(uuid.MAX)

NIL tiene todos los bits en cero y puede representar “sin UUID” en protocolos que exigen 128 bits. MAX tiene todos los bits en uno y puede servir como centinela superior. En bases de datos, prefiere NULL cuando la ausencia pertenece al modelo.

Comparación y ordenación

Los objetos UUID se comparan mediante su valor entero.

valores = [uuid.uuid7() for _ in range(5)]
ordenados = sorted(valores)
assert ordenados == valores

Comparar un UUID con un tipo no relacionado genera TypeError. Normaliza las entradas antes de ordenar o usarlas como claves.

Almacenamiento en bases de datos

Las representaciones habituales son:

  • tipo UUID nativo;
  • 16 bytes binarios;
  • texto con 36 caracteres;
  • texto hexadecimal con 32 caracteres.

El tipo nativo suele aportar validación y operadores adecuados. El binario ahorra espacio, pero requiere cuidado con el orden de bytes y resulta menos legible. El texto es cómodo, aunque mayor.

UUID como clave primaria

Los UUID permiten que clientes y servicios creen registros antes de contactar una base central. Además, no revelan directamente la cantidad de registros como ocurre con enteros consecutivos.

UUIDv4 inserta valores en posiciones aleatorias del índice y puede aumentar fragmentación. UUIDv7 mejora la localidad, pero sigue siendo más grande que un entero de 64 bits. Mide el efecto según base, volumen y patrón de consultas.

UUID no es autorización

Un identificador difícil de adivinar no reemplaza el control de acceso. Incluso UUIDv4 es solo el nombre de un recurso.

# incorrecto: permitir acceso porque el UUID existe
# correcto: verificar usuario, organización, rol y propiedad

Las APIs deben aplicar autenticación y autorización en cada operación. No uses UUID como contraseña, token de sesión o secreto criptográfico.

Colisiones y restricciones únicas

La probabilidad de colisión de UUIDv4 es extremadamente baja, pero la base debe mantener una clave primaria o restricción UNIQUE. El generador reduce la probabilidad; la restricción protege la integridad.

Si ocurre un conflicto, genera otro valor y repite dentro de una transacción controlada. Nunca elimines la restricción porque “UUID no colisiona”.

IDs deterministas e idempotencia

UUIDv5 funciona bien cuando los datos tienen una clave natural estable.

def id_cliente(sistema: str, codigo: str) -> uuid.UUID:
    nombre = f"{sistema.strip().lower()}:{codigo.strip()}"
    return uuid.uuid5(uuid.NAMESPACE_URL, nombre)

La canonicalización debe documentarse y probarse. Si una versión futura cambia la regla, versiona el namespace o incluye la versión en el nombre.

Serialización JSON

El módulo json estándar no serializa objetos UUID automáticamente. Convierte a cadena en la frontera.

import json
import uuid

registro = {"id": str(uuid.uuid4()), "nombre": "Ana"}
texto = json.dumps(registro)
print(texto)

Al leer, reconstruye mediante UUID(). Frameworks web y ORMs suelen ofrecer campos UUID nativos.

Uso desde la línea de comandos

Desde Python 3.12, el módulo puede ejecutarse como script. Python 3.14 añadió versiones 6, 7 y 8, además de generación múltiple.

python -m uuid
python -m uuid -u uuid7
python -m uuid -C 10
python -m uuid -u uuid5 -n @url -N https://ejemplo.com

Esto ayuda en pruebas, migraciones y despliegues sin escribir un programa separado.

RFC 9562 y compatibilidad

El RFC 9562, publicado en 2024, sustituyó al RFC 4122. Conserva las versiones tradicionales y estandariza UUIDv6, UUIDv7, UUIDv8, NIL y MAX.

El código antiguo aún puede mostrar la constante RFC_4122 por compatibilidad. El nombre permanece, aunque el diseño actual se documenta en el RFC nuevo.

Errores frecuentes

  • Usar UUIDv1 sin considerar privacidad.
  • Elegir UUIDv4 esperando orden temporal.
  • Usar UUIDv7 como única fecha de auditoría.
  • Tratar UUID como token de acceso.
  • Guardar texto sin validar.
  • Eliminar restricciones únicas.
  • Usar UUIDv5 sin canonicalizar el nombre.
  • Adoptar UUIDv8 sin documentar el formato.

Buenas prácticas

  • Usa UUIDv4 para identificadores aleatorios generales.
  • Usa UUIDv5 para IDs deterministas por nombre.
  • Considera UUIDv7 para bancos que se benefician del orden temporal.
  • Evita UUIDv1 en sistemas nuevos por privacidad.
  • Usa tipos UUID nativos cuando sea posible.
  • Mantén restricciones de unicidad.
  • Separa identidad de autorización.
  • Prueba parsing, versiones, round-trip y ordenación.

Conclusión

El módulo uuid en Python ofrece familias de identificadores para contratos distintos. UUIDv4 proporciona aleatoriedad segura, UUIDv5 genera valores deterministas, UUIDv6 mejora el orden temporal del legado UUIDv1 y UUIDv7 combina tiempo Unix con buena localidad para bases de datos. UUIDv8 queda reservado a formatos personalizados.

La elección correcta depende de generación, orden, reproducibilidad y privacidad. Con validación, un tipo adecuado en el banco, restricciones únicas y autorización independiente, los UUID permiten crear identificadores distribuidos sin coordinación central ni secuencias globales.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Archivos organizados que representan almacenamiento temporal seguro en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    tempfile en Python: archivos temporales

    Aprende tempfile en Python para crear archivos y carpetas temporales seguros, con limpieza automática en Windows y Unix.

    Ler mais

    Tempo de leitura: 8 minutos
    29/07/2026
    Relojes que representan zonas horarias internacionales con zoneinfo en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    zoneinfo en Python: zonas horarias

    Aprende zoneinfo en Python para convertir zonas, tratar horario de verano, fold, UTC y tzdata sin errores de programación.

    Ler mais

    Tempo de leitura: 7 minutos
    29/07/2026
    Icono de documentos duplicados que representa copias superficiales y profundas en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    copy en Python: copia superficial y profunda

    Aprende copy en Python para crear copias superficiales, profundas y reemplazar campos sin compartir objetos mutables por error.

    Ler mais

    Tempo de leitura: 7 minutos
    28/07/2026
    Monitor con búsqueda binaria y listas ordenadas en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    bisect en Python: listas siempre ordenadas

    Aprende bisect en Python para mantener listas ordenadas, localizar rangos, encontrar vecinos e insertar valores con búsqueda binaria.

    Ler mais

    Tempo de leitura: 9 minutos
    27/07/2026
    Desarrollador implementando una cola de prioridad con heapq en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    heapq en Python: colas de prioridad

    Aprende heapq en Python para crear colas de prioridad, encontrar valores mínimos y procesar tareas con heaps eficientes.

    Ler mais

    Tempo de leitura: 7 minutos
    26/07/2026
    Compactando arquivos ZIP automaticamente com Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear archivos ZIP con Python y zipfile

    Crea archivos ZIP con Python y zipfile: carpetas, filtros, arcname, compresión, contenido en memoria, verificación, hashes y backups seguros.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026