Generowanie losowych ciągów znaków z dużymi literami i cyframi

Chcę wygenerować ciąg znaków o rozmiarze N.

Powinien on składać się z liczb i wielkich liter angielskich, takich jak:

  • 6U1S75
  • 4Z4UKK
  • U911K4

Jak mogę to osiągnąć w pythoniczny sposób?

Rozwiązanie

Odpowiedź w jednej linii:

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

lub nawet krócej, zaczynając od Pythona 3.6, używając random.choices():

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

Bezpieczniejsza kryptograficznie wersja; zobacz https://stackoverflow.com/a/23728630/2213647:.

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

W szczegółach, z czystą funkcją do dalszego wykorzystania:

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

**Jak to działa?

Importujemy string, moduł zawierający sekwencje zwykłych znaków ASCII, oraz random, moduł zajmujący się generowaniem losowym.

String.ascii_uppercase + string.digits` po prostu konkatenuje listę znaków reprezentujących wielkie litery ASCII oraz cyfry:

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

Następnie używamy list comprehension, aby utworzyć listę 'n' elementów:

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

W powyższym przykładzie używamy [ do utworzenia listy, ale nie w funkcji id_generator, więc Python nie tworzy listy w pamięci, ale generuje elementy w locie, jeden po drugim (więcej na ten temat tutaj).

Zamiast prosić o utworzenie 'n' razy łańcucha elem, poprosimy Pythona o utworzenie 'n' razy losowego znaku, wybranego z ciągu znaków:

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

Dlatego random.choice(chars) for _ in range(size) naprawdę tworzy ciąg znaków size. Znaków, które są losowo wybierane z 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']

Następnie po prostu łączymy je z pustym ciągiem, więc sekwencja staje się ciągiem:

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

Prostszym, szybszym, ale nieco mniej losowym sposobem jest użycie random.sample zamiast wybierania każdej litery osobno, Jeśli n-powtórzeń jest dozwolonych, powiększ swoją losową bazę o n razy np.

import random
import string

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

Uwaga: random.sample zapobiega ponownemu użyciu znaków, zwielokrotnienie rozmiaru zestawu znaków sprawia, że wielokrotne powtórzenia są możliwe, ale nadal są mniej prawdopodobne niż w przypadku czystego losowego wyboru. Jeśli wybierzemy ciąg o długości 6, i wybierzemy 'X' jako pierwszy znak, w przykładzie wyboru, prawdopodobieństwo otrzymania 'X' jako drugiego znaku jest takie samo jak prawdopodobieństwo otrzymania 'X' jako pierwszego znaku. W implementacji random.sample szanse na otrzymanie 'X' jako dowolnego kolejnego znaku są tylko 6/7 szans na otrzymanie go jako pierwszego znaku.

Komentarze (10)

Myślałem, że nikt nie odpowiedział na to jeszcze lol! Ale hej, tutaj'jest mój własny go na to:

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