Hope you’re doing well!
I’m fairly new to Codecademy and this is my first post.
I’m currently solving the Medical Ensurance Exercise of the Classes Chapter in the Data Scientist Career Path. (https://www.codecademy.com/paths/data-science/tracks/dscp-python-fundamentals/modules/dscp-python-classes/projects/ds-python-classes-project)
The 12th task asks you to create a class method which creates a dictionary including the class attributes as a string and the values of said attributes as values.
The hint encourages you to just fill the values in manually but i thought it would be cooler/better if it involved some sort of automation. So i tried to take a part of the directory list as keys
( keys = dir(self)[dir(self).index(‘age’):dir(self).index(‘smoker’)+1 )
and add the values in a for loop but i just cant find out a way on how to make it work.
My question is: Is there a somewhat simple way to make this method create the desired dictionary using the list above as input for the keys?
Please dont mind if my code is bad and/or my question is dumb i’m new to codecademy as well as coding as a whole.
Also i really want to get this code running because i felt quite smart when i got the idea to use the directory list as part of my code and i desperately want to see it work haha.
Thanks in advance and have a great week. The whole code is below.
Greetings from Bavaria!
class Patient:
def __init__(self, name, age, sex, bmi, noc, smoker):
self.name = name
self.age = age
self.sex = sex
self.bmi = bmi
self.num_of_children = noc
self.smoker = smoker
def estimated_insurance_cost(self):
cost = 250*self.age - 128*self.sex + 370*self.bmi + 425*self.num_of_children + 24000*self.smoker - 12500
#print("{name}'s estimated insurance costs is {cost} dollars.".format(name = self.name, cost = cost))
return cost
def update_age(self, new_age):
self.age = new_age
print("{name} is now {age} years old.".format(name = self.name, age = self.age))
self.estimated_insurance_cost()
def update_num_of_children(self, new_num):
self.num_of_children = new_num
if new_num == 1:
print("{name} has 1 child.".format(name = self.name))
else:
print("{name} has {noc} children.".format(name = self.name, noc = self.num_of_children))
self.estimated_insurance_cost()
def patient_profile(self):
wip = {}
keys = dir(self)[dir(self).index('age'):dir(self).index('smoker')+1]
for n in keys:
var = eval(n)
wip[n] = self.var
patient1 = Patient("John Doe", 25, 1, 22.2, 0, 0)
#print(patient1.estimated_insurance_cost())
print(patient1.patient_profile())
#print(dir(patient1))
#print(dir(patient1)[dir(patient1).index('age'):dir(patient1).index('smoker')+1])
Exactly how you choose the relevant attribute names is up to you but an iterable sequence sounds nice to me.
In future please see How do I format code in my posts? which helps keep indentation and formatting of your code which is very important for a whitespace reliant language like Python.
If anyone is interested:
getattr works great for this. What does not work is using the part between ‘age’ and ‘smoker’ in the dir() list since it also includes the self-defined methods as entires.
My solution to this is a bit wonky but i want to get on with the lessons.
I renamed the number_of_children attribute to noc and put a conditional statement in the for loop which excludes attributes with underscores.
It looks like this:
def patient_profile(self):
wip = {}
for n in dir(self):
if '_' not in n:
wip[n] = getattr(self, n)
return wip
So using this one can only use attribute names without underscores, which definitely isnt perfect.
Kudos for digging a bit deeper on this one, I like the idea behind this option.
I’d be wary of using dir, even in the docs it mentions that it’s not strictly a complete list of attributes, I think the intended purpose is technically to be more of a debugging tool. Something like vars() might be closer since it directly gets the instance .__dict__ which means you avoid any methods in the first place since they’re bound to the class and not the instance. You’d want to be a little bit careful about working with this since it’s not a copy by default but the actual instance data (a copy or a deepcopy might be necessary if the returned dictionary will be modified).
If you’re just getting started with Python classes then it might be going a bit too far here but it’s always worth keeping in mind what your class may be used for. For robustness and safety when subclassing I think it’s worth considering just defining a sequence of attributes you actually want (rather than filtering out the ones you don’t want). Perhaps a default parameter or a class attribute with a basic set of names for the dictionary could make it easier to work with whilst giving you the option to change what the dictionary actually returns if you wanted to.