创建随机字符串和随机十六进制数的最轻量级方法

创建如下30个字符的随机字符串的最轻量级方法是什么?

Ufhy3skj5nca0d2dfh9hwd2tbk9sw1

还有像下面这样30位的十六进制数?

8c6f78ac23b4a7b8c0182d7a89e9b1

125233 次浏览
import string
import random
lst = [random.choice(string.ascii_letters + string.digits) for n in xrange(30)]
s = "".join(lst)
print s
ocwbKCiuAJLRJgM1bWNV1TPSH0F2Lb

30位十六进制字符串:

>>> import os,binascii
>>> print binascii.b2a_hex(os.urandom(15))
"c84766ca4a3ce52c3602bbf02ad1f7"

这样做的好处是可以直接从操作系统获得随机性,这可能比 Random ()更安全和/或更快,而且您不需要为它播种。

顺便说一句,这是在建议的两种方法上使用 timeit的结果:

使用 random.choice():

>>> t1 = timeit.Timer("''.join(random.choice(string.hexdigits) for n in xrange(30))", "import random, string")
>>> t1.timeit()
69.558588027954102

使用 binascii.b2a_hex():

>>> t2 = timeit.Timer("binascii.b2a_hex(os.urandom(15))", "import os, binascii")
>>> t2.timeit()
16.288421154022217

我得到了一个更快的十六进制输出,使用与上面相同的 t1和 t2:

>>> t1 = timeit.Timer("''.join(random.choice('0123456789abcdef') for n in xrange(30))", "import random")
>>> t2 = timeit.Timer("binascii.b2a_hex(os.urandom(15))", "import os, binascii")
>>> t3 = timeit.Timer("'%030x' % random.randrange(16**30)", "import random")
>>> for t in t1, t2, t3:
...     t.timeit()
...
28.165037870407104
9.0292739868164062
5.2836320400238037

t3只对随机模块进行一次调用,不必构建或读取列表,然后使用字符串格式进行其余操作。

注意: random.choice(string.hexdigits)是不正确的,因为 string.hexdigits返回 0123456789abcdefABCDEF(小写和大写) ,所以您将得到一个有偏差的结果,十六进制数字“ c”出现的可能性是数字“7”的两倍。相反,只需使用 random.choice('0123456789abcdef')

与 jcdyer 提到的方法相比,有一个更快的方法。

from numpy.random.mtrand import RandomState
import binascii
rand = RandomState()


lo = 1000000000000000
hi = 999999999999999999
binascii.b2a_hex(rand.randint(lo, hi, 2).tostring())[:30]


>>> timeit.Timer("binascii.b2a_hex(rand.randint(lo,hi,2).tostring())[:30]", \
...                 'from __main__ import lo,hi,rand,binascii').timeit()
1.648831844329834         <-- this is on python 2.6.6
2.253110885620117         <-- this on python 2.7.5

If you want in base64:

binascii.b2a_base64(rand.randint(lo, hi, 3).tostring())[:30]

您可以更改传递给 randint (last arg)的 size 参数,以根据您的需求改变输出长度。因此,对于一个60个字符的人来说:

binascii.b2a_hex(rand.randint(lo, hi, 4).tostring())[:60]

一行功能:

import random
import string


def generate_random_key(length):
return ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(length))


print generate_random_key(30)

比这里的解决方案快得多:

timeit("'%0x' % getrandbits(30 * 4)", "from random import getrandbits")
0.8056681156158447

另一种方法:

from Crypto import Random
import binascii


my_hex_value = binascii.hexlify(Random.get_random_bytes(30))

重点是: Byte value 总是等于十六进制的值

在 Py3.6 + 中,另一种选择是使用新的标准 secrets模块:

>>> import secrets
>>> secrets.token_hex(15)
'8d9bad5b43259c6ee27d9aadc7b832'
>>> secrets.token_urlsafe(30*3//4) # see notes
'teRq7IqhaRU0S3euX1ji9f58WzUkrg'

注意: token_urlsafe()使用 base64编码,这意味着将请求的数字减少 3//4。它也可能包括 _--不清楚这是否可以接受

In [1]: import random


In [2]: hex(random.getrandbits(16))
Out[2]: '0x3b19'

添加一个混合答案,执行速度比@eemz 解决方案更快,而且完全是字母数字。请注意,这样做 没有给你一个十六进制的答案。

import random
import string


LETTERS_AND_DIGITS = string.ascii_letters + string.digits


def random_choice_algo(width):
return ''.join(random.choice(LETTERS_AND_DIGITS) for i in range(width))


def random_choices_algo(width):
return ''.join(random.choices(LETTERS_AND_DIGITS, k=width))




print(generate_random_string(10))
# prints "48uTwINW1D"

快速的基准收益率

from timeit import timeit
from functools import partial


arg_width = 10
print("random_choice_algo", timeit(partial(random_choice_algo, arg_width)))
# random_choice_algo 8.180561417000717
print("random_choices_algo", timeit(partial(random_choices_algo, arg_width)))
# random_choices_algo 3.172438014007639

这当然不是最轻量级的版本,但是它是随机的,并且很容易调整你想要的字母/长度:

import random


def generate(random_chars=12, alphabet="0123456789abcdef"):
r = random.SystemRandom()
return ''.join([r.choice(alphabet) for i in range(random_chars)])