Primer generador de contraseñas en Python

Sep 10 2020

Este es mi primer proyecto con Python. Hice un generador de contraseñas simple que verifica la entrada del usuario. ¿Cómo puedo mejorarlo?

import random
def password_generator():
    password = []
    letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
               "v", "w", "x", "y", "z"]
    password_length = 0
    password_numbers = []
    password_letters = []
    # Input the length of the password
    while True:
        password_length_input = input("Choose the length of your password with numbers between 6 and 15:\n")
        if not password_length_input.isnumeric():
            print(f"{password_length_input} is not a number, try again:")
            continue
        else:
            password_length = int(password_length_input)
            print(f"Password length: {password_length}")
        if 6 <= password_length <= 15:
            break
        else:
            print("The password must be between 6 and 15 characters, try again:")
            continue
    # Input the amount of numbers in password
    while True:
        password_numbers_input = \
            input(f"Choose the amount of numbers you want in your password, max {password_length}\n")
        if not password_numbers_input.isnumeric():
            print(f"{password_numbers_input} is not a number try again")
            continue
        elif int(password_numbers_input) > password_length:
            password_numbers = 0
            print(f"The value is too high, choose maximum {password_length} numbers")
            continue
        else:
            password_numbers = int(password_numbers_input)
            print(f"Password numbers: {password_numbers}")
            for number in range(0,password_numbers):
                password.append(random.randrange(0,9))
            break
    # Check for numbers and letters in password
    while True:
        if password_numbers == password_length:
            print(f"The password will be only {password_numbers} numbers, no letters.")
            break
        else:
            password_letters = password_length - password_numbers
            print(f"""Your password will be {password_length} characters with {password_numbers} numbers and {password_letters} letters.""")
            for letter in range(0,password_letters):
                password.append(random.choice(letters))
            break
    random.shuffle(password)
    password_string = ''.join([str(item) for item in password])
    print(f"Your password is:\n{password_string}")

password_generator()

Ejemplo de uso:

Choose the length of your password with numbers between 6 and 15:
Password length: 8
Choose the amount of numbers you want in your password, max 8
Password numbers: 2
Your password will be 8 characters with 2 numbers and 6 letters.
Your password is:
pzc11bmf

Respuestas

9 benrg Sep 10 2020 at 22:06
letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",`
           "v", "w", "x", "y", "z"]

Este método de escribir el alfabeto es muy propenso a errores. Importaría stringy usaría string.ascii_lowercaseen lugar de letters. Si desea generar su propio rango de letras por cualquier motivo, escribiría

letters = [chr(n) for n in range(ord('a'), ord('z') + 1)]

ya que entonces no hay peligro de omitir o duplicar una carta.


password_length = 0
password_numbers = []
password_letters = []

Estos valores predeterminados nunca se utilizan. Los valores predeterminados para password_numbersy password_lettersno tienen sentido ya que esas variables contienen números. Eliminaría las tres líneas.


if not password_length_input.isnumeric():
    print(f"{password_length_input} is not a number, try again:")
    continue
else:
    password_length = int(password_length_input)
    print(f"Password length: {password_length}")

En su lugar escribiría

try:
    password_length = int(password_length_input)
except ValueError:
    print(f"{password_length_input} is not a number, try again:")
    continue
print(f"Password length: {password_length}")

while True:
    if password_numbers == password_length:
        ...
        break
    else:
        ...
        break

No tiene sentido tener un whilebucle aquí, ya que siempre se sale de él en la primera iteración.


range(0,password_numbers)

Puedes simplemente escribir range(password_numbers).


password.append(random.randrange(0,9))

Esto agregará un dígito del 0 al 8 inclusive, nunca 9. Si desea los diez dígitos, debe escribir random.randrange(10). O, quizás mejor, use random.choice(string.digits).


password_string = ''.join([str(item) for item in password])

Si usa string.digits, cada elemento de passwordserá un carácter, por lo que puede simplificarlo a password_string = ''.join(password).

6 Anonymous Sep 10 2020 at 20:31

Una forma más sencilla de generar una cadena aleatoria:

import random
import string

    def get_random_string(length):
        letters = string.ascii_lowercase
        result_str = ''.join(random.choice(letters) for i in range(length))
        print("Random string of length", length, "is:", result_str)
    
    get_random_string(8)
    get_random_string(8)
    get_random_string(6)

tomado prestado de aquí , y hay más ejemplos.

Ahora, si tiene requisitos específicos como un número mínimo de dígitos, puede modificar la fórmula o generar dos listas y fusionarlas mientras baraja los valores.

Hay un ejemplo en el enlace que cité anteriormente: "Genere una cadena alfanumérica aleatoria con un recuento fijo de letras y dígitos" => fusionando dos listas por comprensión.

La forma en que lo está haciendo es procedimental pero no Pythonic. Es como reinventar la rueda.

Como mínimo, su lista de caracteres permitidos debería verse así:

letters = "abcdefghijklmnopqrstuvwxyz"

Luego elige una letra aleatoria, por ejemplo letters[3], devolverá 'd' ya que la lista está basada en 0 y Python trata las cadenas como secuencias de caracteres. Usando shuffle como ya lo está haciendo, puede escribir código más conciso.

3 K.Oleksy Sep 10 2020 at 19:19

primero

Sugiero crear métodos separados para cada ciclo while.

Segundo

El bucle "while True" no es una buena práctica, creo. En lugar de eso, use condition.

Tercero

Sugiero crear la clase PasswordGenerator que contendrá su código. Le ayudará a ampliar su código en el futuro.

Estructura base para su proyecto

 class PasswordGenerator():
     
    check_declared_password_length():
        ...
        
    check_amount_of_password_numbers():
        ...
    *
    *
    *

Para el final, recuerde crear funciones con una sola responsabilidad. Después de eso, puede escribir pruebas unitarias para cada uno de ellos y será más fácil.