class Monster: 
  hit_points = 1
  sound = 'roar'
  ...

  def battlecry(self):
    return self.sound.upper()
>>> from monster import Monster
>>> jubjub = Monster() 
>>> jubjub.hit_points = 5
>>> jubjub.hit_points
5
>>> jubjub.sound = 'tweet'
>>> jubjub.battlecry()
'TWEET'
>>> type(jubjub)
<class 'monster.Monster'>
#Settings attributes approach #1
class Monster:
  def __init__(self, hit_points, weapon, color, sound):
    self.hit_points = hit_points
#Settings attributes approach #2
class Monster:
  def __init__(self, hit_points=5, weapon='sword', color='yellow', sound='roar'):
    self.hit_points = hit_points
#Settings attributes approach #3, the best way!
class Monster:
  def __init__(self, **kwargs):
    self.hit_points = kwargs.get('hit_points', 1)
    ...

>>> from monster import Monster
>>> jubjub = Monster(color='red', sound='tweet')
#Choosing from a constant list
COLORS =  ['yellow', 'red', 'blue', 'green'] 
color = random.choice(COLORS)
#Setting new attributes or existing ones
for key, value in kwargs.items():
  setattr(self, key, value)
#Inheritance without change
class Goblin(Monster):
  pass
#Inheritance with change
class Troll(Monster):
  min_hit_points = 3 
  sound = 'growl'
  ... 
class Dragon(Monster): 
  min_hit_points = 5
  ...
>>> from monster import Monster
>>> snaga = Troll()

>>> import monster
>>> snaga = monster.Troll()
#__str__()
>>> draco = Dragon()
>>> print(draco)
<monster.Dragon object at 0x7f351cd3518a>
#Get class name
def __str__(self):
  return self.__class__.__name__
#DRY coding example
return roll > 4
#Multiple Inheritance
class Goblin(Monster, Combat):
...
#List of instances
monsters = [ Goblin(), Dragon(), Troll() ]
return monsters.pop(0)
#Overriding Inheritance
class Combat:  
  def attack(self):
    roll = random.randint(1, self.attack_limit)
    return roll > 4
    ...

class Character(Combat):
  def attack(self):
    if self.weapon = 'sword':
      roll += 1
    else
    ...
import sys
sys.exit()