Happy 1st of September!
I have decided to write a letter to you and share some thoughts and gratitude for your visits.
I recently walked into my favorite park and saw beautiful white pigeons picking some worms. They were mingling without any concern with other “usual” pigeons. They looked so different but were also quite indifferent to their differences from each other. At the same time, they were pigeons who did not care about feather color differences. They all enjoyed green grass and little worms in it.
Birds are so beautiful, all of them. And I have decided to do a simple wrap-up of simple Python classes defining birds and pigeons. I think that this post is a good recap or start for understanding Object-Oriented Programming and the available functionality in Python.
Table of Contents
- Object-Oriented Programming
- Classes in Python
- Class Methods
- Inheritance
- Polymorphism
- Encapsulation
- Conclusion
- References
Object-Oriented Programming
In CS, there are few programming paradigms widely known and discussed. I am not keen to go into discussions, and Writing code is the best way to understand programming paradigms and their meaning. In this post, I am going to focus on Object-Oriented Programming. As we see from the name, we are dealing with objects. And we can describe any real-life or abstract object with the help of classes.
For instance, birds. We can define a class for birds and also define a class for pigeons. Both classes will be similar, right? And with Python and OOP we can describe birds and pigeons very neatly and create many instances of both classes, thus creating many bird objects defined by both classes!
Classes in Python
In Python, we define a class with the respective word. A class is initialised as follows. Our simple bird class has just two class attributes such as bird_type_name and the birds’ color. The init method is a special method to construct (or create) birds.
class Bird:
def __init__(self, bird_type_name, color):
self.bird_type_name = bird_type_name
self.color = color
After defining the “Bird” class, we can create an instance of a bird with needed attributes. We can create a few bird objects, two white and one black. Notice that we have used the list comprehensions to create a list of pigeons, which are instances of our “Bird” class. This way, we can create so many different colored pigeons!
bird_type_name = "Pigeon"
bird_colors = ["white", "black", "white"]
pigeons = [Bird(bird_type_name=bird_type_name, color=color) for color in bird_colors]
print(pigeons)
[<__main__.Bird instance at 0x10b268e18>, <__main__.Bird instance at 0x10b2fa440>, <__main__.Bird instance at 0x10b2fa518>]
All three bird instances are located in different places in the computer memory and are unique objects.
The bird attributes can be accessed this way.
print(f"Our first bird is a {pigeons[0].color} {pigeons[0].bird_type_name}.")
Our first bird is a white Pigeon.
As in real life, our created birds are different too!
print(pigeons[0]==pigeons[1])
False
Interestingly, we can change the class attributes quite easily! Imagine our first white pigeon becomes a blue pigeon!
pigeon = pigeons[0]
pigeon.color = "blue"
pigeon.color
'blue'
Class Methods
Class methods are “functions” that our objects can do. Most of the pigeons can fly. Thus, we will create the fly() method. We do not want to make too speedy pigeons. We set the limits of pigeon fly speeds and the maximum distances to make the pigeons as realistic as possible. I have defined the max_fly_speed and max_distance class attributes after consulting the page “How Fast do Pigeons Fly?.”
class Bird:
max_fly_speed = 150 # km/hour
max_distance = 676 # in kilometers
# Class constructor
def __init__(self, bird_type_name, color):
self.bird_type_name = bird_type_name
self.color = color
# Class method to get bird types
def bird_type(self):
return self.bird_type_name
# Class method to fly birds
def fly(self, velocity, distance):
if velocity > self.max_fly_speed:
print(f"Sorry, our {self.bird_type_name} does not want to fly with the speed over {self.max_fly_speed} (km/h).")
return False
if distance > self.max_distance:
print(f"Sorry, our{self.bird_type_name} does not want to fly further than {self.max_distance} (km). They get tired.")
return False
print(f"The {self.color} {self.bird_type_name} \
flies at the speed of {velocity} (km/h)\
to a distance of {distance} (km). Done!")
return True
Notice the “self” keyword. It helps in accessing the class methods and defined class-wide attributes. For instance, the bird_type() method returns self.bird_type_name variable initialised in the class constructor.
pigeon = Bird(bird_type_name="Pigeon", color="white")
pigeon.bird_type()
'Pigeon'
Let’s try to make the pigeon fly to the 800 km distance! It can’t! It should be a different bird, for instance, an albatross!
pigeon = Bird(bird_type_name="pigeon", color="white")
pigeon.fly
Sorry, our Pigeon does not want to fly further than 676 (km). They get tired. False
Lazy pigeon! Ok, it will exercise with flying to 100km!
pigeon.fly
The white Pigeon flies at the speed of 55 (km/h) to a distance of 100 (km). Done! True
Inheritance
The are so many different types of birds with their features. Some birds, such as pigeons, can fly, while others, like penguins, cannot fly but swim very well. Indeed, we can hard-encode all these differences inside of the fly() method, and with the check of the Bird class attribute ‘bird_type_name’, we can alter the functionality. Luckily, there is a more elegant way of creating classes in OOP.
Inheritance allows us to inherit the properties of a parent class. We can make our Bird class a parent class, reusable in its children’s classes. We can also add more features to the children’s class without modifying the parent class. For instance, let’s create a class, Penguin, which will inherit the functionality of the Bird class, while introducing a new method swim().
class Penguin(Bird):
bird_type_name = "Penguin"
color="grey"
def __init__(self):
super().__init__(bird_type_name=self.bird_type_name,
color=self.color)
def swim(self):
print(f"A {self.bird_type_name} can swim very well.")
return True
Notice that we redefined our “init” constructor method. Bu calling the parent class’ method “init” with super(). and the defined attributes “bird_type_name” and “color.” Otherwise, the Python interpreter will complain and demand that we define the bird type and color when creating a penguin instance of the class. Now, we can create a penguin object.
new_bird = Penguin()
The “color” attribute is not defined in the class “Penguin,” yet, it is available since it is inherited from the class “Bird.” We can also change it anytime a penguin age and changes its plumage.
new_bird.color = "grey"
new_bird.color = "black and white"
We can also use the parent class method bird_type() from the child class, not realising it.
new_bird.bird_type()
'Penguin'
Polymorphism
It is pretty annoying that our implementation of the Penguin class allows penguins to fly! And should we call the fly() method, we get a wrong output. That happens because we inherit the fly() method from the parent class.
new_bird.fly()
The grey Penguin flies at the speed of 55 (km/h) to a distance of 100 (km). Done! True
Fortunately, the polymorphism concept is about modifying the inherited method in the child class. When re-implementing the child method, we “override” it with new functionality. In this final implementation of the Penguin class, we change the fly() method, returning False and printing the message that penguins do not fly. Notice that we can also redefine fly() without arguments “velocity” and “distance.”
class Penguin(Bird):
bird_type_name = "Penguin"
color="grey"
def __init__(self):
super().__init__(bird_type_name=self.bird_type_name,
color=self.color)
def fly(self, velocity=0, distance=0):
print(f"A {self.bird_type_name} cannot fly.")
return False
def swim(self):
print(f"A {self.bird_type_name} can swim very well.")
return True
new_bird = Penguin()
new_bird.fly()
A Penguin cannot fly. False
Encapsulation
Providing access to all class methods and variables is not always a good idea. Sometimes we deal with sensitive information that should be protected. Since Python is a bit different from Java and does not have access modifiers, we can use a different approach to controlling access to the class data and methods.
For instance, we want to make Bird class variables max_distance and max_fly_speed as private. There are several ways of implementing it.
The first option is to preceed the variable names with an underscore, as pythonists say “creating a protected variable”.
class Bird:
_max_fly_speed = 150 # km/hour
_max_distance = 676 # in kilometers
# Class constructor
def __init__(self, bird_type_name, color):
self.bird_type_name = bird_type_name
self.color = color
# Class method to get bird types
def bird_type(self):
return self.bird_type_name
# Class method to fly birds
def fly(self, velocity, distance):
if velocity > self._max_fly_speed:
print(f"Sorry, our {self.bird_type_name} does not want to fly with the speed over {self._max_fly_speed} (km/h).")
return False
if distance > self._max_distance:
print(f"Sorry, our{self.bird_type_name} does not want to fly further than {self._max_distance} (km). They get tired.")
return False
print(f"The {self.color} {self.bird_type_name} \
flies at the speed of {velocity} (km/h)\
to a distance of {distance} (km). Done!")
return True
Unfortunately, we can still easily access the content of variables (_max_distance). This is a mere convention for coders that this variable is protected and should not be used outside the class.
bird = Bird('Pigeon', color="grey")
bird._max_distance
676
Another way is to use two underscores, which really works to hide the variable. And the rest of the code works as intended. This variable can be referred as “private”.
class Bird:
__max_fly_speed = 150 # km/hour
__max_distance = 676 # in kilometers
# Class constructor
def __init__(self, bird_type_name, color):
self.bird_type_name = bird_type_name
self.color = color
# Class method to get bird types
def bird_type(self):
return self.bird_type_name
# Class method to fly birds
def fly(self, velocity, distance):
if velocity > self.__max_fly_speed:
print(f"Sorry, our {self.bird_type_name} does not want to fly with the speed over {self.__max_fly_speed} (km/h).")
return False
if distance > self.__max_distance:
print(f"Sorry, our{self.bird_type_name} does not want to fly further than {self.__max_distance} (km). They get tired.")
return False
print(f"The {self.color} {self.bird_type_name} \
flies at the speed of {velocity} (km/h)\
to a distance of {distance} (km). Done!")
return True
bird = Bird('Pigeon', color="grey")
bird.__max_distance
AttributeError: Bird object has no attribute __max_distance
Further, we can create getter and setter methods to define these hidden variables.
class Bird:
__max_fly_speed = 150 # km/hour
__max_distance = 676 # in kilometers
# Class constructor
def __init__(self, bird_type_name, color):
self.bird_type_name = bird_type_name
self.color = color
# __max_fly_speed setter
def set_max_fly_speed(self, max_fly_speed):
self.__max_fly_speed = max_fly_speed
# __max_fly_speed getter
def get_max_fly_speed(self):
return self.__max_fly_speed
# Class method to get bird types
def bird_type(self):
return self.bird_type_name
# Class method to fly birds
def fly(self, velocity, distance):
if velocity > self.__max_fly_speed:
print(f"Sorry, our {self.bird_type_name} does not want to fly with the speed over {self.__max_fly_speed} (km/h).")
return False
if distance > self.__max_distance:
print(f"Sorry, our{self.bird_type_name} does not want to fly further than {self.__max_distance} (km). They get tired.")
return False
print(f"The {self.color} {self.bird_type_name} \
flies at the speed of {velocity} (km/h)\
to a distance of {distance} (km). Done!")
return True
bird = Bird('Pigeon', color="grey")
bird.get_max_fly_speed()
150
Now we can change the private variable, pigeons’ maximum fly speed, and make a flying champion!
bird.set_max_fly_speed(200)
bird.get_max_fly_speed()
200
Wait, but where is my so-promised Pigeon class? Similarly, with the Pinguin class, we inherit the functionality from the Bird class. I have created my pigeons’ default color to be white as a sign of Peace, but you can make your own choice!
class Pigeon(Bird):
bird_type_name = "Pigeon"
color="white"
def __init__(self, color="white"):
self.color = color
super().__init__(bird_type_name=self.bird_type_name,
color=self.color)
white_pigeon = Pigeon()
white_pigeon.fly()
Conclusion
Birds are free to fly, love their families, and do not care about the feather color of other birds. Many birds mate for life, raise their babies, and enjoy the sunshine on this beautiful Earth. I wish people would be more like birds, free, loving, tolerant of each other, and happy!
Openness and willingness to understand each other are essential in the World tormented by the ongoing history of suffering, which never ends due to greed and stupidity. I always focus my life on family, science, and coding. What is right and what is wrong? I think that we are all having the feeling of a better World. And, it will be my greatest joy to see a change for humanity, for the best.
We all deserve to be happy in this beautiful World, full of kind, intelligent, and gorgeous people. We are all part of Nature and the Universe, which moves so quickly, with all the stars born and dying as time moves. Nothing matters except humanity, freedom, love, Nature, and birds!
That’s all on birds and OOP in Python! I hope that this post was helpful for the familiarity of classes and what we can do with them in Python. In short, we made the “Bird” class that forms a “blueprint” describing particular bird objects. We made a few pigeons as instances of the “Bird” class and made them fly within considerable distances and speed limits. We also created a Penguin, inherited from the Bird class. We observed how class inheritance, polymorphism, and encapsulation work!
p.s. I want to thank my Twitter friend and an excellent ML coder Daniel Torac for his constructive comments on this article, particularly on protected and private variables considering the encapsulation in Python. Thank you very much for reading and for much inspiration!
References
Did you like this post? Please let me know if you have any comments or suggestions.
Python posts that might be interesting for youAbout Elena Elena, a PhD in Computer Science, simplifies AI concepts and helps you use machine learning.
|