Understanding Classes in Python: From Templates to Objects

Noran Saber Abdelfattah
20 min readJun 28, 2023

--

Introduction

Python class idea is presented in this article. In order to enable the generation of new instances of a type, a class is defined as a template that groups data and functions together. Each instance of a class may include methods for changing its state as well as properties for preserving it. According to the article, a class may be compared to a template or blueprint where properties specify the precise structure of an object and when it is constructed, it adopts that shape. To demonstrate the idea of Python classes, the article offers an example of a Dog class.

Classes

Classes provide a means of bundling data and functionality together. Creating a new class creates a new type of object, allowing new instances of that type to be made. Each class instance can have attributes attached to it for maintaining its state. Class instances can also have methods (defined by its class) for modifying its state.

This is the definition of class in Python documentation. Let’s take it simply.

So we can say the class is a template that has a specific shape (attributes) and when we want to create a model from that template we just take the attribute and give it a value and when we build our model we build our object

  • We can think about a class like this template, this template has its shape (attribute) which will make the product (object) take a specific shape.
  • The product is called an object. I guess I made you hungry 😂😂

Let’s take a real example

  • Note: this is not the syntax I'm just trying to simplify the concept

Creating and Using the Class

Classes may be used to model practically anything. Let’s start by creating a straightforward class called Dog to represent dogs in general rather than just one specific dog.

What do we know about the majority of dogs kept as pets? They all have names and ages, so. We are also aware that the majority of dogs sit and roll over.

Due to the fact that the two characteristics (name and age) and the two actions (sit and roll over) are typical of most dogs, they will be included in our Dog class.

This class will instruct Python on how to create a dog-themed object. We’ll utilise our class after it has been created to create various instances, each of which will represent a particular dog.

Each instance created from the Dog class will store a name and an age, andwe’ll give each dog the ability to sit() and roll_over():

class Dog():
"""A simple attempt to model a dog."""
def __init__(self, name, age):
"""Initialize name and age attributes."""
self.name = name
self.age = age

def sit(self):
"""Simulate a dog sitting in response to a command."""
print(self.name.title() + " is now sitting.")

def roll_over(self):
"""Simulate rolling over in response to a command."""
print(self.name.title() + " rolled over!")

  • We establish the Dog class. Python class names are traditionally referred to by their capitalization.
  • Since we are constructing this class from scratch, the brackets in the class declaration are empty.
  • We create a docstring at v that explains what this class performs.

The __init__() Method

  • If we have a function and this function is a part of a class we called it method
  • whenever a new instance of the Dog class is created. This function uses a convention methong init that uses two starting underscores and two trailing underscores to avoid conflicts with Python’s default method names.
  • We specify that the three arguments for the __init__() method are self, name, and age.
  • The self parameter must occur first in the method declaration before any other parameters since it is necessary.
  • It is necessary to mention it in the definition since Python will automatically give the self argument when using this __init__() function later (to create an instance of Dog).
  • Self, a reference to the instance itself, is automatically sent by every method call connected to a class, granting the specific instance access to the properties and methods in the class.
  • Simply you can think about self as a card to sport club, so if you have the card you can access everything in the club (attribute, method), but if you don’t have the card you can’t
  • You give the dog a name and an age when you utilise the blueprint to make a real dog.
  • In Python, we build unique functions called methods inside of a class that we create. One of these procedures is referred to as __init__(), and it functions much like a set of guidelines for breeding a new dog.
  • Three specific items are mentioned in the __init__() method’s instructions: self, name, and age. “This dog” or “the dog we’re creating” are the meanings of the unique word “self.” Python automatically provides it to us when we use the blueprint to build a dog.
  • Here, self.name = name, self.age = age you can think about it like you are saying my dog name is equal the value inside the variable name, and my dog age is the value inside the variable age
  • The instructions for the __init__() function include three particular items: self, name, and age. The singular word “self” has two meanings: “this dog” or “the dog we’re creating.” When we utilise the blueprint to construct a dog, Python automatically gives it to us.
  • the two more declared methods, sit() and roll_over() These methods are defined to include a parameter called self even if they don’t require extra data like a name or age.
  • These methods will be available to the instances we construct later. They’ll be able to turn over and sit. Roll_over() and sit() don’t do anything right now.
  • The dog is simply described as sitting or rolling over in a message that is printed. However, the idea may be applied to more real-world scenarios: if this class were a component of a genuine computer game, these methods would have code that would cause an animated dog to sit and turn over.
  • These methods would control actions that make a dog robot sit and roll over if this class were designed to control a robot.

Making an Instance from a Class

Consider a class as a collection of guidelines for creating an instance. Python is instructed on how to create unique instances that represent certain canines by the class Dog. Let’s create an example that represents a certain dog:

my_dog = Dog('willie', 6)

print("My dog's name is " + my_dog.name.title() + ".")
w print("My dog is " + str(my_dog.age) + " years old.")

We instruct Python to make a six-year-old dog with the name “Willie.” Python executes the __init__() function in Dog with the inputs “willie” and “6” after reading this line.

The name and age properties are set using the information we supplied in the __init__() function, which also creates an instance representing this specific dog.

Python automatically produces an instance that represents this dog even when the __init__() function lacks a return statement explicitly.

That instance is kept in the variable “my_dog.” We can normally presume that a capitalised name, such as Dog, refers to a class, and a lowercase name, such as my_dog, refers to a single instance made from a class. This is where the naming convention is useful.

Accessing Attributes

To access the attributes of an instance, you use dot notation. At v we access the value of my_dog’s attribute name by writing:

my_dog.name

‘willie’, the value of the name property on my_dog, is made to begin with a capital letter using the function my_dog.name.title().

The value of the age property for my_dog, 6, is converted to a string in the second print statement by the function str(my_dog.age).

The result is an overview of the information we have on my_dog:

Calling Methods

Any method specified in the Dog class can be called using dot notation after an instance of the class has been created. Let’s force our dog to roll over and sit:

class Dog():
--code--

my_dog.sit() #object.method
my_dog.roll_over()#object.method

When calling a method, separate the name of the instance (in this case, my_dog) from the method name with a dot.

Python searches the class Dog for the function sit() and executes the code when it reads my_dog.sit().

The line my_dog.roll_over() has the same meaning in Python. Willie now complies with our instructions:

Creating Multiple Instances

You are able to create as many instances from a class as you need. Let’s create second dog called your_dog:

my_dog = Dog('willie', 6)
your_dog = Dog('lucy', 3)
print("My dog's name is " + my_dog.name.title() + ".")
print("My dog is " + str(my_dog.age) + " years old.")
my_dog.sit()
print("\nYour dog's name is " + your_dog.name.title() + ".")
print("Your dog is " + str(your_dog.age) + " years old.")
your_dog.sit()

In this illustration, we make the dogs Willie and Lucy. Each dog is a distinct instance with its own set of characteristics and the same set of actions available to it:

Python would still construct a unique instance of the Dog class even if we gave the second dog the identical name and age. As long as you give each instance of a class a distinct variable name or place in a list or dictionary, you can create as many instances of that class as you need.

Try it yourself

The Car Class

Create a new class that represents an automobile. Our class will keep track of information regarding the type of automobile we’re dealing with and include a function that condenses it:

class Car():
"""A simple attempt to represent a car."""
def __init__(self, make, model, year)
"""Initialize attributes to describe a car"""
self.make = make
self. year = year
self. model= model

#Methog from the class
def get_descriptive_name(self):
"""Return a neatly formatted descriptive name."""
long_name = self.year + ' ' + slef.make + ' ' + self.model
return long_name.title()

#Creating object
my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())

We define the __init__() function with the self argument first, exactly like we did with our Dog class, at position u in the Car class. Three more factors are also provided: make, model, and year. The function __init__()

accepts these inputs and saves them in the properties that are linked to instances created from this class. We must choose a make, model, and year for our new Car instance when creating it.

We write a function at v called get_descriptive_name() that succinctly describes an automobile by combining its year, manufacturer, and model into one string. This will save us from having to print the value of each attribute separately. In this procedure, we utilise self.make, self.model, and self.year to manipulate the attribute values.

We create a Car class object and save it in the variable “my_new_car.” Next, we call get_descriptive_name() to display the type of vehicle we are driving:

To make the class more interesting, let’s add an attribute that changes over time. We’ll add an attribute that stores the car’s overall mileage.

Setting a Default Value for an Attribute

Even if the initial value of an attribute in a class is 0 or an empty string, it still has to have a value.

If you supply this initial value in the body of the __init__() method for an attribute, you don’t need to add a parameter for that attribute in some circumstances, such as when establishing a default value.

Add a property called odometer_reading that always has a value of 0. It should be added. A method called read_odometer() will also be added to allow us read the odometer of each vehicle:

Before that, just that’s mean you can use or add an attribute without putting this attribute between () in init method, just by defining it in the body of init method with value

class Car():
def __init__(self, make, model, year): #you didn't write it here
"""Initialize attributes to describe a car."""
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0 #but used it here with value

def get_descriptive_name(self):
--snip--

def read_odometer(self):
"""Print a statement showing the car's mileage."""
print("This car has " + str(self.odometer_reading) + " miles on it.")

my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()

The make, model, and year values are now stored as attributes when Python invokes the __init__() function to create a new instance, just like it did in the previous example. After that, Python generates a new property named “odometer_reading” and initialises it to “0 u”.

Additionally, a new function named read_odometer() at v makes it simple to read an automobile’s mileage. Our automobile has a starting mileage of 0 miles:

Modifying Attribute Values

There are three methods to alter the value of an attribute: directly through an instance, modify the value using a method, or increase the value (add a certain

  1. Modifying an Attribute’s Value Directly

Accessing an attribute directly through an instance is the easiest approach to change its value. Here, we immediately set the mileage to 23:

my_new_car.odometer_reading = 23
my_new_car.read_odometer()

We access the car’s odometer_reading property at u and set its value directly using dot notation. Python is instructed by this line to look for the attribute odometer_reading that is connected to the instance my_new_car, find it, and set its value to 23:

Sometimes you’ll want to build a method that updates the value for you, but other times you’ll want to write an access attribute directly like this.

2. Modifying an Attribute’s Value Through a Method

Having methods that change certain characteristics for you might be useful. You give the updated value to a method that takes care of updating internally rather than accessing the attribute directly. Here is an illustration of the update_odometer() function in action:

def Update_mileage(self, mileage):
"""Set the odometer reading to the given value """
self.mileage = mileage

my_new_car = Car('audi', 'a4', 2016)
print(my_new_car.get_descriptive_name())

v my_new_car.update_odometer(23)
my_new_car.read_odometer()

Update_odometer() is the sole enhancement that has been made to Car. This method reads the mileage value from the self.odometer_reading variable.

Update_odometer() is called at position v with the input 23 to the mileage parameter in the method description). read_odometer() outputs the reading after setting the odometer reading to 23.

Every time the odometer reading changes, we may enhance the function update_odometer() to perform more work. To ensure that nobody tries to change the odometer reading back, let’s add a little logic:

class Car():
--snip--
def update_odometer(self, mileage):
"""
Set the odometer reading to the given value.
Reject the change if it attempts to roll the odometer back.
"""
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")

The mileage argument, which indicates the new mileage you wish to set on the odometer, is required by the update_odometer() function.

The method determines whether the new mileage (mileage) exceeds or equals the previous mileage (self.odometer_reading).

The odometer has been updated appropriately if the new mileage is more or equal to the old number. In this instance, the method calls self.odometer_reading = mileage to update the odometer reading to the current miles.

The odometer is being attempted to be reset if the new distance is less than the reading at the moment. In this instance, the approach indicates that rolling back the odometer is prohibited by displaying the message: “You can’t roll back an odometer!”

3. Incrementing an Attribute’s Value Through a Method

Instead of setting a brand-new value for an attribute, you may occasionally wish to increase its value by a specific amount. Let’s imagine we purchase a secondhand automobile and drive it 100 miles before registering it. Here is a technique that enables us to add that value to the odometer reading and pass this incremental amount:4

def increament_dodmeter(self, miles):
"""Add the given amount to the odometer reading """
self.odometer_reading += miles

my_used_car = Car('subaru', 'outback', 2013)
print(my_used_car.get_descriptive_name())
my_used_car.update_odometer(23500)
my_used_car.read_odometer()
my_used_car.increment_odometer(100)
my_used_car.read_odometer()

A number of miles are entered into the new function increment_odometer() at u, and this value is then added to self.odometer_reading.

We design a used automobile called my_used_car. We use update_odometer() and pass 23500 at w to set the odometer to 23,500.

To add the 100 miles between when we bought the automobile and when we registered it, we use increment_odometer() at x and give it the value 100:

Exercise

Inheritance

When writing a class, you don’t necessarily have to start from scratch. You can utilise inheritance if the class you’re building is a customised version of another class you authored.

One class immediately acquires all the characteristics and methods of the first class when it inherits from another.

Both the new class and the previous class are referred to as children.

The child class is free to create additional attributes and methods in addition to inheriting all of the attributes and methods from the parent class.

The __init__() Method for a Child Class

Let’s model an electric vehicle as an illustration. Since an electric automobile is only one type of car among many, we may build our new ElectricCar class on the earlier-written automobile class.

Then, just the characteristics and behaviour unique to electric automobiles will require coding. Let’s begin by creating a basic ElectricCar class that performs all of the functions of the Car class:

class Car():
"""A simple attempt to represent a car."""
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0

def get_descriptive_name(self):
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title()

def read_odometer(self):
print("This car has " + str(self.odometer_reading) + " miles on it.")

def update_odometer(self, mileage):
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")

def increment_odometer(self, miles):
self.odometer_reading += miles
#Our Subclass
class ElectricCar(Car): #it takes the parent class as a param
"""Represent aspects of a car, specific to electric vehicles. """
def __init__(self, make, model, year, ):
"""Initialize attributes of the parent class """
#translation: from parent(super) init function bring (make, model, year)
super().__init__(model, year, make)

my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())

We begin with Car at. The parent class must be present in the current file and must come before the child class when you create a child class.

We define the ElectricCar child class. In the definition of the child class, the name of the parent class must be included in brackets.

The data needed to create a Car instance is sent to the w object’s __init__() function.

Super() is a unique method that facilitates connections between parent and child classes in Python.

This line instructs Python to invoke the parent class’s __init__() function, which provides an instance of ElectricCar with all of its parent class’s characteristics. The name super comes from a convention of calling the parent class a superclass and the child class a subclass.

By using the same type of information we would need to make a traditional automobile, we attempt to build an electric car in order to see how well inheritance works.

We create a new ElectricCar class instance at y and save it in my_tesla. This line instructs Python to execute the __init__() method from the parent class Car by calling the __init__() method defined in the child class ElectricCar.

We offer the supporting evidence of Tesla, Model S, and 2016. There are currently no properties or methods specific to an electric automobile other than __init__().

Right now, all we’re doing is making sure the electric car exhibits the proper automobile behaviours.

Defining Attributes and Methods for the Child Class

Once a child class inherits from a parent class, any additional properties and methods required to set the child class apart from the parent class can be added.

Let’s add a battery-related property that is unique to electric vehicles and a way to report on it. We’ll develop a method that prints a description of the battery and saves the battery size:

class Car():
--snip--
class ElectricCar(Car):
"""Represent aspects of a car, specific to electric vehicles."""
def __init__(self, make, model, year):
super().__init__(make, model, year)
self.battery_size = 70

def describe_battery(self):
"""Print a statement describing the battery size."""
print("This car has a " + str(self.battery_size) + "-kWh battery.")

my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()
  • We create a new characteristic called self.battery_size at u and initialise it to, let’s say 70.
  • While not being connected with any instances of Car, this characteristic will be applied to any instances of the ElectricCar class that are produced.
  • Additionally, we have a function called describe_battery() that outputs details about the battery at v. We receive a description when we call this function that is obviously particular to an electric car:
  • The degree to which you can specialise in the ElectricCar class is unrestricted.
  • To model an electric automobile with the level of precision you want, you may include as many characteristics and techniques as you like.
  • Instead of adding an attribute or function to the ElectricCar class, which is only applicable to electric Classes 175 cars, the Car class should be introduced. In that case, anybody who uses the Car class will also have access to that capability, and the ElectricCar class will only include code for data and functionality exclusive to electric automobiles.

Overriding Methods from the Parent Class

Any parent class method that doesn’t suit what the child class is attempting to emulate can be overridden.

To do this, you declare a method in the child class with the same name as the parent class method you wish to override.

Python will ignore the parent class method and focus only on the child class method that you write.

Imagine there was a function named fill_gas_tank() in the Car class. You may wish to override this function because it is useless for an all-electric car. Here is one approach to doing that:

def ElectricCar(Car):
--snip--

def fill_gas_tank():
"""Electric cars don't have gas tanks."""
print("This car doesn't need a gas tank!")
  • Now if someone tries to call fill_gas_tank() with an electric car, Python will ignore the method fill_gas_tank() in Car and run this code instead
  • When you utilise inheritance, you may force your child classes to override any parent class features you don’t need while keeping the ones you want.

Instances as Attributes

You could notice that you’re adding more and more information to a class as you mimic anything from the actual world in code.

You’ll notice that your files are getting longer and that you have a growing set of methods and properties.

You might realise that a portion of one class can be written as a distinct class in these circumstances.

You may divide your huge class into several smaller ones. For instance, if we keep adding information to the ElectricCar class, we may observe that we are adding a lot of characteristics and methods that are particular to the car’s battery.

When we see that this is taking place, we can halt the process and transfer those characteristics and methods to another class named Battery.

Then we can use a “Battery” instance as an attribute in the ElectricCar class:

class Car():
--snip--

class Battery():
"""A simple attempt to model a battery for an electric car."""
def __init__(self, battery_size=70):
self.battery_size = battery_size

def describe_battery(self):
"""Print a statement describing the battery size."""
print("This car has a " + str(self.battery_size) + "-kWh battery.")

class ElectricCar(Car):
"""Represent aspects of a car, specific to electric vehicles."""
def __init__(self, make, model, year):
"""
Initialize attributes of the parent class.
Then initialize attributes specific to an electric car.
"""
super().__init__(make, model, year)
self.battery = Battery()


my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
  • We create a brand-new class named Battery at the position that doesn’t derive from any other classes.
  • Battery_size is an additional argument to self in the __init__() function. If no value is specified for this optional parameter, the battery size is set to 70.
  • Additionally, the function describe_battery() has been relocated to this class. We now include a self.battery property in the ElectricCar class.
  • This line instructs Python to create a new instance of Battery and store it in the attribute self.battery with a default size of 70 due to the lack of a value.
  • Every time the __init__() function is invoked, a Battery instance will now be automatically constructed for any ElectricCar instances.
  • This line instructs Python to search for the battery attribute on the my_tesla instance, execute the function describe_battery(), and then identify the Battery instance that is stored in the attribute.
  • The result is the same as what we previously observed:
  • Despite the fact that it appears to be a lot of extra effort, we can now describe the battery in as much detail as we like without complicating the ElectricCar class.
  • Let’s add another function to the Battery that computes the car’s range depending on the size of the battery:
class Car():
--snip--

class Battery():
--snip--

def get_range(self):
if self.battery_size == 70:
range = 240
elif self.battery_size == 85:
range = 270

message = "This car can go approximately " + str(range)
message += " miles on a full charge."
print(message)

class ElectricCar(Car):
--snip--

my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()
  • The new function get_range() at u does a quick examination. Get_range() sets the range to 240 miles for batteries with a capacity of 70 kWh, and 270 miles for batteries with a capacity of 85 kWh.
  • After that, it reports this value. When utilising this technique, we must once more call it using the battery characteristic of the vehicle.
  • Based on the size of the battery, the output indicates the vehicle’s range:

Polymorphism

Consider having a variety of noisy animals, such as a dog, cat, and bird. Each animal, nevertheless, has its own sound. Polymorphism is connected to this idea.

Polymorphism is the ability of objects (instances of classes) to have different forms or behaviours while sharing a common interface or base class. In simpler terms, it means that different objects can respond or behave differently to the same method or function call.

In a simple way

  1. While sharing a method name, objects from various classes might each have their own distinctive behaviours.
  2. These classes are frequently connected through inheritance, in which a base class establishes a standard interface (methods) that derived classes may use in their own unique ways.
  3. When you call a method on an object, the class of the object controls how the method is carried out. Therefore, despite the fact that the function has the same name, various objects may implement it differently depending on their class.
  4. As a result, you may design code that treats various objects consistently even if they may act differently.
class Animal:
def make_sound(self):
pass

class Dog(Animal):
def make_sound(self):
print("Woof!")

class Cat(Animal):
def make_sound(self):
print("Meow!")

class Bird(Animal):
def make_sound(self):
print("Chirp!")

# Creating instances of different animals
dog = Dog()
cat = Cat()
bird = Bird()

# Calling the common method on different objects
dog.make_sound() # Output: Woof!
cat.make_sound() # Output: Meow!
bird.make_sound() # Output: Chirp!

The output

  • In this illustration, Dog, Cat, and Bird are descended classes from the basic class Animal. Each of these classes has a unique make_sound() function implementation.
  • Every object that is created from an instance of one of these classes and calls the make_sound() function does so in accordance with the implementation that was chosen for that object. Even though they all use the same Animal class-provided interface, the dog barks, the cat meows, and the bird chirps.
  • Thus, polymorphism in classes enables several objects to react to the same method name in their own special ways, increasing the flexibility and reusability of code. Despite the fact that they have distinct behaviours or shapes, it allow us to handle various things consistently.

Conclusion

To sum up, Python classes offer a mechanism to arrange information and functionality into templates that can be modified and reused. With their help, it is possible to create several instances of a class, each with a unique set of properties and functions. Classes facilitate code reuse and modularity while aiding in the modelling of real-world things. Developers may make use of the power of object-oriented programming and write better-organised, efficient code by grasping the notion of classes.

If you’re eager to delve into the job market, check out the website to review requirements, qualifications, and salary information. Take a glimpse into your potential future.😉
Remote Rocketship

Your feedback is invaluable! ❤️

Feel free to connect 🌹me on:

Twitter

Linkedin

Github

Sincerely, Noran🌹

--

--