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-446655440000El 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 errorEl 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_DNSpara dominios;NAMESPACE_URLpara URLs;NAMESPACE_OIDpara OIDs ISO;NAMESPACE_X500para 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 == valoresComparar 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 propiedadLas 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.comEsto 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.







