Problems with arrays in a Python password generator

Asked

Viewed 135 times

2

I’m making a generator of passwords and I’m not able to join letters that are separated into one array.

from random import choice
minusculas = ['a','b','c','d','f','g','h']
maiusculas = ['A','B','C','D','F','G','H']
senha = []
pos=0
while pos < 16:
    aleatorio = choice(maiusculas)
    senha.append(aleatorio)
    pos+=1
    aleatorio = choice(minusculas)
    senha.append(aleatorio)
    pos+=1
print(senha)

The result is something like: ['G', 'd', 'B', 'h', 'D', 'f', 'D', 'd', 'C', 'c', 'A', 'f', 'B', 'g', 'B', 'g']

I wanted to put all these letters together in one word.

5 answers

3


First, an important comment: the module documentation random clearly states that this should not be used for security and encryption purposes, and suggests the use of module secrets (available from Python 3.6).

That said, let’s see how to solve with both modules.


One detail: you’re always adding a capital letter and a lowercase letter, and a capital letter, and a lowercase letter, etc. This doesn’t seem random enough. Wouldn’t it be better to mix all the letters?

Also, from Python 3.6 you can use random.choices (note the "Choices", plural), passing the amount of random elements you want to extract.

Then, to join all the letters in a single string, use join, as indicated by the other replies.

As in your case the password has 16 characters, it would look like this:

from random import choices

minusculas = ['a','b','c','d','f','g','h']
maiusculas = ['A','B','C','D','F','G','H']

senha = ''.join(choices(minusculas + maiusculas, k=16))

Thus, the password gets a little more random than if you always switch one case with another lower case.

One detail is that the letters do not need to be in a list. In Python, strings are also iterable, so this could be how it works too:

minusculas = 'abcdefgh'
maiusculas = 'ABCDEFGH'
senha = ''.join(choices(minusculas + maiusculas, k=16))

If you are using Python < 3.6 you can simply join the two lists and call choice several times:

from random import choice

minusculas = ['a','b','c','d','f','g','h']
maiusculas = ['A','B','C','D','F','G','H']
letras = maiusculas + minusculas

senha = []
for _ in range(16):
    senha.append(choice(letras))

senha = ''.join(senha)

Or, using a comprehensilist on, much more succinct and pythonic:

senha = ''.join(choice(letras) for _ in range(16))

Of course there is still the chance that the generated password only has lowercase (or only uppercase), but it still seems better to me than to always have them alternated. Anyway, at the end we will see how to ensure this with the module secrets.


But if the idea is in fact to alternate (always a capital and then a lowercase), there is no way, have to call choice several times:

from random import choice

minusculas = ['a','b','c','d','f','g','h']
maiusculas = ['A','B','C','D','F','G','H']

senha = []
for _ in range(8): # iterando 8 vezes para que a senha tenha 16 caracteres
    senha.append(choice(maiusculas))
    senha.append(choice(minusculas))

senha = ''.join(senha)

Or, using a comprehensilist on, together with the module itertools:

from random import choice
from itertools import chain

minusculas = ['a','b','c','d','f','g','h']
maiusculas = ['A','B','C','D','F','G','H']

senha = ''.join(chain.from_iterable((choice(maiusculas), choice(minusculas)) for _ in range(8)))

But if you did so just to "guarantee" that it will always have 8 upper and 8 lower case letters, one option would be to shuffle the list at the end, because at least they do not alternate (although the fact that you always generate with 8 of each makes the password more predictable than mixed everything, but anyway):

import random
senha = []
for _ in range(8): # iterando 8 vezes para que a senha tenha 16 caracteres
    senha.append(random.choice(maiusculas))
    senha.append(random.choice(minusculas))

random.shuffle(senha) # embaralhar a string, para as maiúsculas não ficarem alternadas com minúsculas
senha = ''.join(senha)

Module secrets

As already said at the beginning, the module random is not suitable for generating passwords and any other uses involving encryption and security, and the documentation suggests the use of the module secrets.

The form of use is very similar, so much so that there is a method choice similar to existing in random:

from secrets import choice

minusculas = ['a','b','c','d','f','g','h']
maiusculas = ['A','B','C','D','F','G','H']
letras = maiusculas + minusculas

senha = ''.join(choice(letras) for _ in range(16))

The problem is that there is still the possibility that the password only has lowercase (or only uppercase) letters - maybe that’s why you were switching? Finally, the documentation itself suggests a way around this:

while True:
    senha = ''.join(choice(letras) for _ in range(16))
    if (any(c.islower() for c in senha)
        and any(c.isupper() for c in senha)): # se tiver pelo menos uma maiúscula e uma minúscula, interrompe o loop
        break

3

You don’t have to do something so complex. You don’t need a array with the base characters, you can use a string. The most intuitive count is going by the size of the individual characters.

If I wanted to do a generic function, which allows other character sets I would have to change this code a little, I won’t even do it because I don’t know the real requirement. For example some solutions posted here have changed the semantics that the original code did, I don’t know if it can. If you can solve in a row. I kept the draw interspersed between uppercase and lowercase. Is it good? It’s not, but it’s what was done.

from random import choice

minusculas = "abcdefgh"
maiusculas = "ABCDEFGH"
senha = []
pos = 0
while pos < 8:
    senha.append(choice(maiusculas))
    senha.append(choice(minusculas))
    pos += 1
print(''.join(senha))

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

Could have made direct concatenation with the string? It could, but it’s not efficient.

If you don’t have to keep the same semantics and stay even better you could do:

print(''.join([choice("abcdefghABCDEFGH") for i in range(16)]))

To generate better passwords the algorithm would need to be quite different from all of these, and of course, it would be a little more complex, but by necessity and not by accident.

1

If you want to join these characters into a single string, you can use the str.Join(iterable):

# os valores que você deu como exemplo
senha = ['G', 'd', 'B', 'h', 'D', 'f', 'D', 'd', 'C', 'c', 'A', 'f', 'B', 'g', 'B', 'g']

# une usando "nenhum" caractere como separador ('')
senha_concat = ''.join(senha)

print(senha_concat) # => GdBhDfDdCcAfBgBg

1

To join all the letters in a single string, just use the string method join passing the list as argument. See the example below:

senha_lista = ["O","z","X","Q","r","n","d","K"]
senha_string = "".join(senha_lista)

You can further shorten your code by creating a function and using comprehensilist on to get the list of letters in a single line. See the code below:

def get_random_password(chars, length = 10):

    letters = [choice(chars) for i in range(length)]
    return "".join(letters)

chars = ['a','b','c','d','f','g','h','A','B','C','D','F','G','H']
password = get_random_password(chars, 16)

0

Use a text variable for the password and increment every step of your loop.

from random import choice
minusculas = ['a','b','c','d','f','g','h']
maiusculas = ['A','B','C','D','F','G','H']
pos=0
senha = ''
while pos < 16:
    aleatorio = choice(maiusculas)
    senha = senha + aleatorio
    pos+=1
    aleatorio = choice(minusculas)
    senha = senha + aleatorio
    pos+=1
print(senha)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.