8 Tips For Object-Oriented Programming in Python


Object-oriented programming language is a programming paradigm that is widely used in software design as it makes the code reusable and reduces code redundancy. It uses classes and objects to implement real-world objects in programming. Python and other languages like C++, java, javascript, etc support object-oriented programming. In this article, we will understand the characteristics of object-oriented programming and some tips to use object-oriented programming in Python.

In object-oriented programming, objects are created from their class blueprint. These objects represent real-world objects as they have some properties called attributes and methods just like real-world objects have their own properties and behavior. For example, if we consider a Dog as a real-world object then it has certain properties like breed, color, size, and also some behaviors like barking, running speed, etc. These properties and behavior can be encapsulated in a Dog object which will have its attributes and methods to represent a dog in programming.

Object-oriented programming has 4 characteristics that can make the code more modular, reusable, and maintainable −

Inheritance

In Inheritance, a new class is created from an already existing class i.e the new class uses the methods and behavior of the existing class which makes the code more reusable and maintainable. Inheritance gives the functionality to also add new functions to the new class and to override existing functions of the old class in the inherited class. Inheritance reduces code complexity and makes the code more reusable and scalable.

Encapsulation

Encapsulation is the process of encapsulating data and the functions using that data in a single entity. OOP encapsulates its data and the function that uses that data in class and allows access and modification to that data and functions in a controlled way. Encapsulation uses access modifiers like public, private, and protected to provide restricted access to the data and functions of a class.

Polymorphism

Objects behaving in different ways in different situations is done using polymorphism. In OOP, polymorphism can be achieved through method overloading and method overriding. Method overloading is the process of creating multiple methods with the same name but different parameters, while method overriding is the process of creating a new implementation of a method in a subclass. Polymorphism allows developers to write more flexible and adaptable code, making it easier to add new functionality or features to the system without breaking existing code.

Data Abstraction

Abstraction is the process of hiding the implementation details of the object and showing only the functionality. Due to abstraction the user is able to know what the function is doing but is unable to understand how it is working or the internal details of the function. Abstraction allows developers to create a high-level view of the system, making it easier to understand and manage the complexity of the code

Now, let's look at some tips for object-oriented programming in Python −

Use Classes to Model any Real-world Object

Classes are the blueprints that define the attributes and methods of the object. Before creating an object make sure that it is created from a valid class as when we create an instance of a class we create an object with real values to its attributes.

Example

If you are creating a game first create classes for players, enemies, weapons, and items and then create instances of those classes to create the game logic.

class Player:
   def __init__(self, player_name):
      self.player_name = player_name

class Enemy:
   def __init__(self, enemy_name, enemy_health):
      self.enemy_name = enemy_name
      self.enemy_health = enemy_health

class Weapon:
   def __init__(self, weapon_name, weapon_damage):
      self.weapon_name = weapon_name
      self.weapon_damage = weapon_damage

class Game:
   def __init__(self, players, enemies, weapons):
      self.players = players
      self.enemies = enemies
      self.weapons = weapons

   def start_game(self):
      print("Game has started")

   def end_game(self):
      print("Game has ended")

# create some players, enemies, and weapons
player1 = Player("John")
player2 = Player("Jane")
enemy1 = Enemy("Zombie", 50)
enemy2 = Enemy("Goblin", 75)
weapon1 = Weapon("Sword", 25)
weapon2 = Weapon("Axe", 30)

# create a game object with the players, enemies, and weapons
game = Game([player1, player2], [enemy1, enemy2], [weapon1, weapon2])

# start the game
game.start_game()

# play the game...

# end the game
game.end_game()

Output

Game has started
Game has ended

Use Meaningful Naming Convention

Use meaningful names for the classes and also the attributes and functions defined in the class. The name should specify the behavior of the class and the functions. In the industry, Camel-Casing is used as a default naming convention. Always make sure that the one class, attribute, and method is responsible for only one task.

Example

In the below example, a Person class is created with the class name “Person” and attributes such as name, age, and occupation.

class Person:
   def __init__(self, person_name, person_age,person_occupation):
      # type: (str, int, str) -> None
      self.person_name = person_name
      self.person_age = person_age
      self.person_occupation = person_occupation

   def introduce(self):
      # type: () -> str
      return "Myself {}, I am {} years old, and I work as a {}.".format(self.person_name, self.person_age, self.person_occupation)

# Create a person object
person1 = Person("John Smith", 35, "Software Engineer")

# Call the introduce method to get the person's introduction
introduction = person1.introduce()

# Print the introduction
print(introduction)

Output

Myself John Smith, I am 35 years old, and I work as a Software Engineer.

Differentiate Between Class and Instance-level Data

As inheritance is an important pillar of OOPs, it is necessary to differentiate between class-level and instance-level data to better understand inheritance. The attributes of the instance of a class are restricted to that object and these attributes are defined inside the constructor of the class whereas the attributes which belong specifically to the class are defined outside the constructor function of the class.

Example

In the below example, we created a class Car with class level attribute and instance level attribute. The class level attribute and instance level attribute can be accessed as follows −

class Car:
   # class-level attribute
   category = 'Vehicle'

   def __init__(self, make, model):
      # instance-level attribute
      self.make = make
      self.model = model

# creating instances of Car class
car1 = Car('Toyota', 'Corolla')
car2 = Car('Honda', 'Civic')

# accessing class-level attribute
print(Car.category)    # output: Vehicle

# accessing instance-level attributes
print(car1.make, car1.model)    # output: Toyota Corolla
print(car2.make, car2.model)    # output: Honda Civic

# changing class-level attribute value
Car.category = 'Automobile'

# accessing updated class-level attribute
print(Car.category)    # output: Automobile

# changing instance-level attribute value
car1.make = 'Nissan'

# accessing updated instance-level attribute
print(car1.make, car1.model)    # output: Nissan Corolla

Output

Vehicle
Toyota Corolla
Honda Civic
Automobile
Nissan Corolla

Use Polymorphism to Write Flexible Code

polymorphism is the process by which functions or objects can be used in different forms. Using polymorphism you can write flexible code by which objects from different classes can be used interchangeably with the same function which reduces the code and avoids redundancy. For example, if you have a function that takes a list of objects as an argument, you can pass in a list of any objects that have the same interface. This allows you to write generic code that can work with a variety of objects.

Docstring in Python

For a better understanding of the code developers use to write comments in the code so that the other person reading it can easily understand what the function is doing. But this is not a convenient way for large Python packages, modules, and functions. So Python structured document also called docstring provides a convenient way to document the public Python packages, functions, and methods to describe what the function does. You can use ‘’’’ triple single quote’’’ or “”” triple-double quote””” to write docstring in Python.

Example

In the below example, we created a function calculate_area for calculating the area of the rectangle given its length and width. The doc string is enclosed in triple quotes which describe the function, the parameters of the function, and the return type of the function.

def calc_area(len, wid):
    """
   Given the length and width of the rectangle , calculate its area.
   Parameters:
      len (float): The length of the rectangle.
      wid (float): The width of the rectangle.
   Returns:
      Float: Reactangle calculated area.
   """
   area = length * width
   return area

We can access the docstring using help function as follows −

print(help(calculate_area))
print(calculate_area.__doc__)

Output

Help on function calc_area in module __main__:

calc_area(len, wid)
   Given the length and width of the rectangle , calculate its area.
   Parameters:
      len (float): The length of the rectangle.
      wid (float): The width of the rectangle.
   Returns:
      Float: Reactangle calculated area.

None

   Given the length and width of the rectangle , calculate its area.
   Parameters:
      len (float): The length of the rectangle.
      wid (float): The width of the rectangle.
   Returns:
      Float: Reactangle calculated area.

Setting Access to Attributes

Attributes are properties of the object defined inside the class and are used by the object to get, set, or update values to it and the methods associated with them. Python provides some inbuilt functions to access and manipulate the attributes of the class.

  • getattr() − This function is used to get or access the value of the attribute .

  • setattr() − This function is used to update the value of the attribute.

  • hasattr() − This function is used to check if the attribute exists or not.

  • delattr() − This function is used to delete an attribute.

Example

In the below example, we set the access for the class Car attributes using getter and setter functions as follows −

class Car:
   def __init__(self, car_company, car_model):
      self.car_company = car_company
      self.car_model = car_model

car1 = Car('Toyota', 'Corolla')

# setting attribute value using setattr
setattr(car1, 'color', 'red')

# getting attribute value using getattr
print(getattr(car1, 'car_company'))    # output: Toyota

# checking attribute existence using hasattr
print(hasattr(car1, 'car_model'))    # output: True
print(hasattr(car1, 'year'))    # output: False

# deleting attribute using delattr
delattr(car1, 'color')
print(hasattr(car1, 'color'))    # output: False

Output

Toyota
True
False
False

Use Abstract Classes

Abstract class provides a common interface for the implementation of a component. The methods created in abstract classes can be used in any child class created from the abstract class. Abstract class reduces the code for the developer and makes the code more maintainable.

Example

In the below example we create an abstract class called Animal with the method make_sound and all other classes that inherit from it should implement the make_sound method.

try:
   from abc import ABC, abstractmethod
except ImportError:
   # Python 2.x fallback
   class ABCMeta(type):
      def __new__(cls, name, bases, namespace):
         return type.__new__(cls, name, bases, dict(namespace))

   class ABC(object):
      __metaclass__ = ABCMeta

   def abstractmethod(method):
      method.__isabstractmethod__ = True
      return method

class Animal(ABC):
   @abstractmethod
   def make_sound(self):
      pass

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

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

# creating objects
cat = Cat()
dog = Dog()

# calling methods
cat.make_sound()    # output: Meow
dog.make_sound()    # output: Woof

Output

Meow
Woof

Pep8 Guidelines

In 2001 Guido van Rossum, Barry Warsaw, and Nick Coghlan created some coding conventions which must be taken care of while creating Python packages. These coding conventions are called Python Enterprise proposal or PEP guidelines. Apart from code quality, other factors should also be taken care of while creating Python packages. All these factors are mentioned in the PEP guidelines. Some of the important factors are −

  • Each line of code should be limited to 80 characters

  • Import all libraries required in the beginning of the code.

  • Do not use redundant variables inside the code.

Example

In the below example, we created a calculate_area method in two different ways. The good example is implemented using pep8 guidelines while the Bad example does not follow the pep8 guidelines.

# Good Example

def calc_area(wid, ht):
   """Calculate the area of a rectangle."""
   calculated_area = wid * ht
   return calculated_area 

# Bad Example

def Calc_Area(Wid, Ht):
   calculated_area=Wid*Ht
   return calculated_area


# PEP 8 naming conventions
print(calc_area.__name__)   
print(Calc_Area.__name__)

Output

calc_area
Calc_Area

Conclusion

In this article, we discussed various tips for object-oriented programming with suitable examples. By following the tips we discussed in this article we can write object-oriented code that is well-organized, modular, and easy to maintain.

Updated on: 17-Apr-2023

587 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements