Génération de chaînes aléatoires avec des lettres et des chiffres en majuscules

Je veux générer une chaîne de caractères de taille N.

Elle doit être composée de chiffres et de lettres anglaises majuscules, comme :

  • 6U1S75
  • 4Z4UKK
  • U911K4

Comment puis-je réaliser cela de manière [pythique][1] ?

[1] : https://en.wikipedia.org/wiki/Python_%28programming_language%29#Features_and_philosophy

Solution

Réponse en une ligne:

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

ou encore plus court à partir de Python 3.6 en utilisant [random.choices()][1] :

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

Une version cryptographiquement plus sûre ; voir https://stackoverflow.com/a/23728630/2213647:

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

En détail, avec une fonction propre pour une réutilisation ultérieure:

>>> 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'

Comment ça marche ?

Nous importons string, un module qui contient des séquences de caractères ASCII courants, et random, un module qui s'occupe de la génération aléatoire.

string.ascii_uppercase + string.digits concatène simplement la liste des caractères représentant les caractères ASCII majuscules et les chiffres :

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

Ensuite, nous utilisons une compréhension de liste pour créer une liste de 'n' éléments :

>>> 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']

Dans l'exemple ci-dessus, nous utilisons [ pour créer la liste, mais nous ne le faisons pas dans la fonction id_generator. Python ne crée donc pas la liste en mémoire, mais génère les éléments à la volée, un par un (plus d'informations à ce sujet [ici][2]).

Au lieu de demander de créer 'n' fois la chaîne elem, nous demanderons à Python de créer 'n' fois un caractère aléatoire, choisi dans une séquence de caractères :

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

Par conséquent, random.choice(chars) for _ in range(size) crée réellement une séquence de caractères size. Des caractères qui sont choisis au hasard dans 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']

Ensuite, on les joint avec une chaîne vide pour que la séquence devienne une chaîne :

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

[1] : https://docs.python.org/3/library/random.html#random.choices [2] : https://stackoverflow.com/questions/231767/the-python-yield-keyword-explained/231855#231855

Commentaires (27)

Une manière plus simple, plus rapide mais légèrement moins aléatoire est d'utiliser random.sample au lieu de choisir chaque lettre séparément. Si n répétitions sont autorisées, agrandissez votre base aléatoire par n fois, par exemple.

import random
import string

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

Note : random.sample empêche la réutilisation des caractères, multiplier la taille du jeu de caractères rend les répétitions multiples possibles, mais elles sont toujours moins probables que dans un choix purement aléatoire. Si nous choisissons une chaîne de caractères de longueur 6, et que nous choisissons 'X' comme premier caractère, dans l'exemple du choix, les chances d'obtenir 'X' comme deuxième caractère sont les mêmes que les chances d'obtenir 'X' comme premier caractère. Dans l'implémentation de random.sample, les chances d'obtenir 'X' pour n'importe quel caractère suivant sont seulement 6/7 des chances de l'obtenir pour le premier caractère.

Commentaires (10)

Je pensais que personne n'avait encore répondu à cette question lol ! Mais bon, voici ma propre réponse :

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))], "")
Commentaires (2)