Python Class Tutorial Example
In this tutorial, we will create a Shape class as an example which has a “name” property that is initialized by the init() method…
class Shape():
def __init__(self, name):
self.name = name
This means that we can instantiate the class by …
widget = Shape("Triangle")
Here we gave the name as “Triangle”, which is why when we print …
print(widget.name)
We get output of “Triangle”
Derive class in Python
Now we derive a new class called “Square” from our existing class “Shape” like this …
class Square(Shape):
def __init__(self, name, length):
super().__init__(name)
self.length = length
If we did not define the init method in the sub-class Square, it would have just used its parents init method. But here we are over-riding the init method in the sub-class to take in an extra parameter of length.
class Square(Shape):
def __init__(self, name, length):
super().__init__(name)
self.length = length
Note how in our init, we call our its parents init method by super().
We create an instance of that sub-class by giving it a name and length…
widget = Square("Mister Box", 5)
print(widget.name) # Mister Box
print(widget.length) # 5
We gave our sub-class the name “Mister Box” and it has a length property that our Shape class did not.
repr method
By adding a repr method to the class …
class Square(Shape):
def __init__(self, name, length):
super().__init__(name)
self.length = length
def __repr__(self):
return f"name = {self.name}\nlength = {self.length}"
we affect how the class is printed…
print(widget)
now gives …
name = Mister Box
length = 5
Class Properties
Now let’s add a property called “color” to our Square class…
class Square(Shape):
def __init__(self, name, length):
super().__init__(name)
self.length = length
self.color = ""
def __repr__(self):
return f"name = {self.name}\nlength = {self.length}\ncolor = {self.color}"
def get_color(self):
return self.color
def set_color(self, color):
print("setting color")
self.color = color
We added initialization of empty string for color in the init and added display of color in repr as well. We added print statement in set_color to show that this method is being called. We can get and set the color like this …
widget = Square("Mister Box", 5)
widget.set_color("Blue")
print(widget.get_color())
And when run we see that set_color method had been called. The method need not start with get_. We could have easily have called it “assign_color”.
The problem with this is that a developer can bypass the use of the set_color method by …
widget = Square("Mister Box", 5)
widget.color = "Blue"
print(widget.get_color())
Now color is still set to “Blue”, but the method “set_color” method was NOT called.
Name mangling in Pyton
We want to enforce it so in order to set the color property, the set_color method must be run. One way is to name our property with double-underscore like this…
class Square(Shape):
def __init__(self, name, length):
super().__init__(name)
self.length = length
self.__color = ""
def __repr__(self):
return f"name = {self.name}\nlength = {self.length}\ncolor = {self.__color}"
def get_color(self):
return self.__color
def set_color(self, color):
print("setting color")
self.__color = color
We properly set the color like this …
widget = Square("Mister Box", 5)
widget.set_color("Blue")
print(widget)
# output:
# setting color
# name = Mister Box
# length = 5
# color = Blue
If we try to short-cut by setting the color property directly this …
widget = Square("Mister Box", 5)
widget.__color = "Blue"
print(widget)
# output:
# name = Mister Box
# length = 5
# color =
We see that the color is not set. This is because when property name are preceded with double underscore, it indicates to developer not to set the property directly. Use a class method instead. Python tries to enforce this by “name mangling” the property name. So that internally, color is not referred to as __color. Instead it is referred to as “_Square__color” (You see this property when you print widget.__dir__())
This means that if a developer knows this, they can still directly set the property like this….
widget = Square("Mister Box", 5)
widget._Square__color = "Blue"
print(widget)
# output:
# name = Mister Box
# length = 5
# color = Blue
But they are not supposed to. Now you see that Python does not have strict “private” methods like other languages.
Make sure you use double underscore to precede the property name. If you use single underscore, Python will not name-mangle. Single underscore is a “weaker” indication of private use and is mostly prior convention. Except there is a case where Python does not import single underscored methods when doing an “import *”
Best way to manage attributes
This leads up to the conclusion of how to best create the color attribute. First use the double-underscore. Second use “managed attributes” like this…
class Square(Shape):
def __init__(self, name, length):
super().__init__(name)
self.length = length
self.__color = ""
def __repr__(self):
return f"name = {self.name}\nlength = {self.length}\ncolor = {self.__color}"
@property
def color(self):
return self.__color
@color.setter
def color(self, color):
print("setting color")
self.__color = color
widget = Square("Mister Box", 5)
widget.color = "Green"
print(widget.color)
print(widget)
# output:
# setting color
# Green
# name = Mister Box
# length = 5
# color = Green
Note the use of the @property and the @*.setter decorators. With these, you can set color and get color naturally like …
widget.color = "Green"
print(widget.color)
And it will in fact call the color method as evidence by the printing of the line “setting color”
Property dervied from attributes
You can even add a property like “area” such as …
@property
def area(self):
return self.length ** 2
Static Methods in Python
Getting back to the Shape class, we can add a static method using the @staticmethod decorator…
class Shape():
def __init__(self, name):
self.name = name
@staticmethod
def say_something():
print("Hello from Shape")
We can invoke this method without instantiating a class…
Shape.say_something()
And note that the say_something() method does not take a “self” parameter.
Class Methods in Python
This is different from class method which is defined with the @classmethod and which take a “cls” parameter…
class Shape():
def __init__(self, name):
self.name = name
@staticmethod
def say_something():
print("Hello from Shape")
@classmethod
def say_hello(cls):
print(f"Hello from class instance from {cls.__name__}")
It is meant to be invoked by instances of the class, so we create a class instance and call it…
widget = Shape("Triangle")
widget.say_hello()
# output
# Hello from class instance named Shape
Class Variables
Next we create an class variable “count” initialized to zero …
class Shape():
count = 0
def __init__(self, name):
self.name = name
Shape.count += 1
@staticmethod
def say_something():
print("Hello from Shape")
@classmethod
def num_shapes(cls):
print(f"{cls.count} shapes has been created")
In the init method, we increment class variable count everytime a new instance of Shape is created. We also added an classmethod that prints out the value of this count variable.
When we create two instances (one instance of Shape, and one instance of Square)
widget = Shape("Triangle")
myBox = Square("Mister Box", 5)
Shape.num_shapes()
We get output saying …
2 shapes has been created
Note that we could have called num_shapes as …
widget.num_shapes()
or
myBox.num_shapes()
and it would have worked the same.