I develop bottom up, starting with small simple methods to go to the big full fledged implementation
class Pop(object):
def welcome(self, name, new_member = False):
response = ""
if new_member:
response = " NOT"
return str("hello there "+name+", you seem"+response+" to be a member\n")
def ageVerification(self, name, age, new_member = False):
the_welcome_string = self.welcome(name, new_member)
minimum = ""
excuse = ""
if age < 16:
minimum = " NOT"
excuse = ", sorry"
return str(the_welcome_string+str(age)+" is"+minimum+" the minimum required age to buy beer in Belgium"+excuse+"\n")
def theWholething(self, name, age, address, new_member = False):
if age < 16:
appology = str("you cannot order any beer\n")
else:
appology = str("your beer will be shipped to "+address+"\n")
return str(self.ageVerification(name, age, new_member)+appology)
# EOF
My question is if it is normal that when i reach theWholeThingMethod, I carry along all the parameters of the previously defined methods? Is this pythonic?
My population class has almost 20 "helper" methods called in theWholeThing, and it seems I am just fiddling with parameters to get them in the right order ...
theWholeThing(self,\
name,\
age,\
address,\
registered = True,\
first_date_entered,\
last_date_entered,\
purchased_amount,\
favorite_beer,\
promotional_code,\
and_so_on0,\
and_so_on1,\
and_so_on2,\
and_so_on3,\
and_so_on4,\
and_so_on5,\
and_so_on6,\
and_so_on7,\
and_so_on8,\
and_so_on9,\
and_so_on10):
My question is if it is normal that when i reach theWholeThingMethod, I carry along all the parameters of the previously defined methods? Is this pythonic?
Neither.
There is really no point in having a class if all the methods take all the arguments anyway. These might as well just be functions.
There are many ways this could be done, depending on whether the various parameters are mandatory, or what happens when one is not provided, but here is one possibility:
from dataclasses import dataclass
#dataclass
class Pop(object):
name: str
age: int
address: str
new_member : bool = False
def welcome(self):
response = ""
if self.new_member:
response = " NOT"
return str("hello there "+self.name+", you seem"+response+" to be a member\n")
def ageVerification(self):
the_welcome_string = self.welcome()
minimum = ""
excuse = ""
if self.age < 16:
minimum = " NOT"
excuse = ", sorry"
return str(the_welcome_string+str(self.age)+" is"+minimum+" the minimum required age to buy beer in Belgium"+excuse+"\n")
def theWholething(self):
if self.age < 16:
appology = str("you cannot order any beer\n")
else:
appology = str("your beer will be shipped to "+self.address+"\n")
return str(self.ageVerification()+appology)
# EOF
Note: #nneonneo had a great suggestion of using a dataclasses, so answer tweaked to incorporate that
Related
I am making a text based adventure game in python. Once the game begins, I would like to create an instance of a class called "Character" which is the player's character object. I would like the user to be able to choose the race of the character they want to play. So far I have:
class Race:
def __init__(self, name, passive, hp):
self.name = name
self.passive = passive
self.hp = hp
and
class Lizard(Race):
def __init__(self, name, passive, hp):
super().__init__(name, passive, hp)
self.name = 'Lizardman'
self.passive = 'Regrowth'
self.hp = 20
def regrowth(self):
if 0 < self.hp <= 18:
self.hp += 2
and
def race_select():
races = ['Lizard']
while True:
for i, j in enumerate(races):
print(f"[{i + 1}]", j)
choice = int(input('Pick a race:'))
if choice <= len(races):
print('You are a ', races[choice - 1])
return races[choice - 1]
else:
continue
If I understand correctly, if I wanted the race to be a Lizard, I would still have to do
character = Lizard('Lizardman', 'Regrowth', 20)
Is there an easy way to let the user choose the race and the object to be created accordingly? Thanks
A simple solution would be to map a name to a class using a dictionary. As a simple example:
race_map = {"lizard": Lizard,
"human": Human} # I'm adding a theoretical other class as an example
choice = input('Pick a race:')
race_initializer = race_map.get(choice, None) # Get the chosen class, or None if input is bad
if race_initializer is None:
# They entered bad input that doesn't correspond to a race
else:
new_creature = race_initializer(their_name, their_passive, their_hp)
new_creature is now the new object of the chosen class.
You may want to standardize the input using choice.lower() to ensure that capitalization doesn't matter when they enter their choice.
I changed it to allow for specifying a race by a string name instead of a number. If you wanted a number, you could keep your list, but apply the same idea. Something like:
race_list = races = [('Lizard', Lizard), ('human', Human)]
choice = int(input('Pick a race:'))
try:
race_initializer = race_list[choice][1] # 1 because the class object is the second in the tuple
new_creature = race_initializer(their_name, their_passive, their_hp)
except IndexError:
# Bad input
I included the name in the race_list so that you can loop over the list and print out index->name associations for the user to pick from.
You may also want to use a more robust structure than a plain tuple to store name->initializer mappings, but it works well in simple cases.
It requires isgood to be a string only. I want it to be boolean.
Also self.__isgood = goodornot should throw an error, why not?
class Animal:
Name = ""
isgood = None
def setisgood(self,goodornot):
self.__isgood = goodornot
def nameset(self,name):
self.Name = name
dog = Animal()
dog.setisgood(False)
dog.nameset("jaang")
print("Your pet is:"+dog.isgood)
So then convert it to a string.
print("Your pet is: {}".format(dog.isgood))
print("Your pet is:"+dog.isgood)
is attempting to concatenate a boolean and a string, which cannot be done. In order to do so, you either need to convert dog.isgood into a string
print("Your pet is:" + str(dog.isgood))
like TheoretiCAL suggested in the comments, or use format
print("Your pet is:{}".format(dog.isgood))
like Ignacio Vazquez-Abrams answer, or
print("Your pet is:%s" % dog.isgood)
or,
print("Your pet is:", dog.isgood)
All of these examples will result in the following output:
Your pet is:False
Edit:
Thank you juanpa for pointing this out. The spacing on the class is incorrect and should be causing an issue (I guess I assumed initially that it was just a copy paste thing).
class Animal:
def __init__(self):
self.Name = ""
self.isgood = None
def setisgood(self,goodornot):
self.isgood = goodornot
def nameset(self,name):
self.Name = name
dog = Animal()
dog.setisgood(False)
dog.nameset("jaang")
print("Your pet is:", dog.isgood)
Here's my code:
class student:
def __init__(self):
self.totalSumOGrades = 0
self.numberOGrades = 0
def getAverageScore(self):
return (self.totalSumOGrades / self.numberOGrades)
def addGrade(self,grade):
self.totalSumOGrades = str(grade)
self.numberOGrades = self.numberOGrades + 1
return (self.totalSumOGrades)
class GPA:
def __init__(self,grade):
self.grade = grade
self.score = 0
def gradesScore(self):
gradeLetter = self.grade[0]
gradeSign = ' '
if (len(self.grade)) == 2:
gradeSign = self.grade[1]
if (gradeLetter == 'A'):
self.score = 4
elif (gradeLetter == 'B'):
self.score = 3
elif (gradeLetter == 'C'):
self.score = 2
elif (gradeLetter == 'D'):
self.score = 1
elif (gradeLetter == 'F'):
self.score = 0
if (gradeSign == '+'):
self.score += 0.3
elif (gradeSign == '-'):
self.score -= 0.3
def getScore(self):
self.gradesScore()
return self.score
I need both classes on one sheet. The problem I'm having is the argument that is being taken for class GPA is what "getScore" is calculating. I need it so the addGrade from class student adds a grade and I can have "getScore" calculate those grades instead. How do i fix this?
So as I promised an answer / feedback version to help the OP walk through and pick some new questions - here it is (offered in the hope that train hacking is sufficiently high quality to show case some concepts and offer agood start to create a real solution based on this):
#! /usr/bin/env python
from __future__ import division, print_function
class GradePointAverage(object):
"""Implements a "Grade Point Average" (GPA) calculation.
Note: It keeps all grades to offer an incremental update path.
"""
FILL_CHAR_ZERO_ADJUST = '.'
VALID_LETTERS = ('A', 'B', 'C', 'D', 'E', 'F')
VALID_ADJUSTMENTS = ('+', FILL_CHAR_ZERO_ADJUST, '-')
BASE_SCORE_MAP = dict(zip(reversed(VALID_LETTERS), (0., 1., 2., 3., 4.)))
ADJ_SCORE_MAP = dict(zip(VALID_ADJUSTMENTS, (0.3, 0., -0.3)))
def __init__(self, grades=None):
"""Inintializes the _grades, a sequence of canonical grade
values e.g. ['A+', 'B-'] where any grade recieved is
mapped to uppercase letter plus either ''|'+'|'-' or
and exception ValueError is thrown.
"""
if grades is None:
self._grades = list()
else:
self._grades = [self.canonicalize_grade(g) for g in grades]
def __repr__(self):
"""Helper to (de)serialize and put more in print et al."""
return ('GradePointAverage(%s)' % (str(self._grades)))
def add_grades(self, grades):
"""Add a new result / grade to data."""
for g in grades:
self.add_grade(g)
def add_grade(self, grade):
"""Add a new result / grade to data."""
self._grades.append(self.canonicalize_grade(grade))
def count_grades(self):
"""Return the count of grades, the scoring is based upon."""
return len(self._grades)
def grades(self):
"""Return the grades as list, the scoring is based upon."""
return self._grades
def canonicalize_grade(self, grade):
"""Ensure grade is valid, ensure uppercase letter plus either
''|'+'|'-' on output. If invalid, let raise or throw ValueError. """
c_grade = grade.strip().upper() # May raise
if 1 <= len(c_grade) <= 2:
if len(c_grade) < 2:
c_grade += self.FILL_CHAR_ZERO_ADJUST
else:
raise ValueError("Invalid grade length")
if c_grade[0] not in self.VALID_LETTERS:
raise ValueError("Invalid main grade")
if c_grade[1] not in self.VALID_ADJUSTMENTS:
raise ValueError("Invalid grade adjustment")
return c_grade
def _score(self, canonical_grade):
"""Calculate score from canonical grade."""
base, adj = canonical_grade[0], canonical_grade[1]
return self.BASE_SCORE_MAP[base] + self.ADJ_SCORE_MAP[adj]
def average_score(self):
"""Calculate average score."""
if not self.count_grades():
return None
# implicit else:
score_sum = sum(self._score(c_g) for c_g in self._grades)
return score_sum / float(self.count_grades())
def median_score(self):
"""Calculate median score."""
if not self.count_grades():
return None
# implicit else:
middle_index = self.count_grades() // 2
return sorted([self._score(c_g) for c_g in self._grades])[middle_index]
def best_score(self):
"""retrieve highest score."""
return NotImplemented
class Student:
"""Models student with updateable Grade Point Average."""
def __init__(self, grades):
self._gPA = GradePointAverage(grades)
self.number_of_grades = self._gPA.count_grades()
def __repr__(self):
"""Helper to (de)serialize and put more in print et al."""
return ('Student(%s)' % (str(self._gPA.grades())))
# Delegated / proxy methods
def average_score(self):
return self._gPA.average_score()
def count_grades(self):
return self._gPA.count_grades()
def grades(self):
return self._gPA.grades()
def median_score(self):
return self._gPA.median_score()
def best_score(self):
return self._gPA.best_score()
def add_grade(self, grade):
return self._gPA.add_grade(grade)
def add_grades(self, grades):
return self._gPA.add_grades(grades)
def main():
"""Drive some tests on "scored" Students."""
print('Positive test cases:')
print('... service class under test:')
gPA = GradePointAverage(['a+', 'c-'])
print(gPA)
print('... main class under test:')
student = Student(['F+'])
print(student)
print(student.count_grades())
print(student.average_score())
print(student.median_score())
a_grade = 'E-'
print("Added %s" % (a_grade,))
student.add_grade('E-')
print(student.count_grades())
print(student.average_score())
print(student.median_score())
some_grades = ['E', 'b+', 'b-', 'c+', 'D', 'D']
print("Added %s" % (str(some_grades),))
student.add_grades(some_grades)
print(student.count_grades())
print(student.average_score())
print(student.median_score())
print(student.grades())
print('Negative test cases:')
print(student.best_score())
print('... too long:')
try:
_ = GradePointAverage(['aa+', 'no_impact'])
except ValueError as e:
print(e)
print('... wrong grade letter:')
try:
_ = GradePointAverage(['z', 'no_impact'])
except ValueError as e:
print(e)
print('... wrong adjustment:')
try:
_ = GradePointAverage(['A*', 'no_impact'])
except ValueError as e:
print(e)
print('... wrong grade "type" we did let it just bubble:')
try:
_ = GradePointAverage([42, 'no_impact'])
except AttributeError as e:
print(e)
if __name__ == '__main__':
main()
So a student instance always delegates grade and score related tasks to the member instance of the GradePointsAverage class. This shifts the Student class close to ebing a superfluous layer (as is) but in reality you would now stuff personal info identifying the student being modelled into the Student instance.
When I run above code on my machine (with a python v2 interpreter):
Positive test cases:
... service class under test:
GradePointAverage(['A+', 'C-'])
... main class under test:
Student(['F+'])
1
0.3
0.3
Added E-
2
0.5
0.7
Added ['E', 'b+', 'b-', 'c+', 'D', 'D']
8
2.1625
2.0
['F+', 'E-', 'E.', 'B+', 'B-', 'C+', 'D.', 'D.']
Negative test cases:
NotImplemented
... too long:
Invalid grade length
... wrong grade letter:
Invalid main grade
... wrong adjustment:
Invalid grade adjustment
... wrong grade "type" we did let it just bubble:
'int' object has no attribute 'strip'
[Finished in 0.0s]
One should IMO not over engineer toy problems, but this one might offer interesting extension tasks, like storing a hash/anonymized id with the student instances. That would match more real life, where the storage of data that might allow identification of a person or might have the potential to disclose private details of a person is often split into spreading salted hashes to attach to all class instances that need a back reference, but keep the mapping back to ther real names, dates and places etc. in one especially secure place only.
Also maybe introduce (besides the added median) also min/max i.e. worst/best score or grade "info" from any student instance, maybe even try a simple linear regression on the scores to find some "trend".
Another class of extensions would be trying to "cover" all paths in tests.
A further way to "blow things up" might be, a more elgant mini language internal mapping, where the "collection stages" (those mapping from grades to numeric values) are fully transformed to integers eg. by scaling all by a factor of ten and so have lossless arithmetics, with the price to think about "reporting" the expected back transformed real scores (i.e. as sample 4.3 and not 43) but also have the benefit of reporintg easily the grade representation from any score and remebering to only perform one final "rounding" step.
Note also that the helpful pep8 tool or e.g. python3 -m pep8 so_get_gpa_for_student_edited.py gives neither errors nor warnings.
Another hint, often during development and when extending objects / adding fetaures, the names slowly drift out of bounds. Until now (to me) GradePointAverage is a matching class / type name, as I often accept a median in comparison to an arithmetic average as a usefull twin information. But if I already had entered say. a trend method, then it would have been a good time to further separate the functionality or rename the class.
Also deciding on a consisten error strategy helps a lot. In above code we mostly state the problem class, but eg. do not report back what exactly caused the problem. In many cases this is ok or even wanted, but in other cases one might add some detail to the response.
One last detail: Just so you know how to first define all interface methods, and second implement these one by one while incrementally testing I also added one of the tricks to signal when somethig is planned, but not (yet) implemented. Here I simply return NotImplementedin the best_score method. One might also raise NotImplementedError("Method_best_score") instead, but as this leads to:
File "/Users/sthagen/tmp/so_get_gpa_for_student_edited.py", line 184, in <module>
main()
File "/Users/sthagen/tmp/so_get_gpa_for_student_edited.py", line 157, in main
print(student.best_score())
File "/Users/sthagen/tmp/so_get_gpa_for_student_edited.py", line 117, in best_score
return self._gPA.best_score()
File "/Users/sthagen/tmp/so_get_gpa_for_student_edited.py", line 90, in best_score
raise NotImplementedError("Method best_score")
NotImplementedError: Method best_score
I often during active creation from "zero" the more silent return NotImplemented option and in (pre-)production when a call to a not yet implemented method or function is more probable an error in usage, I switch to the Exception, your mileage may vary ...
Please feel free to comment (in case I misread the task) or if I forgot to comment on a change you notice when comparing to your code smaple.
Let's say I have a spell named heal. How can I prevent a user from spamming heal every time they are damaged. I have considered applying this to individual combat functions; however, I am not sure how to implement a global rule for this? This code may clear it up:
available_spells = ['fireball', 'heal']
equipped = {'Weapon': "Staff",
'Armor': "Robes",
'Spells': ['fireball', 'heal']}
print "Your available spell(s) is(are) '%s'. " % equipped["Spells"]
inp = raw_input("Type the name of a spell you want to use.: ").lower()
lst = [x for x in available_spells if x.startswith(inp)]
if len(lst) == 0:
print "No such spell"
print ' '
elif len(lst) == 1:
spell = lst[0]
print "You picked", spell
#COMBAT FUNCTIONS HERE
else:
print "Which spell of", equipped["Spells"], "do you mean?"
If I were to make a class that defines certain actions for spells to take, how could I implement that into the code I have? For example if I have a class of spells, with functions defining damage rules, cool down times, etc., how could I reference that function in the code I already have? i.e. the player types 'heal' and I want it to reference an above class that has those values defined to check if the player recently played the spell, and what it does when played.
Am I clear enough in this question? How should I write a spell cool-down mechanic? How can I implement this mechanic into the code above?
Instead of storing all available spells as a list, you could store them as a dictionary, which allows you to also store the desired cooldown duration:
available_spells = {
# spell name: cooldown duration in seconds
'fireball': 3.0,
'heal': 5.0,
}
Each player could have another dict that keeps track of the last time they cast each spell. When the game starts, it would be empty:
cast_spells = {}
When the player attempts to cast a spell, check if the spell name is in the cast_spells dict. If it's not, then they have not yet cast it this game, so they are allowed to cast it:
if spell_name not in cast_spells:
cast_spells[spell_name] = datetime.now()
Otherwise, if the spell name is in the cast_spells dict, check if the required cooldown has elapsed:
elif cast_spells[spell_name] + datetime.timedelta(seconds=spells[spell_name]) < datetime.now():
cast_spells[spell_name] = datetime.now()
Otherwise, the cooldown is still in effect.
else:
print 'Spell not ready.'
I would probably do it using with, an exception handler, and a simple timer. That way you can just repeat the cooldown pattern, have shared cooldowns (like shown below), or even global cooldowns, etc.
Here are the classes:
import time
class CooldownException(Exception):
pass
class Cooldown(object):
def __init__(self, seconds):
self.seconds = seconds
self.expire = None
def __enter__(self):
if not self.expire or time.time() > self.expire:
self.expire = time.time() + self.seconds
else:
raise CooldownException('Cooldown not expired!')
def __exit__(self, type, value, traceback):
pass
heal_cooldown = Cooldown(5)
def heal():
try:
with heal_cooldown:
print 'You heal yourself!'
except CooldownException as e:
print e
def apply_bandage():
try:
with heal_cooldown:
print 'You bandage yourself!'
except CooldownException as e:
print e
def drink_potion():
try:
with heal_cooldown:
print 'You quaff a potion!'
except CooldownException as e:
print e
And here's how they're used:
>>> heal()
You heal yourself!
>>> time.sleep(3)
>>> drink_potion()
Cooldown not expired!
>>> time.sleep(3)
>>> apply_bandage()
You bandage yourself!
If I were to make a class that defines certain actions for spells to take, how could I implement that into the code I have?
As you guessed, your problem is very well suited to classes.
Am I clear enough in this question?
Yes.
Your program, but with classes
Here is your program modified to use two custom classes, FireballSpell and HealSpell. Each one has a .name, which is a string, and a .cast(), which is a custom behaviour. It's nearly identical to your original code, so it should be easy for you to understand:
available_spells = [FireballSpell(), HealSpell()]
equipped = {'Weapon': "Staff",
'Armor': "Robes",
'Spells': [FireballSpell(), HealSpell()]}
while True:
print "Your available spell(s) is(are) '%s'. " % [spell.name for spell in equipped["Spells"]]
inp = raw_input("Type the name of a spell you want to use.: ").lower()
lst = [spell for spell in available_spells if spell.name.startswith(inp)]
if len(lst) == 0:
print "No such spell"
print ' '
elif len(lst) == 1:
spell = lst[0]
print "You picked", spell.name
spell.cast()
else:
print "Which spell of", [spell.name for spell in equipped["Spells"]], "do you mean?"
print ""
Run it and give it a try! Here is the complete script. I'm pretty sure it does exactly what you want.
Specific spells
Each specific class has a name, cooldown time, and specific behaviour. The parent Spell class (see bottom) handles the rest.
class FireballSpell(Spell):
def __init__(self):
self.name = "fireball"
self.cooldown_seconds = 5
def spell_specific_behaviour(self):
# do whatever you like with fireball
# this is only called if the spell has cooled down
print "casting fireball"
class HealSpell(Spell):
def __init__(self):
self.name = "heal"
self.cooldown_seconds = 10
def spell_specific_behaviour(self):
# same applies here as from FireballSpell
print "casting heal"
Spell class
This is a generic Spell class - the parent of all spells. It knows the name, cooldown time, and behaviour from the specific spells (child classes above). It also has generic cooldown mechanic that's shared by the spells:
class Spell:
# spell data - filled in by specific spells
name = "default"
cooldown_seconds = 0
last_cast_time = 0
def cast(self):
# only cast the spell if it has cooled down
if self.is_cooled_down():
# call the behaviour set by the specific spell
self.spell_specific_behaviour();
# set the last cast time to the current time
self.last_cast_time = time.time()
else:
print self.name + " has not cooled down yet!"
def spell_specific_behaviour(self):
# implement in specific spell subclasses
return
def is_cooled_down(self):
current_time_seconds = time.time()
cooldown_expire_time_seconds = self.last_cast_time + self.cooldown_seconds
return current_time_seconds > cooldown_expire_time_seconds
Again, here is the whole thing in one working script. Have fun!
META: decorators, exceptions, and with blocks? Whoa, guys. OP is just now learning about classes. Let's keep it simple here.
Here is another example using decorators...
from functools import wraps
class Cooldown(object):
def __init__(self, seconds, cooldown_message):
self.seconds = seconds
self.expire = None
self.cooldown_message = cooldown_message
def decorator(self, fail_message_callback):
def _wrap_decorator(foo):
def _decorator(*args, **kwargs):
if not self.expire or time.time() > self.expire:
self.expire = time.time() + self.seconds
result = foo(*args, **kwargs)
return result
else:
if fail_message_callback:
fail_message_callback(self.cooldown_message)
return None
return wraps(foo)(_decorator)
return _wrap_decorator
heal_cooldown = Cooldown(5, 'Cooldown not expired!')
def display(message):
print message
#heal_cooldown.decorator(display)
def heal():
display('You heal yourself!')
#heal_cooldown.decorator(display)
def apply_bandage():
display('You bandage yourself!')
#heal_cooldown.decorator(display)
def drink_potion():
display('You quaff a potion!')
heal()
time.sleep(3)
drink_potion()
time.sleep(3)
apply_bandage()
So I was having some trouble with some code I'm doing for a basic game that I'm writing in my learning of Python (original question here if it helps).
After a lot of playing around with it, I realized my problem. I don't actually know how to do a "has-a" when trying to let one object (a character) "have" an object of another class (such as a weapon).
If I want, say a Char (character) called dean to do an action specific to the weapon he has. How would I give that object (the Char dean) an object of class "Weapon"?
Thank you.
Edit:
I've found that I can do this by passing the "Weapon" in question as an argument. i.e.:
dean = Char("Dean", hanzo_blade)
Then having Char init with (self, name, weapon).
However, I want the user to choose, from a selection, which weapon the character gets. So I'm not sure if the content of the Char(_____) can be determined on a dynamic basis from user input. If it's possible, how can I do that? If not, what do I do?
Thanks again.
Edit 2: Here is the pertinent code:
class Char(object):
def __init__(self, name):
self.name = name
self.hp = 300
self.mp = 10
self.strn = 1
self.dex = 1
self.armor = 0
self.xp = 0
self.items = []
self.spells = []
self.weapon = sword() # Assume sword is the default. Alternatively, how might I let this default to nothing?
class Weapon(Equip):
impact = 1
sharp = 1
def __init__(self, name):
self.name = name
hanzo_blade = Weapon("Hanzo Blade")
hanzo_blade.wgt = 3
hanzo_blade.impact = 1
hanzo_blade.sharp = 9
dean = Char("Dean")
dean.strn = 3
dean.dex = 8
If hanzo_blade is one option, how could I let the player, for instance, select that Weapon for the Char dean?
Are you aware of the raw_input builtin? (input in Python 3)
# We assume we already have sword, fists, and stern_glare objects
# representing weapons.
weapons = {'sword': sword, 'fists': fists, 'stern glare': stern_glare}
# Prompt for a choice, and keep prompting until you get a valid choice.
# You'll probably want a more user-friendly prompt.
valid_choice = False
while not valid_choice:
weapon_choice = raw_input('Select your weapon')
valid_choice = weapon_choice in weapons
# Create the character.
dean = Char('Dean', weapons[weapon_choice])