如何在Python中生成唯一的ID?

202
我需要基于随机值生成一个唯一的ID。

3
请问您需要什么类型的唯一标识符?它需要是数字吗?还是可以包含字母?请给出一些标识符类型的例子。 - MitMaro
可能相关的是,所有对象都有一个唯一的id id(my_object)id(self)。对我来说已经足够了,因为在Python中的所有东西都是对象并且具有数字标识符; 字符串:id('Hello World') 类:id('Hello World'),所有东西都有一个标识符。 - ThorSummoner
1
实际上,我在使用id时遇到了一些问题,它似乎与变量名有关,具有相同名称的变量会获得相同的id,只是被替换的变量。除非您进行了良好的单元测试并确信其行为符合您的预期,否则最好避免使用id。 - ThorSummoner
我该如何在答案中添加评论? - Bernardo Troncoso
尽管这个问题被标记为已回答(因此关闭)关于在Python中使用UUID函数,但实际上并不完全正确。答案是“你不能”(假设OP指的是给定某个数量N的生成的ID的唯一ID)。使用随机过程生成的任何ID都有可能(虽然很低)发生冲突;毕竟,这就是过程中的“随机”部分。 - undefined
8个回答

205

也许 uuid.uuid4() 可以胜任。更多信息请参见uuid


29
注意,支持该模块的库存在缺陷,容易派生出会保持FD打开状态的进程。这在新版本中已得到修复,但大多数人可能尚未升级,因此我一般避免使用此模块。对于监听套接字,这曾给我带来严重的头疼。 - Glenn Maynard
48
五年过去了,那仍然是一个问题吗? - Jim Hall
44
@GlennMaynard九年后仍在疑惑。 - PascalVKooten
3
在某些系统上,问题似乎出现在os.urandom函数上。使用UUID(int=random.getrandbits(128), version=4))则表现良好。 - Gellweiler
3
来自未来,想知道过去的错误是否已被解决。 - Haris
显示剩余5条评论

172

2
这应该是答案。更详细! - Mayur Mahajan
4
如果我们只想要特定数量的比特,怎么办? - Muhammad Rafeh Atique

29

唯一和随机是互斥的。也许你想要这个?

import random
def uniqueid():
    seed = random.getrandbits(32)
    while True:
       yield seed
       seed += 1

用法:

unique_sequence = uniqueid()
id1 = next(unique_sequence)
id2 = next(unique_sequence)
id3 = next(unique_sequence)
ids = list(itertools.islice(unique_sequence, 1000))

每个返回的id都不相同(唯一),这是基于一个随机种子值实现的。


7
这并不是独一无二的。如果我启动它两次,并每次生成一百万个值,那么两次运行之间发生碰撞的概率是相当大的。为了避免这种情况,我必须每次存储上一次的“种子”--但这样就没有设置种子的意义了;它只是一个序列生成器。通常情况下,统计学上唯一的ID是从随机数据中生成的;至少一类UUID就是这样工作的。 - Glenn Maynard
1
只要每个唯一序列都来自于唯一的uniqueid调用,它就是唯一的。但是不能保证跨生成器的唯一性。 - SingleNegationElimination
15
在这种条件下,即使是一个计数器也是独一无二的。 - Glenn Maynard
1
用计数器生成唯一ID是否有问题?这是数据库设计中的常见做法。 - SingleNegationElimination
你的用法不是那样的(至少在2.7中不是):你需要调用unique_sequence.next()。 - Gerald Senarclens de Grancy
在数据库中生成顺序唯一的ID号没有任何问题,但通常是因为执行了一个原子(不间断)操作以保证返回的数字是唯一的,因此任何数据库操作都可以依赖获取一个唯一的ID号来操作上下文。 - Avery Payne

9
也许这对您有帮助。
str(uuid.uuid4().fields[-1])[:5]

2
你能详细说明一下你的生成字符串吗? - serup
5
好的,了解这个转变以及它是否仍然独特会很好。 - alisa
6
这如何确保唯一性? - Hassan Baig
@Pjl 非常感谢!这是获得唯一ID的好方法。 - Muhammad Rafeh Atique
10
这不是一个好的解决方案。请参见 https://gist.github.com/randlet/65c3812c648517e365f1d774a0122d18 - randlet
我该如何在答案中添加注释? - Bernardo Troncoso

7
import time
import random
import socket
import hashlib

def guid( *args ):
    """
    Generates a universally unique ID.
    Any arguments only create more randomness.
    """
    t = long( time.time() * 1000 )
    r = long( random.random()*100000000000000000L )
    try:
        a = socket.gethostbyname( socket.gethostname() )
    except:
        # if we can't get a network address, just imagine one
        a = random.random()*100000000000000000L
    data = str(t)+' '+str(r)+' '+str(a)+' '+str(args)
    data = hashlib.md5(data).hexdigest()

    return data

4

这种方法会非常快,但不会生成随机值,而是单调递增的值(对于给定的线程)。

import threading

_uid = threading.local()
def genuid():
    if getattr(_uid, "uid", None) is None:
        _uid.tid = threading.current_thread().ident
        _uid.uid = 0
    _uid.uid += 1
    return (_uid.tid, _uid.uid)

它是线程安全的,使用元组可能比字符串更有益(如果有的话更短)。如果您不需要线程安全,请随意删除线程位(而不是使用 threading.local ,请使用 object()并完全删除 tid )。

希望这可以帮助。


4

在这里您可以找到一个实现:

def __uniqueid__():
    """
      generate unique id with length 17 to 21.
      ensure uniqueness even with daylight savings events (clocks adjusted one-hour backward).

      if you generate 1 million ids per second during 100 years, you will generate 
      2*25 (approx sec per year) * 10**6 (1 million id per sec) * 100 (years) = 5 * 10**9 unique ids.

      with 17 digits (radix 16) id, you can represent 16**17 = 295147905179352825856 ids (around 2.9 * 10**20).
      In fact, as we need far less than that, we agree that the format used to represent id (seed + timestamp reversed)
      do not cover all numbers that could be represented with 35 digits (radix 16).

      if you generate 1 million id per second with this algorithm, it will increase the seed by less than 2**12 per hour
      so if a DST occurs and backward one hour, we need to ensure to generate unique id for twice times for the same period.
      the seed must be at least 1 to 2**13 range. if we want to ensure uniqueness for two hours (100% contingency), we need 
      a seed for 1 to 2**14 range. that's what we have with this algorithm. You have to increment seed_range_bits if you
      move your machine by airplane to another time zone or if you have a glucky wallet and use a computer that can generate
      more than 1 million ids per second.

      one word about predictability : This algorithm is absolutely NOT designed to generate unpredictable unique id.
      you can add a sha-1 or sha-256 digest step at the end of this algorithm but you will loose uniqueness and enter to collision probability world.
      hash algorithms ensure that for same id generated here, you will have the same hash but for two differents id (a pair of ids), it is
      possible to have the same hash with a very little probability. You would certainly take an option on a bijective function that maps
      35 digits (or more) number to 35 digits (or more) number based on cipher block and secret key. read paper on breaking PRNG algorithms 
      in order to be convinced that problems could occur as soon as you use random library :)

      1 million id per second ?... on a Intel(R) Core(TM)2 CPU 6400 @ 2.13GHz, you get :

      >>> timeit.timeit(uniqueid,number=40000)
      1.0114529132843018

      an average of 40000 id/second
    """
    mynow=datetime.now
    sft=datetime.strftime
    # store old datetime each time in order to check if we generate during same microsecond (glucky wallet !)
    # or if daylight savings event occurs (when clocks are adjusted backward) [rarely detected at this level]
    old_time=mynow() # fake init - on very speed machine it could increase your seed to seed + 1... but we have our contingency :)
    # manage seed
    seed_range_bits=14 # max range for seed
    seed_max_value=2**seed_range_bits - 1 # seed could not exceed 2**nbbits - 1
    # get random seed
    seed=random.getrandbits(seed_range_bits)
    current_seed=str(seed)
    # producing new ids
    while True:
        # get current time 
        current_time=mynow()
        if current_time <= old_time:
            # previous id generated in the same microsecond or Daylight saving time event occurs (when clocks are adjusted backward)
            seed = max(1,(seed + 1) % seed_max_value)
            current_seed=str(seed)
        # generate new id (concatenate seed and timestamp as numbers)
        #newid=hex(int(''.join([sft(current_time,'%f%S%M%H%d%m%Y'),current_seed])))[2:-1]
        newid=int(''.join([sft(current_time,'%f%S%M%H%d%m%Y'),current_seed]))
        # save current time
        old_time=current_time
        # return a new id
        yield newid

""" you get a new id for each call of uniqueid() """
uniqueid=__uniqueid__().next

import unittest
class UniqueIdTest(unittest.TestCase):
    def testGen(self):
        for _ in range(3):
            m=[uniqueid() for _ in range(10)]
            self.assertEqual(len(m),len(set(m)),"duplicates found !")

希望这能有所帮助!

3
也许是 uuid 模块吗?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接