Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python

object-oriented python

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

hi, im confused to what this line of code does. it belongs to this larger part of code

import random


COLORS = ["red","blue","yellow", "purple","green"]


class Monster:
  min_hit = 1
  max_hit = 100
  min_exp = 1
  max_exp = 100
  weapon = "sword"
  sound = "roar"

  def __init__(self, **kwargs):
      self.hit_points = random.randint(self.min_hit,self.max_hit)
      self.exp = random.randint(self.min_exp,self.max_exp)
      self.color =  random.choice(COLORS)

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

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

i get that it adds any new parameter that we pass into the Monster instance as an attribute . but can anyone just explain like the logic behind it??

we are iterating over the first value(key) and second value(value) inside of kwargs? and the .items() method makes it so its a dictionary with a key and value pair? idk if that sounds right or not.

also another question. whenever i type "self" am i saying that i am passing an instance of the Monster class??

2 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,468 Points

Given the Monster() class in your post, it has several class variables defined for hit pints, experience, weapon and sound. When you instantiate a new monster like dragon = Monster() it will create a new instance of Monster, then call the class __init__() method. The first argument all class methods is self to represent the particular instance being initialized or reference during the call. When the __init__() method is finished the new instance is assigned to dragon.

During the __init__() execution, all of the class variables (and any new variables created during init) are referenced as attributes of this same self. By adding the argument **kwargs, __init__() can accept any keyword arguments.

The code snippet in question handles any extraneous arguments passed it. The ** says that kwargs will be expandable into a dictionary of key-value pairs. items() returns one tuple of a key and value at a time which are assigned to key and value. The function setattr() will effectively be translated to self.key = value. setattr() is used because key is a string value so self.some_string = value would not work. setattr() creates the new attribute in the self attribute namespace.

Using the code snippet allows you to add extra attributes to your new monster or even overwrite existing attributes. Lets say you want your dragon to be named "Puff" and be invincible with 1000 hit points. You would change your dragon statement to:

dragon = Monster(name="Puff", hit_points=1000)

One initialized, dragon has all of the attributes of the class Monster including the __init__() method. While typically not done in practice, you could reinitialize your dragon at any time using:

dragon.__init__(name="Smaug", hit_points=1000000)

very well explained! ^

okay so I'm by no means a pro yet, but this is the best explanation i can give you:

as for your question on what that line of code does, im pretty sure its saying for every KEY and VALUE within kwargs (kwargs being within the init you just called a few lines up) it is going to set the attributes of the monster based on those values WITHIN that instance itself - that's where "self" comes into play. "self" basically calls/sets attributes for that specific instance. the .items() is basically gives the method tuples in the format of a KEY, VALUE.

so basically:

for key, value in kwargs.items():

--- for every key and value inside kwargs (which now has its attributes in a key, value format thanks to .items()) do this:

setattr(self, key, value)

--- this is basically saying set the attributes as per K,V but do it non-directly.. (because its inside the init) its kinda the same as [ self.name = 'VALUE' ]

i hope it helped, and if it was hard to piece together I apologize, i've never been one for Forums :)