Classes in Python

Python Classes – Complete Tutorial

Python is an Object-Oriented Programming Language. That means that Python implements a programming paradigm based on “objects,” where every “object” can store some data and the code to modify it. Class is a description of the “object” that defines attributes that characterize the object and the methods that this object implements. This tutorial will cover Python classes in detail, define classes, use constructors, implement inheritance and polymorphism, and cover some other important aspects of classes by examples.

Python Classes

In Python, a class is a template for creating objects. Objects created from the same class share certain characteristics, but each object also has its own unique identity. When you create an object from a class, you are said to be instantiating that class. The process of instantiating a class creates a new instance of that class. Each instance of a class has its own namespace, which is a collection of variables and methods that belong to that particular instance. When you create an instance of a class, you can access its namespace using the dot notation. For example, if you have an instance of the Foo class, you can access its bar() method by using the syntax foo.bar(). Python’s use of instances enables developers to create complex applications by reusing code from multiple objects. Instances also help to keep code organized and prevent naming collisions between variables and methods from different objects.

In Python, a class instance is an object that is created by a class. A class is a template for creating objects, and an instance is a specific object that has been created from a class. Class instances can have their own attributes and methods, which are defined by the class. Class instances are created by calling the class’s init method. When a class instance is created, the __init__() method is automatically called, and the new instance is passed to it as the first argument. The __init__() method can then initialize the instance’s attributes. Class instance can also be created by using the “class” keyword followed by the name of the class. For example, if we have a class named “MyClass”, we can create an instance of it like this:

my_instance = MyClass()

Defining a new class in Python allows us to create a new type of object. A user-defined class describes the data that the class object should store and methods that can be used to manage that data. A user-defined class is a primitive data structure like list, string, and number classes, but it can store and process more complex information.

Class definition in Python

The simplest form of class definition syntax in Python looks like this:

# keyword 'class' declares a new class 
class ClassName:
    <statement....>
    ...
    ...
    <statement....>

The class keyword allows you to create a new class definition in Python. The statements inside a class are defining variables or functions.

The best practice is to use a docstring as the first statement in class to describe it.

class ClassName:
    '''This is a class that I had created'''
    ...
    ...
    <statement....>

Docstrings are written inside a triple single (''') or double (""") quotes.

Every Python class has special attributes and methods that you can access by putting double underscores (__) in front and after them, for example, __doc__, __init__() and many others. For example, __doc__ the attribute contains the docstring of the class:

class ClassName:
    '''This is a class that I had created'''
print(ClassName.__doc__)

Class vs. instance attributes in Python

Python class attribute is an attribute of a class that is defined inside the class definition. A class attribute is shared by all the instances of the class. Class attributes are generally used to define the characteristics of the class. For example, a class attribute could be used to define the color of an object. Class attributes are different from instance attributes. Instance attributes are attributes that are specific to an instance of a class. They are not shared by all the instances of the class. Instance attributes are defined inside the __init__ method of a class.

The major difference between class and instance variables is that the class variables are shared by all instances of a class, while instance variables are unique to each individual instance. In general, class variables are used to store information that is common to all instances of a class, while instance variables are used to store information that is specific to a particular instance. For example, you might use a class variable to stores the default color for all instances of a particular class, while you would use an instance variable to store the color of a particular instance. In most cases, it is best to use instance variables, since they offer more flexibility and can be more easily changed on a per-instance basis. However, there may be some situations in which using a class variable makes more sense. Ultimately, the decision of whether to use a class or instance variable depends on your specific needs.

In summary, there are two types of attributes available in classes:

  • An instance attribute is a Python variable belonging to one, and only one, object. This variable is defined inside the constructor function of the class and only accessible in the scope of the object.
  • A class attribute (class variable) is a Python variable that belongs to a class itself rather than a particular object. It is defined outside the constructor function of the class and shared between all the objects of this class.

Let’s illustrate the differences by example:

class Person:
    '''This class defines a Person'''
    # class variable
    age = 18
    def __init__(self, name):
        self.name = name

In the example above:

  • age – is a class attribute defined outside of the class constructor
  • name – is an instance attribute defined in the class constructor

Attribute referencing in Python classes

Attribute references in classes use the standard syntax used by all attribute references in Python. For example, class_obj.name is a valid attribute reference. Here’s how you can get access to class attributes:

class ClassName:
    '''This is a class that I had created'''
    number = 7
    name = "MyName"
print(ClassName.number)
print(ClassName.name)
Attribute referencing in Python classes

Constructors in Python classes

Class functions that begin with a double underscore (__) that have special meaning in Python. One of such functions is __init__(). This special function is called whenever a new object of a class is initiated, and it is called a constructor of the class. In the following example, we will create a new class named Person and use a constructor to assign the name and gender of a person.

class Person:
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

Note: self is always the first argument you need to define in the constructor or any function in the class. It references the instance of the class and always points to the current object. 

We can get access to any class instance attribute or function using self.

Here’s an example:

class Person:
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def Introduction(self):
        print("Hello! My name is", self.name)
person1 = Person("Bashir", "Male")
person2 = Person("Bibisoro", "Female")
person1.Introduction()
person2.Introduction()
Self-in-constructor

In the above example, we create two objects: person1 and person2 and get access to the names of the objects using self keyword.

You may redefine self keyword, but make sure that it is still your first argument in the function or the class’s constructor. Let’s modify our previous example to illustrate such behavior: 

class Person:
    def __init__(Noself, name, gender):
        Noself.name = name
        Noself.gender = gender
    def Introduction(abc):
        print("Hello! My name is", abc.name)
# create objects of class Person
person1 = Person("Bashir", "Male")
person2 = Person("Bibisoro", "Female")
person1.Introduction()
person2.Introduction()
Accessing Python class variables without self

Note: it is a good practice not to redefine the default self keyword to make it easier for other developers to read your code in the future.

Class vs. instance in Python

A class is a blueprint for the instance that should be defined. It does not contain actual data. It is just a code of the template or definition. For example, the class of Person contains the attribute name and gender but does not contain the actual values for name and gender variables.

An instance of the class is the actual object that contains real data and function definitions. An instance of Person class is no longer a blueprint. It contains the name of a person like Bashir and gender like Male.

Methods in Python classes

In Python, class methods are functions that are defined within the class. For example, Introduction() from our code snippet is a class method.

Another example of the method declaration in the class:

class Person:
    # class function definition
    def hello(self):
        return "Hello there!"
Declaring method in Python class example

In this example, class Person contains a definition of the method hello() that is supposed to return the "Hello there!" string when this method is called.

Modifying class instance attributes in Python

Once the class instance is created, you can easily change its attributes (class members’ variables). Here’s an example:

class Car:
    wheel = 4
    color = "red"
# Create a new object instance of the Car class
car1 = Car
print(car1.wheel)
print(car1.color)
# now we will change the properties and will print
car1.wheel = 3
car1.color = "blue"
print(car1.wheel)
print(car1.color)
Modifying class instance attributes in Python

We can always change the attributes of objects using the obj.propertyName syntax as shown above.

Deleting class attributes in Python

In Python, we can use keyword del if we need to delete the attribute of the class:

class Car:
    wheel = 4
    color = "red"
# delete attribute
del Car.color
print(Car.color)
Deleting class attributes in Python

In the example above, we deleted the color attribute, and when we tried to get access to it, we got an error that says that the Car class has no attribute color.

Deleting class objects in Python

Class objects can also be deleted in the same way as the properties. Use del keyword to delete the declaration of the class within your Python program:

class Car:
    wheel = 4
    color = "red"
del Car
print(Car.color)
Deleting class object in Python

Inheritance in Python

Inheritance is a feature of a class in Python to inherit all methods and attributes from another class. When describing an inheritance, we usually speak about a parent class or a base class that shares its attributes and methods with the child or a derived class.

Innheritance-diagram

We are using inheritance every time we need to extend the behavior of the base class without copy-pasting its code completely.  

Here is a Python syntax for defining inheritance between classes:

class ParentClass:
    website = "hands-on.cloud"
class ChildClass(ParentClass):
    def print_website_name(self):
        print(f'Website name: {self.website}')
child_class = ChildClass()
child_class.print_website_name()

In the example above, the ChildClass will always have the website attribute even if it has not been defined.

Parent class syntax

Any class can be defined as a parent class in Python. The syntax for a parent class is similar to any other normal class. Here’s an example of the Person class which will be a parent class in our example:

class Person:
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def namPritning(self):
        print(self.name)
    def genderPrinting(self):
        print(self.gender)
man1 = Person("Bashir", "Male")
man1.namPritning()
man1.genderPrinting()
Parent class syntax

Child class syntax

A child class declaration in Python is similar to any class but with the difference that it takes the parent class as a parameter to inherit all functions and variables.

Take a look at the example below:

class Person:
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def namePrinting(self):
        print(self.name)
    def genderPrinting(self):
        print(self.gender)
class Shopkeeper(Person):
    pass
SK = Shopkeeper("Armio", "Male")
SK.namePrinting()
SK.genderPrinting()
Child class syntax

Inheritance example

Now, let’s take one more example to understand the inheritance in detail.

Let’s create a ParentClass and define some methods within it.

class ParentClass():
    def __init__(self, name):
        print("Constructor of parent class")
    def main(self):
        print("This is main method of parent class")
Inheritance example - Parent class

Now let’s create a child class. Remember that we need to provide a ParentClass as a parameter in a child class:

class ChildClass(ParentClass):
    def child(self):
        return True
Inheritance example - Child class

Now, let’s use the inherited function from the ParentClass in the ChildClass:

class ParentClass():
    def __init__(self, name):
        print("Constructor of parent class")
    def main(self):
        print("This is main method of parent class")
class ChildClass(ParentClass):
    def child(self):
        return True
x = ChildClass
x.main("name")
Inheritance example - Using parent class function

Here you can see that we created a new object x which inherits all the properties of ParentClass. We can now easily get access to the functionality of ParentClass from our ChildClass.

Overriding base class behavior

We can always redefine inherited attributes and functions in the child class. For example, if you want to print out “This is the main method of Child class.” instead of “This is the main method of the parent class,” you need to create the main method inside our child class and redefine its behavior.

class ParentClass():
    def __init__(self, name):
        print("Constructor of parent class")
    def main(self):
        print("This is main method of parent class")
class ChildClass(ParentClass):
    def main(self):
        print("This is main method of Child class")
x = ChildClass
x.main("name")
child-class-inherited-property-change

Multiple inheritances in Python

We had seen how we could inherit the functionality of one parent class inside a child class through the inheritance. Python also provides us the ability to inherit the functionality of multiple classes. Multiple inheritances mean that we can get access to properties and functions of many base classes from our child class.

Multiple inheritance in Python diagram

The syntax of such inheritance is simple. In the definition of the child class, you have to provide all base classes as parameters. 

Here’s the syntax of multiple inheritances:

The syntax of multiple inheritance

Now, let take a look at the implementation example of multiple inheritances:

class ParentClass():
    def parent1():
        print("Parent 1 is printing")
class ParentClass2():
    def parent2():
        print("Parent 2 is printing")
class ChildClass(ParentClass, ParentClass2):
    pass
x = ChildClass
x.parent1()
x.parent2()
Multiple inheritance in Python

In the above example, we’re getting access to multiple base classes functions from a ChildClass.

Hierarchical inheritance in Python

Hierarchical inheritance in Python is a situation when multiple child classes are inherited from a single parent class.

Heirarchical-diagram

Now let take a look at the example. We will create one parent class. And then will get access to its method from multiple child classes:

class Parent():
    def parent1():
        print("Parent is printing")
class ChildClass1(Parent):
    def child1():
        print("child 1 is printing")
class ChildClass2(Parent):
    def child2():
        print("child 2 is printing")
x = ChildClass1
x.parent1()
x.child1()
y=ChildClass2
y.parent1()
y.child2()
Heirarchical-program-in-python

As you can see, every child class has its own functions, but both of them inherit the same parent1() method from the base class.

Super() Function in Python

Calling a super() method in the child class constructor allows us to provide attributes to the constructor of the parent class. 

class Parent():
    def __init__(self, name):
        self.name = name
class Child(Parent):
    def __init__(self, childname):
        super().__init__(childname)
c = Child("Bashir")
print(f'name in Parent class: {c.name}')
Super() function in Python

In the example above, we are using the Child class constructor to set up name variable in the Parent class constructor.

Built-in class attributes in Python

There are some common built-in attributes that every class has. They can be accessed by using the dot (.) operator. Here’s a list of the important built-in attributes of the class object in Python:

  • __dict__Dictionary that contains the class namespace
  • __doc__ – Contains a documentation string of the class if defined.
  • __name__ – A variable that stores class name.
  • __module__ – Module name in which the class has been defined
  • __bases__ – A possibly empty tuple containing the base classes, in the order of their inheritance at the child class.

Encapsulation in Python

Encapsulation is one of the important concepts of Object-Oriented Programming in Python, which you can use to restrict access to methods and variables of the class.

Protecting class data in Python

An object’s attribute may or may not be visible outside of the class definition. To hide or protect base class variables or functions from access from the child class uses double underscore (__) as a prefix of the name of attribute or function. 

The following example demonstrates how to protect the data of the Parent class.

class Person:
   __age = 10
  
   def count(self):
      self.__age += 1
      print (self.__age)
Age = Person()
Age.count()
print (Age.__age)
Protecting class data in Python

The example above shows that you can get access to a variable __age only from the class method and not from the outside scope of the class.

Polymorphism in Python

The word “polymorphism” means multiple forms. In programming, polymorphism allows you to define the same function but with different parameters multiple times. Python will automatically detect which function to use based on the provided the function arguments. For example, Python’s built-in function len() can count elements in the list object or the number of characters in the string.

In the same way, polymorphism in allows us to define the same functions in each class and get access to them based on the class type:

class Person():
     def name(self):
         print("Bashir")
     def age(self):
         print("20")
class Person2():
     def name(self):
         print("Aqeel")
 
     def age(self):
         print("23")
FirstPerson = Person()
SecondPerson = Person2()
for x in (FirstPerson, SecondPerson):
    x.name()
    x.age()
Polymorphism-in-python-example

Python uses two different class types in the same way. Here, we have created a for loop that iterates through a tuple of objects. We call the methods without concern about which class type each object belongs to for every object from the tuple.

Additional classes use-cases in Python

This last section will cover some additional handy use-cases of Python classes that developers commonly use.

User-defined exception in Python

In Python, new custom exceptions can be defined by inheriting from the built-in Exception class. Developers usually define custom exceptions to handle different errors in the program separately.

class MyCustomException(Exception):
    pass

Python __repr__() method

The Python __repr__() method is used to redefine a string representation of the class. It can only have one parameter self and should return a string. Usually, developers redefining __repr__() function to provide meaningful information about the data stored in the class.

class Person:
    def __init__(self, name):
        self.name = name
    
    def __repr__(self):
        return self.name
Alam = Person('Alam')
print(Alam)
repr__-method-in-python

In the example above, we are redefining the __repr__() function to return a name stored in the class.

Python issubclass() function

Python has a built-in issubclass() function allows us to check if one class is inherited from another class (second argument).

class Person:
    def type(self):
        print("Parent class")
class Man(Person):
    def type(self):
        print("Child class")
print(issubclass(Man, Person))
Python issubclass() function

Summary

In this tutorial, we’ve covered classes in Python, class constructors, inheritance, polymorphism, and some important built-in properties of classes.

Similar Posts