[Python] Class
Class
Class is a concept introduced with object-oriented programming. Class is a concept to bundle data and functionalities to handle the data. After defining a class, we can create instances of that class. We can create multiple instances from a class and after creation those instances can be handled as unique entity which is independent from one another.
Suppose we come to have an eager to develop our own Pokemon game. Let’s try defining our first custom class Pikachu
.
Defining Class
1
2
3
4
5
6
class Pikachu:
def __init__(self):
self.level = 1
self.hit_point = 100
self.atk = 20
self.type = 'electric'
I wrote Pikachu
class as above. There are 4 attributes: Level (level
), HP (hit_point
), Attack (atk
) and Type (type
). This is simple but we can add more attributes if we want to implement more intricate system.
This Pikachu
class is a blueprint to create Pikachu objects
or instances
which we will handle hereafter. Before reviewing in detail, let’s dive in and create a Pikachu.
1
>>> a = Pikachu()
Variable a
was assigned to a Pikachu object or instance
. According to the blueprint, we know a
should have all 4 attributes we intended. We can access these values as follows:
1
2
3
4
5
6
7
8
>>> print(a.level)
1
>>> print(a.hit_point)
100
>>> print(a.atk)
20
>>> print(a.type)
electric
These 4 variables are dependent to instance a
and so they are called instance variables of instance a
. You can call them as object variables also.
Class, Object, Instance
Before detailing above code defining Pikachu
class, let’s clarify terminology. Rather than give dry definitions for each each concept, let me give you some descriptions which you will encounter frequently based on our Pikachu
class example.
We did:
- Thought Pikachu
object
which would be part of our custom pokemon game. - To create Pikachu
object
definedPikachu
class
. - Using
Pikachu
class, createdinstance
a
which will navigate and engage in battle in our pokemon world.
General explanation to distinguish Object
, Class
and Instance
is as follows:
- Object is what you want to realize in software world.
- Class is a design sheet to create Object.
- Instance is an embodiment of object, created using class, some physical memory actually assigned for.
So object
is the most abstract one and instance
is the most concrete one. But in everyday use distinction between those two are frequently vague. For example, even though in strict sense a
is instance, we also state like “Let’s think about how our object a
works here”. In such context, object
is just a synonym for instance
or we can think there is a realation like instance
$\subset$object
However, in some context mixed use of the terms is obviously weird. For example, when we talk about the Pikachu
class I wrote above, statement like “How can I modify this instance
to solve the issue?” is inappropriate, because the implementation of Pikachu
is about the design but about any physically embodied entity from it.
And there could be more complicated situation. Let’s consider I am a member of a large team developing pokemon game, but my team is a subset of larger business and our team is working only for Windows-based pokemon games. And within our team the class in use to handle Pikachu is implemented in the name of PikachuWindows
. But from different section of our business, members of which are working on pokemon game for Gameboy console, suppose they are using different class PikachuGameboy
.
Now I hear evaesdropped during lunch that it was decided that we will modify Pikachu class, to render our Pikachu to have grass type than original electric type. Suppose that during afternoon the conversation stuck with me and at some point, without knowing myself doing that, I modified type
variable of PikachuWindows
from electric to grass. And on the next day I hear that the converation was between Gameboy dev team and the change was to be applied solely for PikachuGameboy
as they were working on some April fool update…
Above example is just a joke but in such sense object
and class
also are also different each other. We had Pikachu
class only, but in many different context there could be one or more different implementations (class
) for the same object
.
Methods
Now let’s get back to our implementation of Pikachu
class.
1
2
3
4
5
6
class Pikachu:
def __init__(self):
self.level = 1
self.hit_point = 100
self.atk = 20
self.type = 'electric'
Method is a function which is dependently implemented under specific class. In the implementation above, one method named __init__
is already defined.
Constructor __init__
and self
Keyword
This basic method __init__
has special name, ‘constructor’. __init__
is an reserved method name for constructor, and this is called for the very first time when instance creation is attempted. When we created instance a
of Pikachu
class by a = Pikachu()
, __init__
was executed even though we did not explicitly called it.
Another reserved keyword self
is used to designate arbitrary instance of Pikachu
, which is not embodied inside our class. Once an instance like a
is created using the class, we can replace all those self
with a
to imagine how the methods and variables would work with created instance a
.
Now let’s see what happens when we execute a = Pikachu()
. Constructor is the very first method called, so __init__(self)
should be executed. But as we have instance named a
now, actually executed is __init__(a)
. However, as in python syntax we call a method by pull the instance name out of the parenthesis, add period(.), and then method name follows. So a.__init__()
is what we actually execute here.
Playing with Pikachu
1
2
3
4
5
6
7
8
class Pikachu:
def __init__(self):
self.level = 1
self.hit_point = 100
self.atk = 20
self.type = 'electric'
def get_damaged(self, damage):
self.hit_point -= damage
Now I wrote a method other than constructor, named get_damaged
. This method receives damage
as extra argument, to reduce hit_point
of a instance by the value passed to damage
.
Let’s see how this new method work. Remark how we call get_damaged
method as actual code: self
is replaced by instance name a
and pulled out of the parenthesis, leaving extra argument to pass damage value to be inflicted.
1
2
3
4
5
6
>>> a = Pikachu()
>>> print(a.hit_point) # a Check hitpoint of pikachu
100
>>> a.get_damaged(10) # Inflict damage of 10 to pikachu instance a
>>> print(a.hit_point) # Check whether the method worked as intended
90
Method Not Receiving self
as Argument: We can define method inside a class but make it work without self
as argument. Those cases will be briefed in later post in reviewing Static Method and Class Method.
Tackle!
Now let’s write another method attack
.
1
2
3
4
5
6
7
8
9
10
class Pikachu:
def __init__(self):
self.level = 1
self.hit_point = 100
self.atk = 20
self.type = 'electric'
def get_damaged(self, damage):
self.hit_point -= damage
def attack(self, other_pikachu):
other_pikachu.get_damaged(self.atk)
attack
method was written to receive another pikachu instance (other_pikachu
) as instance. If there are two pikachu a
and b
and a
pikachu calls its attack
mehtod and pikachu b
is passed as argument in that call, it would describe a pokemon battle situation - a pikachu attack and inflicting damage to other pikachu.
Let’s check - pikachu, tackle!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> a = Pikachu() # a is an instance of Pikachu
>>> b = Pikachu() # b is also an instance of Pikachu
# Check hitpoint
>>> print(a.hit_point)
100
>>> print(b.hit_point)
100
# a tackles!
>>> a.attack(b)
# Let's check HP.
>>> print(a.hit_point)
100
>>> print(b.hit_point)
80
# It is not forbidden for a pikachu to attack itself...
>>> a.attack(a) # a was confused to attack itself!
# It is a close battle now.
>>> print(a.hit_point)
80
>>> print(b.hit_point)
80
Get Back!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Pikachu:
def __init__(self):
self.level = 1
self.hit_point = 100
self.atk = 20
self.type = 'electric'
self.is_in_monsterball = false
def get_damaged(self, damage):
self.hit_point -= damage
def attack(self, other_pikachu):
other_pikachu.get_damaged(self.atk)
def level_up(self):
self.level += 1
self.hit_point += 10
self.atk += 1
print(f"Pikachu turned to level {self.level}!")
print(f"Pikachu's HP: {self.hit_point}")
print(f"Pikachu's ATK: {self.atk}")
def retrieve(self):
print("Pikachu, get back!")
self.is_in_monsterball = true
print("Pikachu got back to the monster ball!")
I tried some more thing with Pikachu
class. level_up
method was added to increase pikachu’s level, and new instance variable is_in_monsterball
was added and set boolean to case-handling based on if a pikachu is in mosterball(true
) or not(false
)
Let’s do some check to see all implementation is fine.
1
2
3
4
5
6
7
8
>>> my_pikachu = Pikachu()
>>> my_pikachu.level_up()
Pikachu turned to level 2!
Pikachu's HP: 120
Pikachu's ATK: 21
>>> my_pikachu.retrieve()
Pikachu, get back!
Pikachu got back to the monster ball!