Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Python Object-Oriented Python (retired) Inheritance Subclass

Justin Noor
Justin Noor
3,692 Points

What's the point of __init__ and **kwargs if we are assigning so many attributes to the individual classes/subclasses?

I am totally lost on this part of OOP.

Below is the code from the previous lecture. What's the point of using init and **kwargs if we are manually assigning so many attributes to these classes/subclasses anyways? It just seems messy.

my_monster.py
import random

COLORS = ['yellow', 'red', 'blue', 'green']


class Monster:
  min_hit_points = 1
  max_hit_points = 1
  min_experience = 1
  max_experience = 1
  weapon = 'sword'
  sound = 'roar'

  def __init__(self, **kwargs):
    self.hit_points = random.randint(self.min_hit-points, self.max_hit_points)
    self.experience = random.randint(self.min_experience, self.max.experience)
    self.color = random.choice(COLORS)

    for key, value in kwargs.items():
      setattr(self, key, value)

  def battlecry(self):
    return self.sound.upper()

class Goblin(Monster):
  max_hit_points = 3
  max_experience = 2
  sound = 'squeak'

class Troll(Monster):
  min_hit_points = 3
  max_hit_points = 5
  min_experience = 2
  max_experience = 6
  sound = 'growl'

class Dragon(Monster):
  min_hit_points = 5
  max_hit_points = 10
  min_experience = 6
  max_experience = 10
  sound = 'raaaaaar'

2 Answers

The first thing I want to point out, you have an semantic error inside __init__, more specifically on self.experience. you had set self.experience = random.randint(self.min_experience, self.max.experience)

self.max.experience calls the fxn max() instead of the intended use of self.max_experience being a variable, be careful with certain naming techniques they can cause undo stress.

corrected:

import random

COLORS = ['yellow', 'red', 'blue', 'green']


class Monster(object):
  min_hit_points = 1
  max_hit_points = 1
  min_experience = 1
  max_experience = 1
  weapon = 'sword'
  sound = 'roar'

  def __init__(self, **kwargs):
    self.hit_points = random.randint(self.min_hit_points, self.max_hit_points)
    self.experience = random.randint(self.min_experience, self.max_experience)     
    self.color = random.choice(COLORS)                                                                    
    print(self.hit_points, self.experience, self.color)

    for key, value in kwargs.items():
      setattr(self, key, value)

  def battlecry(self):
    return self.sound.upper()

class Goblin(Monster):
  max_hit_points = 3
  max_experience = 2
  sound = 'squeak'

class Troll(Monster):
  min_hit_points = 3
  max_hit_points = 5
  min_experience = 2
  max_experience = 6
  sound = 'growl'

class Dragon(Monster):
  min_hit_points = 5
  max_hit_points = 10
  min_experience = 6
  max_experience = 10
  sound = 'raaaaaar'

if __name__ == '__main__':
  tim, gerald, drew, patty = Monster(), Goblin(), Troll(), Dragon()
  john = Goblin

Start by copying the above code and running it.

What is__init__?

Most if not all classes by default contain the __init__ function, it is commonly refereed to as a "class constructor". When a class is initialized the __init__ fxn whether you overrode the fxn or not is run. It constructs the class on initialization....for example:

The output of the above code:

1 1 blue
1 2 blue
4 4 green
7 7 green

You can run this code multiple times and the output could be different every time thanks to __init__. Did you notice that john = Goblin didn't have an output?(explanation below)

How does this pertain to the code above?

Above we set gerald = Goblin() which is different from when we set john = Goblin. Lets call gerald case#1 and john case#2, in case#1 the class is initialized and in case#2 just the opposite. If you tried to call john.color since the class wasn't initialized python would throw an error AttributeError: type object 'Goblin' has no attribute 'color' on the flip side gerald.color would return a randomly generated value from the list COLORS. However you can still initialize case#2 by john(), at this point __init__ is called.

Class inheritance

Monster is what's called a super class or parent class, values in it will be inherited by the classes Dragon, Troll, and Goblin.

Dragon, Troll, and Goblin are referred to as a child class or a child of Monster since they inherit certain attributes from Monster.

**kwargs

**kwargs is a place holder variable that allows you to pass an undetermined number of variables to it. For instance since you aren't passing minimum values to the parent class from Goblin, you use **kwargs to prevent TypeError $FUNCTION() missing $NUM required positional arguments from occurring. By using **kwargs you are saying you are going to pass an indiscriminate number of variables to the function.

What's the point of using init and **kwargs if we are manually assigning so many attributes to these classes/subclasses anyways?

If you notice, you aren't assigning direct values to your classes you are passing parameters to the parent class which are calculated by the fxn __init__ upon initialization.

I hope I have answered your question, please let me know if you need further clarification.

  • Fundamentals of Python: First Programs by: Kenneth A. Lambert

https://www.amazon.com/Fundamentals-Python-Programs-Introduction-Programming/dp/1111822700/ref=sr_1_1?ie=UTF8&qid=1470645403&sr=8-1&keywords=fundamentals+of+python+first+programs

-Fundamentals of Python: Data Structures by: Kenneth A. Lambert

https://www.amazon.com/Fundamentals-Python-Structures-Kenneth-Lambert/dp/1285752007/ref=pd_sim_14_1?ie=UTF8&dpID=51MD0E6TRSL&dpSrc=sims&preST=_AC_UL160_SR129%2C160_&psc=1&refRID=HYEFA3046R5ETBFQV87P

  • Functional Programming by Bruce Maclennan

https://www.amazon.com/Functional-Programming-Practice-Bruce-Maclennan/dp/0201137445/ref=sr_1_10?s=books&ie=UTF8&qid=1470645482&sr=1-10&keywords=functional+programming

  • Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. “Uncle Bob” Martin

https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/ref=sr_1_1?s=books&ie=UTF8&qid=1470645557&sr=1-1&keywords=clean+code

Justin Noor
Justin Noor
3,692 Points

Hi Gerald. I appreciate you taking the time to reply. Thanks for spotting out that type as well. I don't think I made my question very clear. My bad, I was kind of rattled and I typed it on a whim.

I'm relatively clear on the the constructor init , and **kwargs, just not totally clear on the way they are used in the video.

The video uses the constructor init(self, **kwargs) only for three attributes, and then hard codes another 5 or 6 attributes in each of the remaining classes. It just doesn't seem very Pythonic to use the constructor if you're going to hard code the classes anyways.

I might be missing something. I need to re-watch with fresh eyes.

I think at that point its more functionality based. If you are creating a game you need some randomization, without it the difficulty decreases. I haven't done this course in a while, but later on today I will go through the videos to see if I can get a better understanding of your confusion.