Python Classes: In simple language
Object-Oriented Programming (OPP) presents an alternative and frequently more efficient programming approach. It involves organizing code to reduce repetition and create new programs by modifying pre-existing code rather than altering it directly.
In Python, a class serves as a tool employed to create new object types that enable inheritance- a system for customizing and reusing code we will look at later in this guide.
In this guide, we have three sections, we will start with an introduction to Python classes, we will then look at the anatomy of a class using an example and finally, we will touch on some advanced topics like inheritance and access control.
Introduction to Python classes
Understanding the basics
Before we start, let us define some keywords associated with Python classes:
An object(s) is a specific instance of a class. When you create a class you’re defining a blueprint that describes attributes and methods that the objects you create from the class will possess. (e.g., Object ‘drv1’ (for driver one))
An object attribute(s) refers to the properties or characteristics associated with a specific object.
We want to define this in our class. (e.g., Object, ‘drv1’ with attribute(s) ‘first name’ and ‘last name’)
An object method(s) is a function defined within a class that defines how we want to use our object(s). Methods can perform operations on the object(s) attributes or execute specific actions associated with the object. (e.g., Method ‘fullname()’ that prints the full name of object ‘drv1’)
What are classes?
A class in Python serves as a blueprint that defines the attribute(s) and method(s) that objects created from it will have.
It encapsulates data and functionality into a single unit, allowing you to create multiple objects that share the same structure and behavior. (e.g., Object 2; ‘drv2’ with a ‘first name’ and ‘last name’ same as object 1; ‘drv1’)
Why use classes?
Classes are Python program units, just like functions and modules. They serve as an additional container to encapsulate both logic and data.
Compared to other program units (functions and modules), Classes have three important differences that enhance their utility in constructing new objects.
1. Multiple objects: Classes help us generate one or more objects.
Each object generated from a class has access to the class’s attribute(s) and gets a namespace of its own for data that varies per object.
2. Supports inheritance: Classes also support the OOP notion of inheritance; we can extend a class by redefining its attributes outside the class itself. More generally, classes can build up namespace hierarchies, which define names to be used by objects created from classes in the hierarchy.
3. Operator overloading: By providing special protocol methods, classes can define objects that respond to build-in type operations like slicing, concatenation, indexing, etc.
Anatomy of a class
Creating your first class
We construct a class using the class statement followed by its name and a colon at the end. See below
Class Name: # names are usually written in uppercase. (e.g., Class Motorsport)
It marks the beginning of the class block where you specify the attributes and methods that describe the objects you created
Consider the example below:
(name)
class Motorsport: # note the use of colon
……..perc_raise = 1.05 # Class attribute
……..def __init__(self, first_name, last_name, pay): # object attributes
……..self.first_name = first_name
……..self.last_name = last_name (instance variables)
……..self.pay = pay
drv1 = Motorsport(‘Max’, ‘Verstappen’, 2000000) # instantiating an object, drv1
>>Print(drv1.first_name) # print the first name of drv1
>>>max # Output
Explanation:
This code defines a Python class named Motorsport which represents individuals involved in motorsport and their associated attributes.
Let’s break it down into five steps:
Class definition:
Motorsport is our class.
perc_raise = 1.05 is a class attribute in the Python code above.
In Python, class attributes are variables that are defined within a class but outside of any class methods. They are accessible to all instances (objects) of that class.
perc_raise is defined within the Motorsport class but outside of any class methods (such as __init__).
This means it’s a property of the class itself rather than a property specific to any instance of the class. It’s shared among all instances of the class and can be accessed using the class name or its instances.
For example, you could access the perc-raise attribute either using the class name or an instance of the class:
Print (Motorsport.perc_raise) # Accessing using class name, Motorsport
# OR
drv1 = Motorsport (‘Max’, ‘Verstappen’, 2000000)
Print (drv1.perc_raise) # Accessing using an instance, drv1
Both would output 1.05, demonstrating that perc_raise is a class attribute shared by all instances of the Motorsport class.
Constructor (__init__ method):
In Python, the __init__ method is a special method used to initialize the attributes of an object when an instance of a class is created. It’s also known as the constructor method.
Let’s break down the constructor above;
1. Purpose of __init__:
The __init__ method is the first method that gets called when you create an instance of the Motorsport class
It takes in four parameters: self (which represents the instance being called), first_name, last_name, and pay.
Inside __init__, it assigns the values passed as arguments (first_name, last_name, pay) to instance variables (self.first_name, self.last_name, self.pay).
2. Instance variables(self.first_name, self.last_name, self.pay):
Instance variables are specific to each instance of the class. They hold data that is unique to each object from the class.
self.first_name, self.last_name, and self.pay are instance variables initialized in the __init__ method using the values passed during object creation
(Motorsport (‘Max’, ‘Verstappen’, 55000000).
Get Paul Mutembei’s stories in your inbox
Join Medium for free to get updates from this writer.
For instance drv1 = Motorsport(‘Max’, ‘Verstappen’, 2000000), drv1.first_name will hold the value ‘Max’, drv1.last_name will hold ‘Verstappen’, and drv1.pay will hold 55000000
Creating an instance of the class (drv1):
The line Drv1 = Motorsport(‘Max’, ‘Verstappen’, 2000000) creates an instance (drv1) of the Motorsport class and initializes it with the first name ‘Max’, last name ‘Verstappen’ and a pay of 55000000
Accessing Object Attributes:
The line print(drv1.first_name) prints the value of the first_name attribute of the drv1 object
Object attributes are accessed using the dot notation object.attribute just as module attributes.
Output:
The expected output of print(drv1.first_name) is ‘Max’(the first name of drv1).
Before we move on to advanced concepts, let’s talk about defining and invoking methods
Consider the following code as an extension of the code above:
Suppose we want to print the full name of the driver in our class, then we would need a method that operates on the first_name variable and the last_name as shown;
def full_name(self): # note that we use self here
Return ‘{} {}’.format(self.first_name, self.last_name) # execute this code
Print(drv1.full_name()) # Outputs ‘Max Verstappen’
The above function full_name() is invoked with drv1 as its argument, it executes the format expression that returns the objects full_name.
In the above examples, we have learned the following on creating and using classes:
1. Creating objects(instances): Instantiating objects from classes and defining attribute properties
2. Working with methods: Defining and invoking methods and accessing attributes in methods
Advanced concepts in classes
Inheritance and subclasses
Suppose we want to create another class defined on the Motorsport class called Formula1 with the same attributes as the parent class (Motorsport).
In the example below, the subclass (formula1) also referred to as the child class of the parent class has an additional attribute ‘team’.
class Formula1(Motorsport):
……..def __init__(self, first_name, last_name, pay, team)
……..super().__init__(first_name, last_name, pay)
……..self.team = team
drv2 = Formula1(‘Lewis’, ‘ Hamilton’, 35000000, ‘Mercedes AMG’)
print(drv2.fullname()) # inherited method from Motorsport, Outputs Lewis Hamilton
print(drv2.team) # Outputs Mercedes AMG
Explanation:
The code above demonstrates inheritance and the use of a subclass inheriting from a superclass.
Let’s break it down
1. Inheritance:
The line class Formula1 (Motorsport): defines a new class Formula1 that inherits from the Motorsport class. This means that formula1 inherits all the attributes and methods from Motorsport.
2. Constructor (__init__) in the subclass:
def __init__(self, first_name, last_name, pay, team): is a constructor method for the Formula1 class.
The line Super ().__init__(first_name, last_name, pay) calls the constructor of the superclass (Motorsport) to initialize the inherited (first_name, last_name, pay) for the formula1 object.
The line self.team = team initializes an additional attribute specific to Formula1 objects, storing the object’s team information.
3. Accessing Inherited Methods and Subclass Attributes:
The print(drv2.fullname()) line calls the fullname() method inherited from the Motorsport class. It outputs the full name of the driver, ‘Lewis Hamilton’.
The line Print(drv2.team) is the same as earlier and it prints the value of the team attribute specific to the Formula1 class for the drv2 object, Outputs ‘Mercedes AMG’.
Encapsulation and access control
Encapsulation
Encapsulation refers to the bundling of data (attributes) and the methods that operate on that data within a single unit (a class in this case).
It hides the internal state of an object and restricts direct access to certain parts of the object
In the code above, encapsulation is illustrated by the classes Motorsport and Formula1 encapsulating attributes (first_name, last_name, pay, team) and the methods (fullname()) within their respective class definitions.
Access Control:
Access control refers to regulating access to the attributes and methods of a class. It defines the level of visibility or accessibility of these elements from within or outside the class.
In python, theirs is a convention that attributes and methods prefixed with a single underscore(e.g., _attribute) are considered ‘protected’ meaning they should be accessed within the class or its subclass, but are technically accessible outside the class.
Attributes or methods prefixed with double underscore (e.g., __attribute) are ‘private’ and are intended to be accessed only within the class itself.
In the code above, the attributes first_name, last_name, and pay are implicitly accessible due to being set within the constructor (__init__). However, the team is directly set within the Formula1 class.
Conclusion and Next Steps
Recap on key concepts:
Python, as a versatile and powerful programming language, leverages the concept of classes to implement object-oriented programming (OOP) paradigms. Classes serve as blueprints for creating objects, encapsulating attributes, and defining behaviors.
1. Classes act as templates for creating objects, which are instances of those classes. They encapsulate data (attributes) and behaviors (methods) within a single entity.
2. Attributes represent data stored within a class or instance, while methods are functions defined within a class to perform operations on the data.
3. Inheritance facilitates the creation of new classes (subclasses) by deriving features from existing classes (superclasses). This mechanism enables code reuse and establishes a hierarchy among classes.
4. Encapsulation refers to bundling data (attributes) and methods within a class, restricting direct access from outside the class. It enhances security and promotes cleaner code organization.
5. Polymorphism allows objects of different classes to be treated as objects of a common superclass. It enables flexibility by allowing methods to be used interchangeably across related classes.
6. While Python lacks strict access control like some other languages, conventions exist to denote access levels. Single underscores imply ‘protected’ attributes/methods, while double underscores imply ‘private’. However, these are conventions and not strict rules enforced by the language.
7. The ‘__init__’ method initializes object attributes when an instance of a class is created. It’s the constructor method that gets automatically called upon object instantiation.
8. The ’self’ parameter refers to the instance itself and is used to access instance variables within methods. Instance variables store data unique to each instance of the class.
In conclusion, understanding Python classes is important for effective object-oriented programming. Mastering key concepts — classes, objects, inheritance, encapsulation, polymorphism, access control, constructors, and instance variables — unlocks Python’s OOP potential. These form the core for organized, reusable, and scalable code, enabling robust software development and empowering developers to craft resilient, adaptable applications.
Thank you for reading.





