13.9. uuid — 通用唯一识别码

未匹配的标注

目标:uuid 模块实现了在 RFC 4122 中描述的通用唯一识别码。

RFC 4122 定义了一种不需要中央注册机制的方式来给资源分配通用唯一识别码的系统。 UUID 值有 128 位长,按照指导手册的说法," 能够在空间和时间上保证唯一性。" 该系统对于给文件,主机,应用和其他一些场景中需要唯一标识的资源生成标识码是很有用的。 RFC 特别专注于如何创建统一资源命名空间并涵盖三个主要的算法:

  • 采用 IEEE 802 MAC 地址作为唯一性来源
  • 采用伪随机数
  • 采用知名的字符串并与加密哈希结合

在所有情形中, 种子值总是和系统时钟与时钟序列值结合,这在时钟被反向设置时,能够维持唯一性。

UUID 1 - IEEE 802 MAC Address

UUID 的第一个版本的值是基于主机的 MAC 地址来计算的。 uuid 模块使用 getnode() 来获取当前系统的 MAC 地址值。

uuid_getnode.py

import uuid

print(hex(uuid.getnode()))

如果一个系统的网卡多于一块,那么就有多个 MAC 地址,因此返回的值可能是其中的任意一个。

$ python3 uuid_getnode.py

0xa860b60304d5

要为主机生成一个由它的 MAC 地址标明的 UUID ,那么就使用 uuid1() 函数。节点标识参数不是必须的,如果留空就意味着采用 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)

返回的 UUID 对象的组成部分可以通过实例的只读属性查看。有一些属性,比如 hexint ,和 urn 仅是 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

由于含有时间组成部分,每一次调用 uuid1() 都返回一个新的值。

uuid_uuid1_repeat.py

import uuid

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

在该输出中,仅仅只有时间组成部分(在字符创开头位置)有改变。

$ python3 uuid_uuid1_repeat.py

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

由于每一台电脑的 MAC 地址都不同,在不同系统上运行该示例程序会产生完全不同的值。下面这个例子显式地传递了不同的节点 ID 来模拟示例程序运行在不同主机上的情况。

uuid_uuid1_othermac.py

import uuid

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

除了时间值不一样之外, UUID 末尾处的节点标识也变得不一样了。

$ python3 uuid_uuid1_othermac.py

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

UUID 3 and 5 - 基于名称的值

在某些上下文环境中,创建基于名称而不是随机或者基于时间的 UUID 值也是比较有用的。UUID 版本3和5声明使用基于加密哈希值(MD5 或者 SHA-1)将名称空间的特定种子值与名称组合在一起。这有几个众所周知的命名空间,由预定义的 UUID 值标识,用于处理 DNS,URLs, ISO OIDs, 以及 X.500 可分辨名称。可以通过生成和存储 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()

为了从 DNS 名称创建 UUID。传递 uuid.NAMESPACE_DNS 作为 uuid3() 或者 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

无论何时何地计算,命名空间中给定名称的 UUID 值始终是相同的。

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()

命名空间中相同名称的值是不同的(命名空间和名称都会影响结果):

$ 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 - 随机值

有些时候,基于名称和命名的 UUID 值不够「不同」。例如,在 UUID 作为被用作哈希键的情况下,一个更具有区别性随机序列值才能够更加容易避免哈希碰撞?。具有较少公共数字的值也更容易在日志文件中找到他们。为了给 UUID 带来更多的区别性,请使用 uuid4() 生成他们,它使用了随机输入值。

uuid_uuid4.py

import uuid

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

随机源取决于 uuid 被导入时可用的C库。如果  libuuid 或者 (uuid.dll)能够被加载,将使用库中包含生成随机值的函数。否则使用  os.urandom()  或者使用 random  模块。

$ python3 uuid_uuid4.py

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

处理 UUID 对象

除了生成新的 UUID 值之外,还可以解析标准格式的字符串创建 UUID 对象,更加容易处理比较和排序操作。

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)

输入中周围的花括号被删除了,如短划线(-)。如果字符串中包含前缀  urn: 以及/或者 uuid:,也将会被删除掉。剩下的文本是一个16进制数字的字符串,他们将被解释成一个 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

推荐阅读

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/pymotw/uuid-uni...

译文地址:https://learnku.com/docs/pymotw/uuid-uni...

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~