I'm still learning python so I decided to experiment by making a tiny text adventure. I'm only a student in highschool so I haven't had the chance to learn many common techniques used in code as most of my learning has been self taught, so I wanted to take this opportunity to have some kind of review of some of the techniques I use in this code.Here it is in it's entirety:class Character:    def __init__(self, class_name, strength, dexterity, intelligence):        self.class_name = class_name        self.strength = strength        self.dexterity = dexterity        self.intelligence = intelligence        self.health = strength*10    def print_stats(self):        return self.class_name +  Strength:  + str(self.strength) +  Dexterity:  + str(self.dexterity) +  Intelligence:  + str(self.intelligence)    def print_stat_strength(self):        return Strength:  + str(self.strength)    def print_stat_dexterity(self):        return Dexterity:  + str(self.strength)    def print_stat_intelligence(self):        return Intelligence:  + str(self.strength)    def print_stat_health(self):        return Max Health:  + str(self.health)    def change_stat_strength(self, new_value):        self.strength = new_value    def change_stat_dexterity(self, new_value):        self.dexterity = new_value    def change_stat_intelligence(self, new_value):        self.intelligence = new_valuedef character_select():    global player_class    barbarian = Character(Barbarian, 20, 10, 10)    archer = Character(Archer, 10, 20, 10)    wizard = Character(Wizard, 10, 10, 20)    print(Welcome to TEST TEXT ADVENTURE RPG. Choose a character!)    print((A):  + barbarian.print_stats())    print((B):  + archer.print_stats())    print((C):  + wizard.print_stats())    while True:        user_answer = input().upper()        if user_answer == A:            player_class = barbarian            break        elif user_answer == B:            player_class = archer            break        elif user_answer == C:            player_class = wizard            break        else:            print(Invalid input! Try again.)    print(####CHARACTER CREATION COMPLETE####)def main():    character_select()    print(player_class.print_stats())main()Looking at this, my main concern is how the main function is utilized. To my knowledge it is supposed to be relatively clean and made up of other functions, lacking a lot of original code of its own. Making the above, I came into an issue trying to use the player_class object that I created, since it was created in the character_select function, it could only be used there unless it was global. The global fix seems kind of clunky so I was wondering the proper way to implement this kind of thing.One other concern that I can identity could be the built in functions in the Character class. Do I need to make a separate function for every single stat like strength, dexterity, intelligence, or is it possible to do something like this?def change_stat(self, stat_to_change, new_number):    self.stat_to_change = new_numberNow I'm asking this question because I tried the above code on my own to try to cut down on the amount of similar functions for each individual stat, but I couldn't seem to use it how I imagined. Those are my main concerns but anybody reading this is welcome to bring up any other improper coding technique. I really don't want bad habits to manifest themselves and continue to appear if I can help it.
Character Creation in Text Adventure; Proper use of Main and Global Values?
python
There is a way to pass the attribute name which you want to change, you can use setattr:def change_stat(self, stat_to_change, new_number):    setattr(self, stat_to_change, new_number)However, there is no difference to what would happen if you just say self.strength = 10 instead of self.change_stat(strength, 10).Regarding the need for global for the player character, there isn't. Just return the chosen class from the character_select function:def character_select():    barbarian = Character(Barbarian, 20, 10, 10)    archer = Character(Archer, 10, 20, 10)    wizard = Character(Wizard, 10, 10, 20)    print(Welcome to TEST TEXT ADVENTURE RPG. Choose a character!)    print((A):  + barbarian.print_stats())    print((B):  + archer.print_stats())    print((C):  + wizard.print_stats())    while True:        user_answer = input().upper()        if user_answer == A:            return barbarian        elif user_answer == B:            return archer        elif user_answer == C:            return wizard        else:            print(Invalid input! Try again.)You could make it even nicer if you used a dictionary:from collections import OrderedDictdef character_select():    classes = OrderedDict([(A, Character(Barbarian, 20, 10, 10)),                           (B, Character(Archer, 10, 20, 10)),                           (C, Character(Wizard, 10, 10, 20))])    print(Welcome to TEST TEXT ADVENTURE RPG. Choose a character!)    for key, cls in classes.items():        print(({}): {}.format(key, cls.print_stats()))    while True:        user_answer = input().upper()        try:            return classes[user_answer]        except KeyError:            print(Invalid input! Try again.)You can then use it like this in your main function:def main():    player_class = character_select()    print(player_class.print_stats())You should probably rename your Character.print_stats function. Right now it sounds like it would actually print the stats, but it just returns them. So I would make your class like this:class Character:    def __init__(self, class_name, strength, dexterity, intelligence):        self.class_name = class_name        self.strength = strength        self.dexterity = dexterity        self.intelligence = intelligence        self.health = strength*10    def __str__(self):        return {self.class_name}  \               Health: {self.health}  \               Strength: {self.strength}  \               Dexterity: {self.dexterity}  \               Intelligence: {self.intelligence}.format(self=self)If you are using Python 3.6, you can use f{self.class_name} ... instead of {self.class_name} ....format(self=self), which uses f-strings.Because I named the function __str__, you can now do:barbarian = Character(Barbarian, 20, 10, 10)print(barbarian)# Barbarian Health: 200 Strength: 20 Dexterity: 10 Intelligence: 10And the loop in character_select becomes this, because str.format automatically calls str on its arguments, which will then invoke the Character.__str__ function:for key, cls in classes.items():    print(({}): {}.format(key, cls))__str__ is one of the magic (or dunder) methods, which can give your class features so it acts like built-in classes. Have a look at this nice guide for a list and explanation of all of them.Finally, you should add a if __name__ == __main__: guard to allow importing your functions from other scripts:if __name__ == __main__:    main()