Search code examples
pythonloopspassword-generator

My password generator is consistently losing characters each time it is run


I'm fairly new to back-end programming and have been given the assignment to create a simple password generator using nothing but loops. The program prompts the user to decide how many letters, numbers, and symbols they wish to include in their password. In order to generate passwords wherein these characters would be inserted in a random order, I implemented a list for each type of character to correspond to. The "randKey" variable picks one of these keys at random and calls a function that inserts a character based on its value. When the variable representing the number of characters the user wishes to enter is less than 0, the key is removed from the list. For some reason, whenever I run this specific algorithm, the program produces less than the desired number of characters. Can you help me figure out why this is?

I tried changing values such as the range of the loop itself, but it proved completely inconsequential.

Edit: I'm also aware that I could have just shuffled the characters around in the password, but I wanted to see if I could get this particular method to work just to determine whether or I would be capable of implementing it.

# Password Generator
import random

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', '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']
numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+']

characters = [[letters],[numbers],[symbols]]

numLetters = int(input("How many letters would you like to include? "))
numSymbols = int(input("How many symbols would you like to include? "))
numNums = int(input("How many numbers would you like to include? "))

totalLen = numLetters + numSymbols + numNums

pswd = ""

# Each of these functions will be called by another function in a random order.
def addLetter():
    return letters[random.randint(0, len(letters) - 1)]

def addNumber():
    return numbers[random.randint(0, len(numbers) - 1)]

def addSymbol():
    return symbols[random.randint(0, len(symbols) - 1)]

# When an "add" function is called, it will evaluate whether or not the "num--" variable is greater than 0 in order
# to determine if it should go through.
keys = ["a", "b", "c"]

for i in range(totalLen - 1):
    randKey = keys[random.randint(0, len(keys) - 1)]
    if randKey == "a":
        if numLetters > 0:
            pswd += addLetter()
            numLetters -= 1
        else:
            keys.pop(0)
    elif randKey == "b":
        if numSymbols > 0:
            pswd += addSymbol()
            numSymbols -= 1
        else:
            keys.pop(1)
    elif randKey == "c":
        if numNums > 0:
            pswd += addNumber()
            numNums -= 1
        else:
            keys.pop(2)

print(pswd)

Solution

  • I know you state you are interested in your particular algorithmic approach, but, if it's of any help, here is a much simpler and more foolproof approach, too:

    import itertools
    import random
    import string
    
    letters = string.ascii_lowercase
    numbers = string.digits
    symbols = ["!", "#", "$", "%", "&", "(", ")", "*", "+"]
    
    characters = [letters, numbers, symbols]
    
    n_letters = int(input("How many letters would you like to include? "))
    n_numbers = int(input("How many numbers would you like to include? "))
    n_symbols = int(input("How many symbols would you like to include? "))
    
    user_inputs = [n_letters, n_numbers, n_symbols]
    
    pswd = [random.choices(x, k=y) for (x, y) in zip(characters, user_inputs)]
    
    pswd = list(itertools.chain.from_iterable(pswd))
    pswd = "".join(random.sample(pswd, k=len(pswd)))
    
    print(pswd)
    print(len(pswd))
    
    How many letters would you like to include? 10
    How many numbers would you like to include? 3
    How many symbols would you like to include? 1
    fx*m2wnyiu63ib
    14