如何在 Python 中生成可重现(带有种子)的随机 UUID

问题描述 投票:0回答:9

Python模块uuid

uuid4()
函数生成一个随机的UUID,而且好像每次生成的都不一样:

In [1]: import uuid

In [2]: uuid.uuid4()
Out[2]: UUID('f6c9ad6c-eea0-4049-a7c5-56253bc3e9c0')

In [3]: uuid.uuid4()
Out[3]: UUID('2fc1b6f9-9052-4564-9be0-777e790af58f')

我希望每次运行脚本时都能生成相同的随机 UUID——也就是说,我想在

uuid4()
中播种随机生成器。有没有办法做到这一点? (或通过其他方式实现)?

到目前为止我尝试了什么

我必须使用

uuid.UUID()
方法生成一个 UUID,并将随机 128 位整数(来自
random.Random()
的种子实例)作为输入:

import uuid
import random

rd = random.Random()
rd.seed(0)
uuid.UUID(rd.getrandbits(128))

然而,

UUID()
似乎不接受这个作为输入:

Traceback (most recent call last):
  File "uuid_gen_seed.py", line 6, in <module>
    uuid.UUID(rd.getrandbits(128))
  File "/usr/lib/python2.7/uuid.py", line 133, in __init__
    hex = hex.replace('urn:', '').replace('uuid:', '')
AttributeError: 'long' object has no attribute 'replace'

还有其他建议吗?

python random uuid
9个回答
51
投票

快到了:

uuid.UUID(int=rd.getrandbits(128))

这是在

help
的帮助下确定的:

>>> help(uuid.UUID.__init__)
Help on method __init__ in module uuid:

__init__(self, hex=None, bytes=None, bytes_le=None, fields=None, int=None, version=None) unbound uuid.UUID method
    Create a UUID from either a string of 32 hexadecimal digits,
    a string of 16 bytes as the 'bytes' argument, a string of 16 bytes
    in little-endian order as the 'bytes_le' argument, a tuple of six
    integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
    8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
    the 'fields' argument, or a single 128-bit integer as the 'int'
    argument.  When a string of hex digits is given, curly braces,
    hyphens, and a URN prefix are all optional.  For example, these
    expressions all yield the same UUID:

    UUID('{12345678-1234-5678-1234-567812345678}')
    UUID('12345678123456781234567812345678')
    UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
    UUID(bytes='\x12\x34\x56\x78'*4)
    UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' +
                  '\x12\x34\x56\x78\x12\x34\x56\x78')
    UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
    UUID(int=0x12345678123456781234567812345678)

    Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', or 'int' must
    be given.  The 'version' argument is optional; if given, the resulting
    UUID will have its variant and version set according to RFC 4122,
    overriding the given 'hex', 'bytes', 'bytes_le', 'fields', or 'int'.

29
投票

Faker 让这变得简单

>>> from faker import Faker
>>> f1 = Faker()
>>> f1.seed(4321)
>>> print(f1.uuid4())
cc733c92-6853-15f6-0e49-bec741188ebb
>>> print(f1.uuid4())
a41f020c-2d4d-333f-f1d3-979f1043fae0
>>> f1.seed(4321)
>>> print(f1.uuid4())
cc733c92-6853-15f6-0e49-bec741188ebb

19
投票

这是基于这里使用的解决方案

import hashlib
import uuid

m = hashlib.md5()
m.update(seed.encode('utf-8'))
new_uuid = uuid.UUID(m.hexdigest())

9
投票

由于尚未发布生成一致的版本 4 UUID 的直接解决方案:

import random
import uuid

rnd = random.Random()
rnd.seed(123) # NOTE: Of course don't use a static seed in production

random_uuid = uuid.UUID(int=rnd.getrandbits(128), version=4)

然后在哪里可以看到:

>>> random_uuid.version
4

这不仅仅是“模拟”版本信息。它创建了一个合适的 UUIDv4:

版本参数是可选的;如果给定,生成的 UUID 将根据 RFC 4122 设置其变体和版本号,覆盖给定的十六进制、字节、bytes_le、字段或 int 中的位。

Python 3.8 文档


6
投票

如果有人需要在种子 UUID 中进行猴子补丁,请在此处添加。我的代码使用

uuid.uuid4()
但为了测试我想要一致的 UUID。以下代码是我如何做到的:

import uuid
import random

# -------------------------------------------
# Remove this block to generate different
# UUIDs everytime you run this code.
# This block should be right below the uuid
# import.
rd = random.Random()
rd.seed(0)
uuid.uuid4 = lambda: uuid.UUID(int=rd.getrandbits(128))
# -------------------------------------------

# Then normal code:

print(uuid.uuid4().hex)
print(uuid.uuid4().hex)
print(uuid.uuid4().hex)
print(uuid.uuid4().hex)

4
投票

基于 alex 的解决方案,以下将提供适当的 UUID4:

random.seed(123210912)
a = "%32x" % random.getrandbits(128)
rd = a[:12] + '4' + a[13:16] + 'a' + a[17:]
uuid4 = uuid.UUID(rd)

4
投票

基于@user10229295 的答案的简单解决方案,并附有关于种子的评论。 编辑队列已满,所以我打开了一个新的答案:

import hashlib
import uuid

seed = 'Type your seed_string here' #Read comment below

m = hashlib.md5()
m.update(seed.encode('utf-8'))
new_uuid = uuid.UUID(m.hexdigest())

关于字符串'seed'的评论: 它将是生成 UUID 的种子:从相同的种子字符串将始终生成相同的 UUID。您可以将具有一定意义的整数转换为字符串,连接不同的字符串并将结果用作种子。有了这个,您将可以控制生成的 UUID,这意味着您将能够在知道您使用的种子的情况下重现您的 UUID:使用相同的种子,从中生成的 UUID 将是相同的。


0
投票

快速入门

如果您的目标是可重现的 UUID,这里有一个简洁的方法

import uuid
seeded_uuid = uuid.UUID(bytes=b"z123456789101112") # 7a313233-3435-3637-3839-313031313132

这在内部如何运作?

使用二进制字符串几乎可以让任何东西充当种子。您还可以使用替代的确定性哈希,它会获取一些数据并为您提供一个表示该数据的 32 字节字符串。在 uuid 调用下面有很多复杂的东西,但它的核心是种子 uuid 调用是如何工作的

initial_seed = b"z123456789101112"

# use this function to validate initial seed
is_valid = lambda x: len(x) == 16 and isinstance(x, bytes)

# for each byte get its unicode int value, convert to hex and concatenate as string
hex_rep = "".join([f"{b:x}" for b in initial_seed]) # 7a313233343536373839313031313132

# for a uuid, storing an int representation unlocks O(1) comparision
int_rep = int(hex_rep, base=16)  # 162421256209101963464626711665304482098

# string representation for readability
str_rep = f"\
{hex_rep[0:8]}-\
{hex_rep[8:12]}-\
{hex_rep[12:16]}-\
{hex_rep[16:20]}-\
{hex_rep[20:]}"  # 7a313233-3435-3637-3839-313031313132


0
投票

如其他答案所示,可以直接从种子生成 UUID。但是,如果种子不是唯一的(≈ 加密安全),则生成的 UUID 将不是唯一的可能性不可忽略,因为其他应用程序可以从同一种子生成 UUID。

我建议将种子与秘密结合起来。这可以通过 HMAC 来实现。这是一个实际的例子:

import hmac
from hashlib import sha256
from uuid import UUID

secret = b'my-unique-secret'
seed = b'whatever'
h = hmac.new(secret, msg=seed, digestmod=sha256)
# digest should be truncated to 128 bits = 16 bytes
UUID(bytes=h.digest()[:16], version=4)
© www.soinside.com 2019 - 2024. All rights reserved.