Iterating through python dictionary giving unexpected behavior

Question:

I need to iterate through a dictionary, assigning key values from user input based on a list. If the user enters anything other than valid values from the list, an else statement or exception catches it, and re-sets the list.
I then need to take the values and pass them to a class object as parameters, like:

Class Player(self, strength, dexterity, constitution, intelligence, wisdom, charisma)
    self.strength = strength
    ...

I am expecting the variables to be set by the values, but all are still zero at the end of the function.

def character_generator():
    cls()
    player_name = input(f"Please enter character name: ")
    strength = 0
    dexterity = 0
    constitution = 0
    intelligence = 0
    wisdom = 0
    charisma = 0
    display_dict = {'Strength': strength,
                    'Dexterity': dexterity,
                    'Constitution': constitution,
                    'Intelligence': intelligence,
                    'Wisdom': wisdom,
                    'Charisma': charisma}
    score_list = [15, 14, 13, 12, 10, 8]
    
    while len(score_list):
        for key in display_dict.keys():
            cls()
            
            print(score_list)
            try:
                score = int(input(f"Enter score to assign to {key}: "))
                if score in score_list:
                    print(f"{key} = {score}")
                    display_dict[key] = score
                    score_list.remove(score)
                    
                else:
                    score_list = [15, 14, 13, 12, 10, 8]  # re-set list
                    print(f"Valid scores are listed above.")
                    sleep(.5)
                    print(f"Starting over.")
                    sleep(.5)
                    break
            except ValueError:
                print(f"Invalid entry..")
                score_list = [15, 14, 13, 12, 10, 8]  # re-set list
                sleep(.5)
                print(f"Starting over.")
                sleep(.5)
                break
    for key, value in display_dict.items():
        print(key, ':', value)
    print(strength)
    print(dexterity)
    print(constitution)
    print(intelligence)
    print(wisdom)
    print(charisma)

Asked By: misfit138

||

Answers:

There is nothing that would update those local variables, so little wonder they’re not getting updated.

However, you also don’t need them for anything – just use the dict as your single source of truth, and you can even ** splat it into your Character constructor.

def character_generator():
    cls()
    player_name = input(f"Please enter character name: ")
    stats = {
        "strength": 0,
        "dexterity": 0,
        "constitution": 0,
        "intelligence": 0,
        "wisdom": 0,
        "charisma": 0,
    }
    score_list = [15, 14, 13, 12, 10, 8]

    while len(score_list):
        for key in stats:
            human_key = key.capitalize()
            cls()

            print(score_list)
            try:
                score = int(input(f"Enter score to assign to {human_key}: "))
                if score in score_list:
                    print(f"{key} = {score}")
                    stats[key] = score
                    score_list.remove(score)

                else:
                    score_list = [15, 14, 13, 12, 10, 8]  # re-set list
                    print(f"Valid scores are listed above.")
                    sleep(0.5)
                    print(f"Starting over.")
                    sleep(0.5)
                    break
            except ValueError:
                print(f"Invalid entry..")
                score_list = [15, 14, 13, 12, 10, 8]  # re-set list
                sleep(0.5)
                print(f"Starting over.")
                sleep(0.5)
                break
    for key, value in stats.items():
        print(key, ":", value)
    player = Player(**stats)

Further, I’d simplify your code to

def character_generator():
    stats = {
        'strength': 0,
        'dexterity': 0,
        'constitution': 0,
        'intelligence': 0,
        'wisdom': 0,
        'charisma': 0,
    }
    score_list = [15, 14, 13, 12, 10, 8]
    assert len(score_list) == len(stats)  # So we haven't been silly

    for key in stats:
        human_key = key.capitalize()
        while True:
            try:
                score = int(input(f"Enter score to assign to {human_key} (from {score_list}): "))
            except ValueError:
                print("Please enter a valid number.")
                continue
            if score in score_list:
                print(f"{key} = {score}")
                stats[key] = score
                score_list.remove(score)
                break
            else:
                print(f"Invalid score. Please try again.")
    
    for key, value in stats.items():
        print(key, ':', value)
    player = Player(**stats)

You could refactor that to multiple functions too – a smaller function to prompt for a valid input comes to mind.

Answered By: AKX