1717

How do I generate a string of size N, made of numbers and uppercase English letters such as:

  • 6U1S75
  • 4Z4UKK
  • U911K4
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Hellnar
  • 62,315
  • 79
  • 204
  • 279
  • 22
    This is a very popular question. I wish an expert would add his take on the uniqueness of these random numbers for the top 3 answers i.e. the collision probability for range of string size, say from 6 to 16. – user Jun 21 '14 at 07:06
  • 9
    @buffer It's easy to calculate the number of possible combinations. 10 numbers + 26 letters = 36 possible characters, to the power of 6 (length of string) is equal to about two billion. My rule of thumb for random values is "if I generated values for every human on Earth, how many values could they have each?". In this case that would be less than one value per person, so if this is to identify users or objects, it's too few characters. One alternative would be to add in lower case letters, which lands you at 62^6 = almost 57 billion unique values. – Blixt May 17 '15 at 18:17
  • 3
    And while it might seem silly to think of the world's population, that's just because you want a huge buffer for potential collisions. See the birthday problem: http://en.wikipedia.org/wiki/Birthday_problem – Blixt May 17 '15 at 18:18
  • 1
    @buffer, You would be interested in [this answer](http://stackoverflow.com/a/2145551/1383051) then. – Anish Ramaswamy Aug 20 '15 at 02:09
  • Shouldn't this be renamed **"Cryptographically secure random string generation..."**? – smci Sep 09 '18 at 01:09
  • 1
    You could start a vim child, wait till the user exits it, read the file the user left when he tried to close vim and filter numbers and uppercase latin characters from it. – 12431234123412341234123 Jul 30 '21 at 16:47

36 Answers36

3175

Answer in one line:

''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))

or even shorter starting with Python 3.6 using random.choices():

''.join(random.choices(string.ascii_uppercase + string.digits, k=N))

A cryptographically more secure version: see this post

''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))

In details, with a clean function for further reuse:

>>> import string
>>> import random
>>> def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
...    return ''.join(random.choice(chars) for _ in range(size))
...
>>> id_generator()
'G5G74W'
>>> id_generator(3, "6793YUIO")
'Y3U'

How does it work ?

We import string, a module that contains sequences of common ASCII characters, and random, a module that deals with random generation.

string.ascii_uppercase + string.digits just concatenates the list of characters representing uppercase ASCII chars and digits:

>>> string.ascii_uppercase
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> string.digits
'0123456789'
>>> string.ascii_uppercase + string.digits
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

Then we use a list comprehension to create a list of 'n' elements:

>>> range(4) # range create a list of 'n' numbers
[0, 1, 2, 3]
>>> ['elem' for _ in range(4)] # we use range to create 4 times 'elem'
['elem', 'elem', 'elem', 'elem']

In the example above, we use [ to create the list, but we don't in the id_generator function so Python doesn't create the list in memory, but generates the elements on the fly, one by one (more about this here).

Instead of asking to create 'n' times the string elem, we will ask Python to create 'n' times a random character, picked from a sequence of characters:

>>> random.choice("abcde")
'a'
>>> random.choice("abcde")
'd'
>>> random.choice("abcde")
'b'

Therefore random.choice(chars) for _ in range(size) really is creating a sequence of size characters. Characters that are randomly picked from chars:

>>> [random.choice('abcde') for _ in range(3)]
['a', 'b', 'b']
>>> [random.choice('abcde') for _ in range(3)]
['e', 'b', 'e']
>>> [random.choice('abcde') for _ in range(3)]
['d', 'a', 'c']

Then we just join them with an empty string so the sequence becomes a string:

>>> ''.join(['a', 'b', 'b'])
'abb'
>>> [random.choice('abcde') for _ in range(3)]
['d', 'c', 'b']
>>> ''.join(random.choice('abcde') for _ in range(3))
'dac'
Joël Brigate
  • 237
  • 2
  • 13
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • could you comment on why the list comprehension in this case does not require surrounding square brackets? – jorelli Jun 09 '11 at 20:32
  • 8
    @jorelli: It's not a list comprehension; it's a generator expression. – Ignacio Vazquez-Abrams Jun 09 '11 at 20:34
  • 2
    @joreilli: I added a quick note about this in the answer, and a link to a more detailed answer about iterable, list comprehension, generators and eventually the yield keyword. – Bite code Nov 02 '11 at 14:54
  • If you want something a little bit more randomly secure, for your consideration: `import random ; random = random.SecureRandom()` – Naftuli Kay Dec 09 '11 at 23:20
  • 1
    @TKKocheran: Do you mean SystemRandom? If not, please supply URL. – John Machin Feb 11 '12 at 07:28
  • 1
    @JohnMachin yep, that's what I meant. – Naftuli Kay Feb 13 '12 at 18:03
  • How soon the string going to repeat by this way? Does random.choice strong enough? – Andrew_1510 Mar 19 '12 at 15:05
  • @Andrew_1510: ["... has a period of 2**19937-1."](http://docs.python.org/library/random.html) – Ignacio Vazquez-Abrams Mar 19 '12 at 19:08
  • nice function. simple and effective. Would suggest 1 minor change, which is to use '_' instead of 'x' in the list comp. since _ is generally an idiom for throw away dummy var in python. – Xerion Jun 25 '12 at 04:29
  • @Xerion: Except when I18N matters. – Ignacio Vazquez-Abrams Jun 25 '12 at 04:46
  • 1
    Great code - I also remove letters and numbers that can appear similar so that users can type them in without getting confused. Eg, I might remove the chars '0O1L'. Of cause, this depends on what font you are rendering with. – Humphrey Oct 18 '12 at 02:19
  • @IgnacioVazquez-Abrams how does I18n relate to using `_` as a dummy variable ? – nurettin Jan 09 '13 at 08:04
  • 1
    I'd replace `range` with `xrange`. – Dr. Jan-Philip Gehrcke Jul 23 '13 at 12:51
  • @Jan-PhilipGehrcke: That wouldn't have a huge effect on the algorithm for sane values of `N`. – Ignacio Vazquez-Abrams Jul 23 '13 at 20:22
  • I would use `random.sample` instead of `random.choice` – chtenb May 14 '14 at 09:56
  • 1
    Is this implementation suitable for generating unique sequences? What's the probability of collision with 6 characters? – user Jun 20 '14 at 18:15
  • @buffer: I don't know the math off-hand, but it is out there somewhere. Maybe try asking on Math.SE. – Ignacio Vazquez-Abrams Jun 20 '14 at 21:14
  • 4
    Very useful. Interestingly, Django is using this piece of code for generating passwords & CSRF tokens. Although you should replace `random` with `random.SystemRandom()` : https://github.com/django/django/blob/875ce287e25d7576f9bd102f86adae09d242360f/django/utils/crypto.py#L77 – user Jun 21 '14 at 07:03
  • 2
    @Chiel92, `random.sample` creates samples without replacement, in other words, without the possibility to repeat characters, which isn't in the OP's requirements. I don't think that would be desirable for most applications. – ontologist Aug 12 '14 at 03:18
  • 1
    Looks nice, but does not ensure the string will contain letters and digits. If its used with a ranger lower than 12, there are many cases where the output contains only letters. – jabez Feb 25 '15 at 12:50
  • Correct, but that was never a requirement in the first place. And even so, the attacker won't necessarily know that; there are still 62**N possible passwords even if every single character in an arbitrary password is a numeral. – Ignacio Vazquez-Abrams Feb 25 '15 at 14:48
  • You really wat to bind those global objects to locals, avoiding mapping and attribute lookups. – Martijn Pieters Jan 24 '18 at 13:37
  • just wondering why all the examples are using `string.ascii_uppercase`. Is there any difference if `string.uppercase` is used? According to source code `ascii_uppercase = uppercase`. So I think there shouldn't be any difference. – Vikas Prasad Apr 06 '18 at 09:41
  • I get name 'string' is not defined in python 3.6 – Soerendip Jun 07 '18 at 18:22
  • If anyone is curious, this can yield 36^6=2 176 782 336 different combinations of letters/numbers. – Philip Jul 26 '18 at 09:48
  • You can also use `hashlib` in python to create a hash very specific to a file. – Muneeb Ahmad Khurram Jun 02 '21 at 18:11
  • 1
    @VikasPrasad For `string.uppercase`: "The specific value is locale-dependent, and will be updated when locale.setlocale() is called." See https://stackoverflow.com/questions/4942239/python-string-uppercase-vs-string-ascii-uppercase – xuiqzy Jan 24 '22 at 10:51
  • Great answer, just a question as a side note: What can we use when _performance_ is important? The `random` library is _incredibly_ slow: with the `random.choice()` and `random.choices()` methods I get 4 and 1 Mchars/sec respectively on my machine. Is there some C-compiled library we can use to generate random strings with reasonable performance? (Using `os.urandom` gives me 160 Mchars/sec, but transforming that to chars from in a given charset, while maintaining reasonable random properties on the distribution, is non-trivial.) – Carl Oct 05 '22 at 21:52
  • [Generate a Secure Random Password/String](https://fe-tool.com/en-us/random-password) – cwtuan Dec 21 '22 at 07:48
642

This Stack Overflow quesion is the current top Google result for "random string Python". The current top answer is:

''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))

This is an excellent method, but the PRNG in random is not cryptographically secure. I assume many people researching this question will want to generate random strings for encryption or passwords. You can do this securely by making a small change in the above code:

''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))

Using random.SystemRandom() instead of just random uses /dev/urandom on *nix machines and CryptGenRandom() in Windows. These are cryptographically secure PRNGs. Using random.choice instead of random.SystemRandom().choice in an application that requires a secure PRNG could be potentially devastating, and given the popularity of this question, I bet that mistake has been made many times already.

If you're using python3.6 or above, you can use the new secrets module as mentioned in MSeifert's answer:

''.join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(N))

The module docs also discuss convenient ways to generate secure tokens and best practices.

Georgy
  • 12,464
  • 7
  • 65
  • 73
Randy Marsh
  • 6,529
  • 1
  • 11
  • 5
  • 6
    Yes, the official standard library for `random` has warn this: "**Warning**: The pseudo-random generators of this module should not be used for security purposes. Use os.urandom() or SystemRandom if you require a cryptographically secure pseudo-random number generator." Here is the ref: [random.SystemRandom](https://docs.python.org/2/library/random.html#random.SystemRandom) and [os.urandom](https://docs.python.org/2/library/os.html#os.urandom) – lord63. j May 16 '15 at 14:30
  • 7
    Great answer. Small note: You changed it to `string.uppercase` which can lead to unexpected results depending on the locale set. Using `string.ascii_uppercase` (or `string.ascii_letters + string.digits` for base62 instead of base36) is safer in cases where encoding is involved. – Blixt May 17 '15 at 18:03
  • small note - better to use `xrange` instead of `range` as the latter generates an in-memory list, while the former creates an iterator. – Guy Jul 31 '16 at 11:44
  • `xrange` is not really a small note (unless everyone here is using Python 3=P). I'm really confused as to why most of these examples ignore performance big time. Another thing would be not to concatenate strings in a loop, it makes a huge difference. This code gets roughly (not very scientifically tested with `time` on a laptop) 20% faster with `xrange` and doing `alphabet = string.ascii_uppercase + string.digits` only once. – Marek Sapota Aug 09 '16 at 19:14
  • 2
    will the random sting will be alway be unique ? i wanted to use a primary key. – shakthydoss Sep 27 '16 at 08:45
  • 5
    @shakthydoss: no. It may return "AAA000", which is a random string, and next "AAA000", which is *also* a random string. You must explicitly add a check for uniqueness. – Jongware Mar 02 '18 at 11:15
264

Simply use Python's builtin uuid:

If UUIDs are okay for your purposes, use the built-in uuid package.

One Line Solution:

import uuid; uuid.uuid4().hex.upper()[0:6]

In Depth Version:

Example:

import uuid
uuid.uuid4() #uuid4 => full random uuid
# Outputs something like: UUID('0172fc9a-1dac-4414-b88d-6b9a6feb91ea')

If you need exactly your format (for example, "6U1S75"), you can do it like this:

import uuid

def my_random_string(string_length=10):
    """Returns a random string of length string_length."""
    random = str(uuid.uuid4()) # Convert UUID format to a Python string.
    random = random.upper() # Make all characters uppercase.
    random = random.replace("-","") # Remove the UUID '-'.
    return random[0:string_length] # Return the random string.

print(my_random_string(6)) # For example, D9E50C
oscfri
  • 2,558
  • 2
  • 27
  • 24
Bijan
  • 25,559
  • 8
  • 79
  • 71
  • 18
    +1 For thinking behind the question. Perhaps you could briefly explain the difference between uuid1 and uuid4. – Thomas Ahle Jan 29 '14 at 12:36
  • 1
    If I do uuid1 three times in a row I get: d161fd16-ab0f-11e3-9314-00259073e4a8, d3535b56-ab0f-11e3-9314-00259073e4a8, d413be32-ab0f-11e3-9314-00259073e4a8, which all seem to be suspiciously similar (the first 8 chars differ and the rest are the same). This isn't the case with uuid4 – Chase Roberts Mar 14 '14 at 00:31
  • 12
    uui1: Generate a UUID from a host ID, sequence number, and the current time. uuid4: Generate a random UUID. – Bijan Mar 14 '14 at 05:19
  • 9
    If you want to skip the string casting & hyphen replacing, you can just call my_uuid.get_hex() or uuid.uuid4().get_hex() and it will return a string generated from the uuid that does not have hyphens. – dshap Apr 11 '14 at 00:50
  • 10
    Is it a good idea to truncate a UUID? Depending on how small `string_length` is, the probability of collision can be a concern. – user Jun 21 '14 at 07:01
  • 1
    `uuid4`, at least on my machine, returns only **hexadecimal** characters. If you want to convert to full alphabet string, you can, but you have to do trickery to get anything beyond the letters `[a-f]`. Also, strictly speaking, this does not answer the original question, which implied full alphabetical requirements. – euxneks Apr 13 '15 at 21:08
  • 1
    Python 3: `str(uuid.uuid4()).upper()[:6]` (due to `'UUID' object has no attribute 'get_hex'` error) – Anton Tarasenko Jan 23 '18 at 11:34
  • 3
    Why limit yourself to *just hex characters*. Base64 or Base32 (for only uppercase characters and 6 different digits) to encode a random `os.urandom()` bytes sequence. Bypass the `uuid` middleman for more speed! – Martijn Pieters Jan 24 '18 at 13:39
  • `uuid.uuid4().hex[:string_length].upper()` is another way to do the one line solution. Works in both Python 2 and 3. – oscfri Jul 31 '18 at 15:33
  • 1
    Don't use this, this will cause approx 5 collisions for every 200k generations. – Julian Camilleri May 19 '20 at 14:11
  • @user Some bits in the uuid are not random! They are used for indicating which variant *and* version the uuid is, so they are *not* random and you get fewer random bits than you expected! Either fully understand how UUIDs work when truncating (read the RFC), or better yet, use the python `secrets` module (or python2 equivalent `random.SystemRandom()`), because that gives you security guarantees (in comparison to the `uuid` module currently). – xuiqzy Jan 24 '22 at 10:58
58

A simpler, faster but slightly less random way is to use random.sample instead of choosing each letter separately, If n-repetitions are allowed, enlarge your random basis by n times e.g.

import random
import string

char_set = string.ascii_uppercase + string.digits
print ''.join(random.sample(char_set*6, 6))

Note: random.sample prevents character reuse, multiplying the size of the character set makes multiple repetitions possible, but they are still less likely then they are in a pure random choice. If we go for a string of length 6, and we pick 'X' as the first character, in the choice example, the odds of getting 'X' for the second character are the same as the odds of getting 'X' as the first character. In the random.sample implementation, the odds of getting 'X' as any subsequent character are only 6/7 the chance of getting it as the first character

Anurag Uniyal
  • 85,954
  • 40
  • 175
  • 219
  • 9
    This way isn't bad but it's not quite as random as selecting each character separately, as with `sample` you'll never get the same character listed twice. Also of course it'll fail for `N` higher than `36`. – bobince Feb 13 '10 at 12:54
  • for the given use case(if no repeat is ok) i will say it is still the best solution. – Anurag Uniyal Feb 13 '10 at 14:30
  • 4
    One of the examples has a repeat, so I doubt he is looking to disallow repeats. – Mark Byers Feb 13 '10 at 14:34
  • @Shih-WenSu thanks, now that makes this the correct and better answer :) – Anurag Uniyal Mar 14 '13 at 21:57
  • 5
    If random.sample prevents character reuse, multiplying the size of the character set makes multiple repetitions _possible_, but they are still less _likely_ then they are in a pure random choice. If we go for a string of length 6, and we pick 'X' as the first character, in the choice example, the odds of getting 'X' for the second character are the same as the odds of getting 'X' as the first character. In the random.sample implementation, the odds of getting 'X' as any subsequent character are only 5/6 the chance of getting it as the first character. – pcurry Aug 31 '13 at 19:11
  • 1
    The chance of getting a particular character repeated drops off as you move through the generated string. Generating a string of 6 characters from the 26 uppercase letters plus 10 digits, randomly choosing each character independently, any particular string occurs with frequency 1/(36^6). The chance of generating 'FU3WYE' and 'XXXXXX' is the same. In the sample implementation, the chance of generating 'XXXXXX' is (1/(36^6)) * ((6/6) * (5/6) * (4/6) * (3/6) * (2/6) * (1/6)) due to the non-replacement feature of random.sample. 'XXXXXX' is 324 times less likely in the sample implementation. – pcurry Aug 31 '13 at 19:24
  • @pcurry yes I agree this fact should be considered while choosing this solution, I will add it to answer, thanks. – Anurag Uniyal Sep 04 '13 at 01:01
  • @pcurry shouldn't next char probability be 6/7 of first as first=6/36 second=5/35 – Anurag Uniyal Sep 04 '13 at 01:10
  • @AnuragUniyal I thought about it this way: without loss of generality, after picking arbitrary character 'G' from the 36 ^ 6 characters available, the 'G' from one of the 6 sets of 36 characters has been exhausted, leaving only 5/6 'G's available to be selected. Subsequently, 4/6, 3/6, 2/6 and 1/6 'G's are left, hence the sequence I posted. You're right, though, the denominator is changing. The sequence is actually (6/(36^6)) * (5/(36^6 - 1)) * (4/(36^6 - 2)) * (3/(36^6 - 3)) * (2/(36^6 - 4)) * (1/(36^6 - 5)) < 6!/((36^6)^6) = 20/(36^35) < 1/(36^6) of the random choice. – pcurry Sep 05 '13 at 03:23
37
import uuid
lowercase_str = uuid.uuid4().hex  

lowercase_str is a random value like 'cea8b32e00934aaea8c005a35d85a5c0'

uppercase_str = lowercase_str.upper()

uppercase_str is 'CEA8B32E00934AAEA8C005A35D85A5C0'

Yajo
  • 5,808
  • 2
  • 30
  • 34
Savad KP
  • 1,625
  • 3
  • 28
  • 40
  • 2
    `uppercase_str[:N+1]` – Yajo May 12 '16 at 15:51
  • @Yajo yeah we can limit using slicing – Savad KP May 13 '16 at 04:40
  • 2
    @Yajo: no, you don't want to slice the hex value. You remove entropy compared to a full sequence of uppercase letters and digits. Perhaps base32-encode the value instead (slightly reduced entropy, from 36 ** n to 32 ** n, still better than 16 ** n). – Martijn Pieters Jan 24 '18 at 13:41
  • 1
    @Yajo Some bits in the uuid are not random! They are used for indicating which variant and version the uuid is, so they are not random and you get fewer random bits than you expected! Either fully understand how UUIDs work when truncating (read the RFC), or better yet, use the python secrets module (or python2 equivalent random.SystemRandom()), because that gives you security guarantees (in comparison to the uuid module currently). – xuiqzy Jan 24 '22 at 11:12
26

From Python 3.6 on you should use the secrets module if you need it to be cryptographically secure instead of the random module (otherwise this answer is identical to the one of @Ignacio Vazquez-Abrams):

from secrets import choice
import string

''.join([choice(string.ascii_uppercase + string.digits) for _ in range(N)])

One additional note: a list-comprehension is faster in the case of str.join than using a generator expression!

Brutus
  • 7,139
  • 7
  • 36
  • 41
MSeifert
  • 145,886
  • 38
  • 333
  • 352
20

A faster, easier and more flexible way to do this is to use the strgen module (pip install StringGenerator).

Generate a 6-character random string with upper case letters and digits:

>>> from strgen import StringGenerator as SG
>>> SG("[\u\d]{6}").render()
u'YZI2CI'

Get a unique list:

>>> SG("[\l\d]{10}").render_list(5,unique=True)
[u'xqqtmi1pOk', u'zmkWdUr63O', u'PGaGcPHrX2', u'6RZiUbkk2i', u'j9eIeeWgEF']

Guarantee one "special" character in the string:

>>> SG("[\l\d]{10}&[\p]").render()
u'jaYI0bcPG*0'

A random HTML color:

>>> SG("#[\h]{6}").render()
u'#CEdFCa'

etc.

We need to be aware that this:

''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))

might not have a digit (or uppercase character) in it.

strgen is faster in developer-time than any of the above solutions. The solution from Ignacio is the fastest run-time performing and is the right answer using the Python Standard Library. But you will hardly ever use it in that form. You will want to use SystemRandom (or fallback if not available), make sure required character sets are represented, use unicode (or not), make sure successive invocations produce a unique string, use a subset of one of the string module character classes, etc. This all requires lots more code than in the answers provided. The various attempts to generalize a solution all have limitations that strgen solves with greater brevity and expressive power using a simple template language.

It's on PyPI:

pip install StringGenerator

Disclosure: I'm the author of the strgen module.

Skippy le Grand Gourou
  • 6,976
  • 4
  • 60
  • 76
Paul Wolf
  • 616
  • 5
  • 6
  • Note this silently falls back to the cryptographically insecure `random.Random` if the secure method is not available! It also uses the fallback when the user is providing a seed value. No general guarantee is made when it uses a cryptographically secure method. – xuiqzy Jan 24 '22 at 11:09
12

Based on another Stack Overflow answer, Most lightweight way to create a random string and a random hexadecimal number, a better version than the accepted answer would be:

('%06x' % random.randrange(16**6)).upper()

much faster.

Community
  • 1
  • 1
Gubbi
  • 756
  • 1
  • 7
  • 18
  • 1
    This is nice, though it will only use 'A-F' and not 'A-Z'. Also, the code gets a little less nice when parametrize `N`. – Thomas Ahle Jan 29 '14 at 12:41
10

I thought no one had answered this yet lol! But hey, here's my own go at it:

import random

def random_alphanumeric(limit):
    #ascii alphabet of all alphanumerals
    r = (range(48, 58) + range(65, 91) + range(97, 123))
    random.shuffle(r)
    return reduce(lambda i, s: i + chr(s), r[:random.randint(0, len(r))], "")
Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
JWL
  • 13,591
  • 7
  • 57
  • 63
  • 5
    I won't vote this down, but I think it's far too complicated for such a simple task. The return expression is a monster. Simple is better than complex. – Carl Smith Dec 28 '12 at 23:14
  • 16
    @CarlSmith, true my solution seems a bit overkill for the task, but I was aware of the other simpler solutions, and just wished to find an alternative route to a good answer. Without freedom, creativity is in danger, thus I went ahead and posted it. – JWL Dec 29 '12 at 04:40
9

If you need a random string rather than a pseudo random one, you should use os.urandom as the source

from os import urandom
from itertools import islice, imap, repeat
import string

def rand_string(length=5):
    chars = set(string.ascii_uppercase + string.digits)
    char_gen = (c for c in imap(urandom, repeat(1)) if c in chars)
    return ''.join(islice(char_gen, None, length))
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • 3
    How is `os.urandom` not pseudo random? It might be using a better algorithm to generate numbers that are more random, but it is still pseudo random. – Tyilo Dec 09 '12 at 15:00
  • 1
    @Tyilo, I am aware of the difference between `/dev/random` and `/dev/urandom`. The problem is that `/dev/random` blocks when there is not enough entropy which limits it's usefulness. For a **one time pad** `/dev/urandom` isn't good enough, but I think it's better than pseudo random here. – John La Rooy Dec 09 '12 at 21:20
  • 1
    I would say that both `/dev/random` and `/dev/urandom` is pseudo random, but it might depend on your definition. – Tyilo Dec 09 '12 at 21:27
8

This method is slightly faster, and slightly more annoying, than the random.choice() method Ignacio posted.

It takes advantage of the nature of pseudo-random algorithms, and banks on bitwise and and shift being faster than generating a new random number for each character.

# must be length 32 -- 5 bits -- the question didn't specify using the full set
# of uppercase letters ;)
_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'

def generate_with_randbits(size=32):
    def chop(x):
        while x:
            yield x & 31
            x = x >> 5
    return  ''.join(_ALPHABET[x] for x in chop(random.getrandbits(size * 5))).ljust(size, 'A')

...create a generator that takes out 5 bit numbers at a time 0..31 until none left

...join() the results of the generator on a random number with the right bits

With Timeit, for 32-character strings, the timing was:

[('generate_with_random_choice', 28.92901611328125),
 ('generate_with_randbits', 20.0293550491333)]

...but for 64 character strings, randbits loses out ;)

I would probably never use this approach in production code unless I really disliked my co-workers.

edit: updated to suit the question (uppercase and digits only), and use bitwise operators & and >> instead of % and //

sea-rob
  • 2,275
  • 1
  • 22
  • 22
6

Use Numpy's random.choice() function

import numpy as np
import string        

if __name__ == '__main__':
    length = 16
    a = np.random.choice(list(string.ascii_uppercase + string.digits), length)                
    print(''.join(a))

Documentation is here http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.random.choice.html

Ian Smith
  • 879
  • 1
  • 12
  • 23
Mudit Jain
  • 4,163
  • 1
  • 22
  • 18
6

Security Oriented Approach

Our recommendation for anything security related is to avoid "rolling you own" and to use the secrets module which is specifically vetted for security.

This is from the best practices section of the docs:

import string
import secrets
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(8))

Since you specifically asked for uppercase letters, you can either substitute ascii_uppercase for ascii_letters, or just uppercase the password with:

password = password.upper()

Standard Approach Not Aiming for Security

The canonical approach to this problem (as specified) uses the choices() function in the random module:

>>> from random import choices
>>> from string import ascii_uppercase, digits

>>> population = ascii_uppercase + digits
>>> str.join('', choices(population, k=6))
'6JWF1H'
Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
5

Sometimes 0 (zero) & O (letter O) can be confusing. So I use

import uuid
uuid.uuid4().hex[:6].upper().replace('0','X').replace('O','Y')
Aseem
  • 5,848
  • 7
  • 45
  • 69
  • Some bits in the uuid are not random! They are used for indicating which variant and version the uuid is, so they are not random and you get fewer random bits than you expected if you are truncating it! Either fully understand how UUIDs work when truncating (read the RFC), or better yet, use the python secrets module (or python2 equivalent random.SystemRandom()), because that gives you security guarantees (in comparison to the uuid module currently). – xuiqzy Jan 24 '22 at 11:13
5

I'd do it this way:

import random
from string import digits, ascii_uppercase

legals = digits + ascii_uppercase

def rand_string(length, char_set=legals):

    output = ''
    for _ in range(length): output += random.choice(char_set)
    return output

Or just:

def rand_string(length, char_set=legals):

    return ''.join( random.choice(char_set) for _ in range(length) )
Carl Smith
  • 3,025
  • 24
  • 36
4
>>> import string 
>>> import random

the following logic still generates 6 character random sample

>>> print ''.join(random.sample((string.ascii_uppercase+string.digits),6))
JT7K3Q

No need to multiply by 6

>>> print ''.join(random.sample((string.ascii_uppercase+string.digits)*6,6))

TK82HK
Javad
  • 67
  • 1
  • 8
user128956
  • 123
  • 1
  • 14
  • 3
    But this variant will force all characters to be different. And it will not work if N is larger than len(string.ascii_uppercase + string.digits) – MarSoft Jan 09 '16 at 09:08
4

I used this method to generate random string of length n from a -> z

import random
s = ''.join(random.choice([chr(i) for i in range(ord('a'),ord('z'))]) for _ in range(10))
Nishan B
  • 627
  • 7
  • 11
3
>>> import random
>>> str = []
>>> chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
>>> num = int(raw_input('How long do you want the string to be?  '))
How long do you want the string to be?  10
>>> for k in range(1, num+1):
...    str.append(random.choice(chars))
...
>>> str = "".join(str)
>>> str
'tm2JUQ04CK'

The random.choice function picks a random entry in a list. You also create a list so that you can append the character in the for statement. At the end str is ['t', 'm', '2', 'J', 'U', 'Q', '0', '4', 'C', 'K'], but the str = "".join(str) takes care of that, leaving you with 'tm2JUQ04CK'.

Hope this helps!

A.J. Uppal
  • 19,117
  • 6
  • 45
  • 76
  • Nice, but you could have used `range(num)` instead, and str could have been a string `str += random.choice(chars)`. – sashk May 09 '14 at 16:32
3

For those of you who enjoy functional python:

from itertools import imap, starmap, islice, repeat
from functools import partial
from string import letters, digits, join
from random import choice

join_chars = partial(join, sep='')
identity = lambda o: o

def irand_seqs(symbols=join_chars((letters, digits)), length=6, join=join_chars, select=choice, breakup=islice):
    """ Generates an indefinite sequence of joined random symbols each of a specific length
    :param symbols: symbols to select,
        [defaults to string.letters + string.digits, digits 0 - 9, lower and upper case English letters.]
    :param length: the length of each sequence,
        [defaults to 6]
    :param join: method used to join selected symbol, 
        [defaults to ''.join generating a string.]
    :param select: method used to select a random element from the giving population. 
        [defaults to random.choice, which selects a single element randomly]
    :return: indefinite iterator generating random sequences of giving [:param length]
    >>> from tools import irand_seqs
    >>> strings = irand_seqs()
    >>> a = next(strings)
    >>> assert isinstance(a, (str, unicode))
    >>> assert len(a) == 6
    >>> assert next(strings) != next(strings)
    """
    return imap(join, starmap(breakup, repeat((imap(select, repeat(symbols)), None, length))))

It generates an indefinite [infinite] iterator, of joined random sequences, by first generating an indefinite sequence of randomly selected symbol from the giving pool, then breaking this sequence into length parts which is then joined, it should work with any sequence that supports getitem, by default it simply generates a random sequence of alpha numeric letters, though you can easily modify to generate other things:

for example to generate random tuples of digits:

>>> irand_tuples = irand_seqs(xrange(10), join=tuple)
>>> next(irand_tuples)
(0, 5, 5, 7, 2, 8)
>>> next(irand_tuples)
(3, 2, 2, 0, 3, 1)

if you don't want to use next for generation you can simply make it callable:

>>> irand_tuples = irand_seqs(xrange(10), join=tuple)
>>> make_rand_tuples = partial(next, irand_tuples) 
>>> make_rand_tuples()
(1, 6, 2, 8, 1, 9)

if you want to generate the sequence on the fly simply set join to identity.

>>> irand_tuples = irand_seqs(xrange(10), join=identity)
>>> selections = next(irand_tuples)
>>> next(selections)
8
>>> list(selections)
[6, 3, 8, 2, 2]

As others have mentioned if you need more security then set the appropriate select function:

>>> from random import SystemRandom
>>> rand_strs = irand_seqs(select=SystemRandom().choice)
'QsaDxQ'

the default selector is choice which may select the same symbol multiple times for each chunk, if instead you'd want the same member selected at most once for each chunk then, one possible usage:

>>> from random import sample
>>> irand_samples = irand_seqs(xrange(10), length=1, join=next, select=lambda pool: sample(pool, 6))
>>> next(irand_samples)
[0, 9, 2, 3, 1, 6]

we use sample as our selector, to do the complete selection, so the chunks are actually length 1, and to join we simply call next which fetches the next completely generated chunk, granted this example seems a bit cumbersome and it is ...

Samy Vilar
  • 10,800
  • 2
  • 39
  • 34
3

(1) This will give you all caps and numbers:

import string, random
passkey=''
for x in range(8):
    if random.choice([1,2]) == 1:
        passkey += passkey.join(random.choice(string.ascii_uppercase))
    else:
        passkey += passkey.join(random.choice(string.digits))
print passkey 

(2) If you later want to include lowercase letters in your key, then this will also work:

import string, random
passkey=''
for x in range(8):
    if random.choice([1,2]) == 1:
        passkey += passkey.join(random.choice(string.ascii_letters))
    else:
        passkey += passkey.join(random.choice(string.digits))
print passkey  
Jacob
  • 59
  • 6
3

this is a take on Anurag Uniyal 's response and something that i was working on myself.

import random
import string

oneFile = open('‪Numbers.txt', 'w')
userInput = 0
key_count = 0
value_count = 0
chars = string.ascii_uppercase + string.digits + string.punctuation

for userInput in range(int(input('How many 12 digit keys do you want?'))):
    while key_count <= userInput:
        key_count += 1
        number = random.randint(1, 999)
        key = number

        text = str(key) + ": " + str(''.join(random.sample(chars*6, 12)))
        oneFile.write(text + "\n")
oneFile.close()
Stopit Donk
  • 33
  • 1
  • 4
2
import string
from random import *
characters = string.ascii_letters + string.punctuation  + string.digits
password =  "".join(choice(characters) for x in range(randint(8, 16)))
print password
Natasha
  • 507
  • 2
  • 7
  • 12
  • 2
    Although this code may answer the question, providing additional context regarding _why_ and/or _how_ it answers the question would significantly improve its long-term value. Please [edit] your answer to add some explanation. – Toby Speight May 11 '16 at 11:40
2
import random
q=2
o=1
list  =[r'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','s','0','1','2','3','4','5','6','7','8','9','0']
while(q>o):
    print("")

    for i in range(1,128):
        x=random.choice(list)
        print(x,end="")

Here length of string can be changed in for loop i.e for i in range(1,length) It is simple algorithm which is easy to understand. it uses list so you can discard characters that you do not need.

lawlie8
  • 31
  • 1
2

I was looking at the different answers and took time to read the documentation of secrets

The secrets module is used for generating cryptographically strong random numbers suitable for managing data such as passwords, account authentication, security tokens, and related secrets.

In particularly, secrets should be used in preference to the default pseudo-random number generator in the random module, which is designed for modelling and simulation, not security or cryptography.

Looking more into what it has to offer I found a very handy function if you want to mimic an ID like Google Drive IDs:

secrets.token_urlsafe([nbytes=None])
Return a random URL-safe text string, containing nbytes random bytes. The text is Base64 encoded, so on average each byte results in approximately 1.3 characters. If nbytes is None or not supplied, a reasonable default is used.

Use it the following way:

import secrets
import math

def id_generator():
    id = secrets.token_urlsafe(math.floor(32 / 1.3))
    return id

print(id_generator())

Output a 32 characters length id:

joXR8dYbBDAHpVs5ci6iD-oIgPhkeQFk

I know this is slightly different from the OP's question but I expect that it would still be helpful to many who were looking for the same use-case that I was looking for.

Community
  • 1
  • 1
Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
1

A simple one:

import string
import random
character = string.lowercase + string.uppercase + string.digits + string.punctuation
char_len = len(character)
# you can specify your password length here
pass_len = random.randint(10,20)
password = ''
for x in range(pass_len):
    password = password + character[random.randint(0,char_len-1)]
print password
Hackaholic
  • 19,069
  • 5
  • 54
  • 72
1

I would like to suggest you next option:

import crypt
n = 10
crypt.crypt("any sring").replace('/', '').replace('.', '').upper()[-n:-1]

Paranoic mode:

import uuid
import crypt
n = 10
crypt.crypt(str(uuid.uuid4())).replace('/', '').replace('.', '').upper()[-n:-1]
mrvol
  • 2,575
  • 18
  • 21
1

Two methods :

import random, math

def randStr_1(chars:str, length:int) -> str:
    chars *= math.ceil(length / len(chars))
    chars = letters[0:length]
    chars = list(chars)
    random.shuffle(characters)

    return ''.join(chars)

def randStr_2(chars:str, length:int) -> str:
    return ''.join(random.choice(chars) for i in range(chars))


Benchmark :

from timeit import timeit

setup = """
import os, subprocess, time, string, random, math

def randStr_1(letters:str, length:int) -> str:
    letters *= math.ceil(length / len(letters))
    letters = letters[0:length]
    letters = list(letters)
    random.shuffle(letters)
    return ''.join(letters)

def randStr_2(letters:str, length:int) -> str:
    return ''.join(random.choice(letters) for i in range(length))
"""

print('Method 1 vs Method 2', ', run 10 times each.')

for length in [100,1000,10000,50000,100000,500000,1000000]:
    print(length, 'characters:')

    eff1 = timeit("randStr_1(string.ascii_letters, {})".format(length), setup=setup, number=10)
    eff2 = timeit("randStr_2(string.ascii_letters, {})".format(length), setup=setup, number=10)
    print('\t{}s : {}s'.format(round(eff1, 6), round(eff2, 6)))
    print('\tratio = {} : {}\n'.format(eff1/eff1, round(eff2/eff1, 2)))

Output :

Method 1 vs Method 2 , run 10 times each.

100 characters:
    0.001411s : 0.00179s
    ratio = 1.0 : 1.27

1000 characters:
    0.013857s : 0.017603s
    ratio = 1.0 : 1.27

10000 characters:
    0.13426s : 0.151169s
    ratio = 1.0 : 1.13

50000 characters:
    0.709403s : 0.855136s
    ratio = 1.0 : 1.21

100000 characters:
    1.360735s : 1.674584s
    ratio = 1.0 : 1.23

500000 characters:
    6.754923s : 7.160508s
    ratio = 1.0 : 1.06

1000000 characters:
    11.232965s : 14.223914s
    ratio = 1.0 : 1.27

The performance of first method is better.

Bi Ao
  • 704
  • 5
  • 11
1

Generate random 16-byte ID containig letters, digits, '_' and '-'

os.urandom(16).translate((f'{string.ascii_letters}{string.digits}-_'*4).encode('ascii'))

socketpair
  • 1,893
  • 17
  • 15
  • When I time this on my machine, this is about x40 faster than solutions involving `random.choices()`, and about x200 faster than solutions using `random.choice()`. – Carl Oct 05 '22 at 21:23
1
import string, random
lower = string.ascii_lowercase
upper = string.ascii_uppercase
digits = string.digits
special = '!"£$%^&*.,@#/?'

def rand_pass(l=4, u=4, d=4, s=4):
    p = []
    [p.append(random.choice(lower)) for x in range(l)]
    [p.append(random.choice(upper)) for x in range(u)]
    [p.append(random.choice(digits)) for x in range(d)]
    [p.append(random.choice(special)) for x in range(s)]
    random.shuffle(p)
    return "".join(p)

print(rand_pass())
# @5U,@A4yIZvnp%51
Pedro Lobito
  • 94,083
  • 31
  • 258
  • 268
0

If you want an easy-to-use but highly customisable key generator, use key-generator pypi package.

Here is the GitHub repo where you can find the complete documentation.

You can customise it to give a string jist like you want with many more options. Here's an example:

from key_generator.key_generator import generate

custom_key = generate(2, ['-', ':'], 3, 10, type_of_value = 'char', capital = 'mix', seed = 17).get_key()
print(custom_key)  # ZLFdHXIUe-ekwJCu

Hope this helps :)

Disclaimer: This uses the key-generator library which I made.

Sahith Kurapati
  • 1,617
  • 10
  • 14
0

None of the answers so far guarantee presence of certain categories of characters like upper, lower, digits etc; so other answers may result in passwords that do not have digits, etc. Surprised that such a function is not part of standard lib. Here is what I use:

def random_password(*, nchars = 7, min_nupper = 3, ndigits = 3, nspecial = 3, special=string.punctuation):
    letters = random.choices(string.ascii_lowercase, k=nchars)
    letters_upper = random.choices(string.ascii_uppercase, k=min_nupper)
    digits = random.choices(string.digits, k=ndigits)
    specials = random.choices(special, k=nspecial)

    password_chars = letters + letters_upper + digits + specials
    random.shuffle(password_chars)

    return ''.join(password_chars)
Oliver
  • 27,510
  • 9
  • 72
  • 103
0

Use this code to quickly generate a string of repeated random text values:

import string
import random

def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

moja_lista = []
for a in range(20):
    moja_lista.append(id_generator(3, "3etrY"))

You get 20 repeated random text values. The generator generates three-component elements from the set "3etrY" set. Everything can be set as you like.

print(len(moja_lista))
moja_lista

enter image description here

Wojciech Moszczyński
  • 2,893
  • 21
  • 27
-1

I have gone though almost all of the answers but none of them looks easier. I would suggest you to try the passgen library which is generally used to create random passwords.

You can generate random strings of your choice of length, punctuation, digits, letters and case.

Here's the code for your case:

from passgen import passgen
string_length = int(input())
random_string = passgen(length=string_length, punctuation=False, digits=True, letters=True, case='upper')
Underoos
  • 4,708
  • 8
  • 42
  • 85
-1

A random generator function without duplicates using a set to store values which have been generated before. Note this will cost some memory with very large strings or amounts and it probably will slow down a bit. The generator will stop at a given amount or when the maximum possible combinations are reached.

Code:

#!/usr/bin/env python

from typing import Generator
from random import SystemRandom as RND
from string import ascii_uppercase, digits


def string_generator(size: int = 1, amount: int = 1) -> Generator[str, None, None]:
    """
    Return x random strings of a fixed length.

    :param size: string length, defaults to 1
    :type size: int, optional
    :param amount: amount of random strings to generate, defaults to 1
    :type amount: int, optional
    :yield: Yield composed random string if unique
    :rtype: Generator[str, None, None]
    """
    CHARS = list(ascii_uppercase + digits)
    LIMIT = len(CHARS) ** size
    count, check, string = 0, set(), ''
    while LIMIT > count < amount:
        string = ''.join(RND().choices(CHARS, k=size))
        if string not in check:
            check.add(string)
            yield string
            count += 1


for my_count, my_string in enumerate(string_generator(6, 20)):
    print(my_count, my_string)

Output:

0 RS9N3P
1 S0GDGR
2 ZNBLFV
3 96FF97
4 38JJZ3
5 Q3214A
6 VLWNK1
7 QMT05E
8 X1ZFP0
9 MZF442
10 10L9AZ
11 GE8HIQ
12 S7PA43
13 MVLXO9
14 YX7Y0G
15 GIIKPF
16 3KCUQA
17 XHIXFV
18 BJQ5VG
19 HQF01Q

If your string always needs to contain letters and numbers, use this:

#!/usr/bin/env python

from typing import Generator
from random import SystemRandom as RND
from string import ascii_uppercase, digits


def string_generator(size: int = 2, amount: int = 1) -> Generator[str, None, None]:
    """
    Return x random strings of a fixed length.

    :param size: string length, defaults to 1
    :type size: int, optional
    :param amount: amount of random strings to generate, defaults to 1
    :type amount: int, optional
    :yield: Yield composed random string if unique
    :rtype: Generator[str, None, None]
    """
    if size < 2:
        print(
            'Since the string must contain letters and numbers,',
            'its size must be at least two characters',
        )
        return
    CHARS = list(ascii_uppercase + digits)
    LIMIT = len(CHARS) ** size
    count, check, string = 0, set(), ''
    while LIMIT > count < amount:
        while 1:
            string = ''.join(RND().choices(CHARS, k=size))
            if not (string.isalpha() or string.isnumeric()):
                break
        if string not in check:
            check.add(string)
            yield string
            count += 1


for my_count, my_string in enumerate(string_generator(6, 20)):
    print(my_count, my_string)

Output:

0 5JIITL
1 3W7AI1
2 8QMO10
3 ETHAI8
4 BLFPP9
5 50X2CC
6 1LI8WA
7 39CKET
8 R4IM0E
9 Q3KHGS
10 65CBIU
11 XTRHO4
12 N2XIGO
13 06VFCW
14 VE9MJ8
15 A9ADHK
16 Y5ROJ0
17 OH7EJF
18 TQ709S
19 P0DDNJ
FifthAxiom
  • 172
  • 1
  • 7
-2

you can now use a new library (python >= 3.6) chancepy here

from chancepy import Chance

random_string = Chance.string(length=10, pool="someLettersAndNumbers123")
Mr. Nun.
  • 775
  • 11
  • 29
-3

I found this to be simpler and cleaner.

str_Key           = ""
str_FullKey       = "" 
str_CharacterPool = "01234ABCDEFfghij~>()"
for int_I in range(64): 
    str_Key = random.choice(str_CharacterPool) 
    str_FullKey = str_FullKey + str_Key 

Just change the 64 to vary the length, vary the CharacterPool to do alpha only alpha numeric or numeric only or strange characters or whatever you want.

M T Head
  • 1,085
  • 9
  • 13