Quero gerar um fio de tamanho N.
Deve ser composto por números e letras maiúsculas em inglês, como por exemplo:
Como posso conseguir isto de uma forma pítonica?
Resposta em uma linha:
''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))
ou ainda mais curto começando com o Python 3.6 utilizando random.choices()
:
''.join(random.choices(string.ascii_uppercase + string.digits, k=N))
Uma versão criptograficamente mais segura; ver https://stackoverflow.com/a/23728630/2213647:
''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))
Em detalhes, com uma função limpa para reutilização posterior:
>>> 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'
**Como é que funciona?
Importamos string
, um módulo que contém sequências de caracteres ASCII comuns, e random
, um módulo que lida com geração aleatória.
string.ascii_uppercase + string.digits' apenas concatena a lista de caracteres que representam caracteres ASCII em maiúsculas e dígitos:
>>> string.ascii_uppercase
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> string.digits
'0123456789'
>>> string.ascii_uppercase + string.digits
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
Depois usamos uma compreensão de lista para criar uma lista de 'n' elementos:
>>> 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']
No exemplo acima, utilizamos [
para criar a lista, mas não na função `id_generator', então Python não cria a lista na memória, mas gera os elementos na hora, um por um (mais sobre isso aqui).
Em vez de pedir para criar 'n' vezes a string 'elem', vamos pedir ao Python para criar 'n' vezes um caracter aleatório, escolhido a partir de uma sequência de caracteres:
>>> random.choice("abcde")
'a'
>>> random.choice("abcde")
'd'
>>> random.choice("abcde")
'b'
Portanto random.choice(chars) for _ in range(size)
realmente está criando uma seqüência de caracteres size
. Caracteres que são escolhidos aleatoriamente a partir de 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']
Depois juntamo-los com uma corda vazia para que a sequência se torne uma corda:
>>> ''.join(['a', 'b', 'b'])
'abb'
>>> [random.choice('abcde') for _ in range(3)]
['d', 'c', 'b']
>>> ''.join(random.choice('abcde') for _ in range(3))
'dac'
Uma maneira mais simples, mais rápida mas ligeiramente menos aleatória é utilizar 'random.sample' em vez de escolher cada letra separadamente, Se n-repetições são permitidas, aumente sua base aleatória por n vezes, por exemplo
import random
import string
char_set = string.ascii_uppercase + string.digits
print ''.join(random.sample(char_set*6, 6))
Nota: random.sample impede a reutilização de caracteres, multiplicar o tamanho do conjunto de caracteres torna possível múltiplas repetições, mas ainda é menos provável que elas estejam em uma escolha puramente aleatória. Se escolhermos uma string de comprimento 6, e escolhermos 'X' como o primeiro caractere, no exemplo de escolha, as chances de obter 'X' para o segundo caractere são as mesmas que para obter 'X' como o primeiro caractere. Na implementação do random.sample, as chances de obter 'X' como qualquer caractere subsequente são de apenas 6/7 a chance de obtê-lo como o primeiro caractere.
Eu pensei que ninguém tinha respondido ainda rs! Mas ei, aqui vai a minha própria resposta:
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))], "")