Reputation: 26567
What is the most lightweight way to create a random string of 30 characters like the following?
ufhy3skj5nca0d2dfh9hwd2tbk9sw1
And an hexadecimal number of 30 digits like the followin?
8c6f78ac23b4a7b8c0182d7a89e9b1
Upvotes: 126
Views: 157548
Reputation: 4377
30 digit hex string:
>>> import os
>>> os.urandom(15).hex()
"c84766ca4a3ce52c3602bbf02ad1f7"
The advantage is that this gets randomness directly from the OS, which might be more secure and/or faster than the random(), and you don't have to seed it.
Upvotes: 98
Reputation: 59
Random String (letters and numbers):
import random
import string
def generate_random_string(length=30):
characters = string.ascii_letters + string.digits
result = ''.join(random.choice(characters) for _ in range(length))
return result
random_string = generate_random_string()
print(random_string)
Random Hexadecimal String:
import random
import string
def generate_random_hex_string(length=30):
characters = string.digits + 'abcdef' # Hexadecimal digits
result = ''.join(random.choice(characters) for _ in range(length))
return result
random_hex_string = generate_random_hex_string()
print(random_hex_string)
For basic random string generation, the random
and string
modules are very lightweight and efficient because they are part of Python's standard library. This means you don't need to install any external packages. So that will be most lightweight and quickest way.
If you need these strings for security-sensitive purposes, like generating passwords or API tokens, consider using the secrets module instead:
import secrets
import string
# Random string of 30 characters
random_string = ''.join(secrets.choice(string.ascii_lowercase + string.digits) for _ in range(30))
# Hexadecimal number of 30 digits
random_hex = hex(secrets.randbits(120))[2:] # 120 bits is 30 hexadecimal digits
print(random_string) # 03ulu22q2yxxel4h1a4xwxuf15xemx
print(random_hex) # 539aaca57247132fd08a325a514aea
Upvotes: 0
Reputation: 30268
In Py3.6+, another option is to use the new standard secrets
module:
Random alphanumeric string:
In []:
import secrets
n = 30*3//4 # see note
secrets.token_urlsafe(n)
Out[]:
'teRq7IqhaRU0S3euX1ji9f58WzUkrg'
Note: token_urlsafe()
uses base64 encoding which means you need to reduce the requested number by 3//4
to get exactly 30
. Alternatively, split the returned string to the first 30.
It may also include _-
- unclear if that is acceptable
Random hexadecimal string:
In []:
secrets.token_hex(15)
Out[]:
'8d9bad5b43259c6ee27d9aadc7b832'
Timings:
In []:
%timeit secrets.token_urlsafe(n)
Out[]:
788 ns ± 4.32 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In []:
%timeit secrets.token_hex(15)
Out[]:
631 ns ± 2.86 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
Upvotes: 96
Reputation: 12027
Compact way of generating a random hex string 32 characters in length, using the random
module:
print(format(random.getrandbits(128), '032x'))
Upvotes: 1
Reputation: 1211
import string
import random
lst = [random.choice(string.ascii_letters + string.digits) for n in xrange(30)]
s = "".join(lst)
print s
ocwbKCiuAJLRJgM1bWNV1TPSH0F2Lb
Upvotes: 35
Reputation: 136437
This is for sure not the most lightweight version, but it is random and it's easy to adjust the alphabet / length you want:
import random
def generate(random_chars=12, alphabet="0123456789abcdef"):
r = random.SystemRandom()
return ''.join([r.choice(alphabet) for i in range(random_chars)])
Upvotes: 2
Reputation: 19155
I got a faster one for the hex output. Using the same t1 and t2 as above:
>>> 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
only makes one call to the random module, doesn't have to build or read a list, and then does the rest with string formatting.
Upvotes: 144
Reputation: 1074
adding one more answer to the mix that performs faster than @eemz solution and is also fully alphanumeric. Note that this does not give you a hexidecimal answer.
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"
a quick benchmark yields
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
Upvotes: 0
Reputation: 1075
In [1]: import random
In [2]: hex(random.getrandbits(16))
Out[2]: '0x3b19'
Upvotes: 9
Reputation: 7223
one-line function:
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)
Upvotes: 6
Reputation: 1520
Another Method :
from Crypto import Random
import binascii
my_hex_value = binascii.hexlify(Random.get_random_bytes(30))
The point is : byte value is always equal to the value in hex.
Upvotes: 5
Reputation: 1321
Dramatically faster solution than those here:
timeit("'%0x' % getrandbits(30 * 4)", "from random import getrandbits")
0.8056681156158447
Upvotes: 28
Reputation: 4995
There's a faster one compared to what jcdyer has mentioned. This takes ~50% of his fastest method.
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]
You can change the size parameter passed to randint (last arg) to vary the output length based on your requirement. So, for a 60 char one:
binascii.b2a_hex(rand.randint(lo, hi, 4).tostring())[:60]
Upvotes: 2
Reputation: 303
Note: random.choice(string.hexdigits)
is incorrect, because string.hexdigits
returns 0123456789abcdefABCDEF
(both lowercase and uppercase), so you will get a biased result, with the hex digit 'c' twice as likely to appear as the digit '7'. Instead, just use random.choice('0123456789abcdef')
.
Upvotes: 17
Reputation: 5438
Incidentally, this is the result of using timeit
on the two approaches that have been suggested:
Using random.choice()
:
>>> t1 = timeit.Timer("''.join(random.choice(string.hexdigits) for n in xrange(30))", "import random, string")
>>> t1.timeit()
69.558588027954102
Using binascii.b2a_hex()
:
>>> t2 = timeit.Timer("binascii.b2a_hex(os.urandom(15))", "import os, binascii")
>>> t2.timeit()
16.288421154022217
Upvotes: 4