uuid — Identificadores universalmente únicos

Propósito:El módulo uuid implementa identificadores universalmente únicos como se describe en el RFC 4122.

RFC 4122 define un sistema para crear identificadores universalmente únicos de recursos de una manera que no requiere un registro central. Los valores UUID tienen una longitud de 128 bits y, como guía de referencia dice, «puede garantizar la singularidad a través del espacio y el tiempo». Son útiles para generar identificadores para documentos, hosts, aplicaciones clientes, y otras situaciones donde se necesita un valor único. El RFC se centra específicamente en crear un espacio de nombres de recurso uniforme de nombres y cubre tres algoritmos principales:

  • Uso de direcciones MAC IEEE 802 como fuente de singularidad
  • Uso de números pseudoaleatorios
  • Uso de cadenas conocidas combinadas con hashing criptográfico

En todos los casos, el valor semilla se combina con el reloj del sistema y un valor de secuencia de reloj utilizado para mantener la singularidad en caso de que el reloj sea establecido hacia atrás.

UUID 1 - Direcciones MAC IEEE 802

Los valores de UUID versión 1 se calculan utilizando la dirección MAC del host. El módulo uuid usa getnode() para obtener el valor MAC del sistema actual.

uuid_getnode.py
import uuid

print(hex(uuid.getnode()))

Si un sistema tiene más de una tarjeta de red, y por lo tanto más de una MAC, cualquiera de los valores puede ser devuelto.

$ python3 uuid_getnode.py

0xa860b60304d5

Para generar un UUID para un host, identificado por su dirección MAC, use el función uuid1(). El argumento del identificador de nodo es opcional; deja el campo en blanco para usar el valor devuelto por getnode().

uuid_uuid1.py
import uuid

u = uuid.uuid1()

print(u)
print(type(u))
print('bytes   :', repr(u.bytes))
print('hex     :', u.hex)
print('int     :', u.int)
print('urn     :', u.urn)
print('variant :', u.variant)
print('version :', u.version)
print('fields  :', u.fields)
print('  time_low            : ', u.time_low)
print('  time_mid            : ', u.time_mid)
print('  time_hi_version     : ', u.time_hi_version)
print('  clock_seq_hi_variant: ', u.clock_seq_hi_variant)
print('  clock_seq_low       : ', u.clock_seq_low)
print('  node                : ', u.node)
print('  time                : ', u.time)
print('  clock_seq           : ', u.clock_seq)

Se puede acceder a los componentes del objeto UUID devuelto a través de atributos de instancia de sólo lectura. Algunos atributos, como hex, int, y urn, son diferentes representaciones del valor UUID.

$ python3 uuid_uuid1.py

38332b62-2aea-11e8-b103-a860b60304d5
<class 'uuid.UUID'>
bytes   : b'83+b*\xea\x11\xe8\xb1\x03\xa8`\xb6\x03\x04\xd5'
hex     : 38332b622aea11e8b103a860b60304d5
int     : 74702454824994792138317938288475964629
urn     : urn:uuid:38332b62-2aea-11e8-b103-a860b60304d5
variant : specified in RFC 4122
version : 1
fields  : (942877538, 10986, 4584, 177, 3, 185133323977941)
  time_low            :  942877538
  time_mid            :  10986
  time_hi_version     :  4584
  clock_seq_hi_variant:  177
  clock_seq_low       :  3
  node                :  185133323977941
  time                :  137406974088391522
  clock_seq           :  12547

Debido al componente de tiempo, cada llamada a uuid1() devuelve un nuevo valor.

uuid_uuid1_repeat.py
import uuid

for i in range(3):
    print(uuid.uuid1())

En esta salida, solo el componente de tiempo (al comienzo de la cadena) cambia.

$ python3 uuid_uuid1_repeat.py

3842ca28-2aea-11e8-8fec-a860b60304d5
3844cd18-2aea-11e8-aca3-a860b60304d5
3844cdf4-2aea-11e8-ac38-a860b60304d5

Debido a que cada computadora tiene una dirección MAC diferente, la ejecución del programa de muestra en diferentes sistemas producirá valores completamente diferentes. Este ejemplo pasa IDs de nodo explícitos para simular la ejecución en diferentes hosts.

uuid_uuid1_othermac.py
import uuid

for node in [0x1ec200d9e0, 0x1e5274040e]:
    print(uuid.uuid1(node), hex(node))

Además de un valor de tiempo diferente, el identificador de nodo al final del UUID también cambia.

$ python3 uuid_uuid1_othermac.py

3851ea50-2aea-11e8-936d-001ec200d9e0 0x1ec200d9e0
3852caa6-2aea-11e8-a805-001e5274040e 0x1e5274040e

UUID 3 and 5 - Valores basados en nombres

También es útil en algunos contextos crear valores UUID a partir de nombres en lugar de valores aleatorios o basados en el tiempo. Versiones 3 y 5 de la especificación de UUID utilizan valores hash criptográficos (MD5 o SHA-1, respectivamente) para combinar valores semilla específicos del espacio de nombres con nombres. Hay varios espacios de nombres conocidos, identificados por valores UUID predefinidos, para trabajar con DNS, URL, OIDs ISO y nombres distinguidos X.500. Los nuevos espacios de nombres específicos de la aplicación pueden ser definidos generando y guardando valores UUID.

uuid_uuid3_uuid5.py
import uuid

hostnames = ['www.doughellmann.com', 'blog.doughellmann.com']

for name in hostnames:
    print(name)
    print('  MD5   :', uuid.uuid3(uuid.NAMESPACE_DNS, name))
    print('  SHA-1 :', uuid.uuid5(uuid.NAMESPACE_DNS, name))
    print()

Para crear un UUID a partir de un nombre DNS, pasa uuid.NAMESPACE_DNS como el argumento del espacio de nombres a uuid3() o uuid5():

$ python3 uuid_uuid3_uuid5.py

www.doughellmann.com
  MD5   : bcd02e22-68f0-3046-a512-327cca9def8f
  SHA-1 : e3329b12-30b7-57c4-8117-c2cd34a87ce9

blog.doughellmann.com
  MD5   : 9bdabfce-dfd6-37ab-8a3f-7f7293bcf111
  SHA-1 : fa829736-7ef8-5239-9906-b4775a5abacb

El valor UUID para un nombre dado en un espacio de nombres es siempre el mismo, no importa cuándo o dónde se calcula.

uuid_uuid3_repeat.py
import uuid

namespace_types = sorted(
    n
    for n in dir(uuid)
    if n.startswith('NAMESPACE_')
)
name = 'www.doughellmann.com'

for namespace_type in namespace_types:
    print(namespace_type)
    namespace_uuid = getattr(uuid, namespace_type)
    print(' ', uuid.uuid3(namespace_uuid, name))
    print(' ', uuid.uuid3(namespace_uuid, name))
    print()

Los valores para el mismo nombre en los espacios de nombres son diferentes.

$ python3 uuid_uuid3_repeat.py

NAMESPACE_DNS
  bcd02e22-68f0-3046-a512-327cca9def8f
  bcd02e22-68f0-3046-a512-327cca9def8f

NAMESPACE_OID
  e7043ac1-4382-3c45-8271-d5c083e41723
  e7043ac1-4382-3c45-8271-d5c083e41723

NAMESPACE_URL
  5d0fdaa9-eafd-365e-b4d7-652500dd1208
  5d0fdaa9-eafd-365e-b4d7-652500dd1208

NAMESPACE_X500
  4a54d6e7-ce68-37fb-b0ba-09acc87cabb7
  4a54d6e7-ce68-37fb-b0ba-09acc87cabb7

UUID 4 - Valores aleatorios

A veces, los valores UUID basados en el host y en el espacio de nombres no son «lo suficientemente diferente». Por ejemplo, en los casos en que UUID está destinado a ser utilizado como una clave hash, una secuencia más aleatoria de valores con más diferenciación es deseable para evitar colisiones en la tabla de hashs. Tener valores con menos dígitos comunes también hace que sea más fácil encontrarlos en los archivos de registro. Para agregar una mayor diferenciación en UUIDs, usa uuid4() para generarlos usando valores de entrada aleatorios.

uuid_uuid4.py
import uuid

for i in range(3):
    print(uuid.uuid4())

La fuente de aleatoriedad depende de qué bibliotecas de C están disponibles cuando se importa uuid. Si libuuid (o uuid.dll) puede ser cargada y contiene una función para generar valores aleatorios, es usada. De lo contrario, os.urandom() o el módulo random son usados.

$ python3 uuid_uuid4.py

74695723-65ed-4170-af77-b9f22608535d
db199e25-e292-41cd-b488-80a8f99d163a
196750b3-bbb9-488e-b3ec-62ec0e468bbc

Trabajar con objetos UUID

Además de generar nuevos valores UUID, es posible analizar cadenas en formatos estándar para crear objetos UUID, haciendo más fácil el manejo de comparaciones y operaciones de clasificación.

uuid_uuid_objects.py
import uuid


def show(msg, l):
    print(msg)
    for v in l:
        print(' ', v)
    print()


input_values = [
    'urn:uuid:f2f84497-b3bf-493a-bba9-7c68e6def80b',
    '{417a5ebb-01f7-4ed5-aeac-3d56cd5037b0}',
    '2115773a-5bf1-11dd-ab48-001ec200d9e0',
]

show('input_values', input_values)

uuids = [uuid.UUID(s) for s in input_values]
show('converted to uuids', uuids)

uuids.sort()
show('sorted', uuids)

Las llaves que la rodean se eliminan de la entrada, al igual que los guiones (-). Si la cadena tiene un prefijo que contiene urn: y/o uuid:, también se elimina. El texto restante debe ser una cadena de 16 dígitos hexadecimales, que luego se interpretan como un valor UUID.

$ python3 uuid_uuid_objects.py

input_values
  urn:uuid:f2f84497-b3bf-493a-bba9-7c68e6def80b
  {417a5ebb-01f7-4ed5-aeac-3d56cd5037b0}
  2115773a-5bf1-11dd-ab48-001ec200d9e0

converted to uuids
  f2f84497-b3bf-493a-bba9-7c68e6def80b
  417a5ebb-01f7-4ed5-aeac-3d56cd5037b0
  2115773a-5bf1-11dd-ab48-001ec200d9e0

sorted
  2115773a-5bf1-11dd-ab48-001ec200d9e0
  417a5ebb-01f7-4ed5-aeac-3d56cd5037b0
  f2f84497-b3bf-493a-bba9-7c68e6def80b

Ver también