Object-oriented Programming in Python

You must know the power of Object-oriented programming if you have ever worked with object-oriented languages like Java, C#, and much more. Python also supports object-oriented programming, and we can define a class in Python. Let’s explore more about how to achieve this in Python.

In the post Getting Started with Python, I have covered the essentials required before becoming a data scientist. In this post, I will continue with a further depth of topics for object-oriented programming with Python.

However, before moving on to the actual steps of doing object-oriented programming in Python, let’s discuss some of the attributes of Python to do so.

Functions in Python

Built-in Functions

Functions are reusable code that we need to execute repeatably. Python has some built-in functions as follows:

1. print: As obvious from the name, the “print” function is to print the results.

print('Hello World');

2. ‘len’: The ‘len’ function determines the variable’s length.

var1=[1,2,3,4]; 
print(len(var1))

Some other useful built-in functions that we usually use to solve data-related problems are:

sorted: We use the “sorted” function to sort the object in order. It takes two arguments, as shown below:

arr = [11, 23, 45, 9, 45.8, 9, 13] 
print(sorted(arr, reverse = False))

# Output
[9, 9, 11, 13, 23, 45, 45.8]

print(sorted(arr, reverse = True))

# Output
[45.8, 45, 23, 13, 11, 9, 9]

upper and count string functions: We can perform these functions on a string. To know the complete list of string functions, you can type “help(str)”.

string = "learning python"

print('String to upper case: ' +string.upper()) 

print(string.count('n'))

#output:
String to upper case: LEARNING PYTHON 
3

Creating functions in Python

Apart from using the built-in functions, we can create our own functions in Python as we can in other programming languages.

To declare a function, we use a “def” keyword. An example is as follows:

def sum_number(x,y):
 z=x+y
 return z

Now, we can call a function in the code as follows:

result = sum_number(4,6) 
print(result)

We can even pass the default value for the variable. Refer to the code shown below:

def sum_number(x,y=0):
 z=x+y
 return z

So now we can call a function as follows:

result = sum_number(4) 
print(result) # 4

result = sum_number(4, 6)
print(result) # 10

Another way to call the “sum_number” function is as shown below:

result = sum_number(x=4, y=6)

This will make it clearer and is good practice while coding.

What if we don’t know how many arguments the function has?

def employee(name, *args):
 print(name)
 print(args)

Now, we can call the above function as 

employee("Mark", 123, 3.14, "Rohit") 
#or
employee("Mark", 123)

Now, let’s see how we can use tuples in functions. To demonstrate, I will be writing a function that returns a tuple. Below is the code for that:

def sayHi(name, message):
 fName = name.capitalize()
 fmessage = message + '!!!'
 tupple_result = (fName, fmessage)
 return tupple_result

name, message = sayHi('python', 'Welcome to python course') print(name) # Python
print(message) # Welcome to python course!!!

You would have noticed in the code above that we are creating a tuple “tupple_result“, which we are returning from the function. Then, when we call a function, we store the result in separate variables: name and message.

Keyword Arguments in Python

Keyword arguments are used when we are unsure how many arguments a function may require. To understand it, consider an example shown below:

def employee(name, **kwargs):
 print(name)
 print(kwargs["id"], kwagrs["name"])

And we can call a function as:

employee("Mark", id=123, name="Rohit")

Accepting Input in Python Function

We can accept input simply by using the Python built-in Input function as shown below:

name = input('Enter employee name') 
print(name)

Nested Function in Python

We can even nest a function within the existing function. The nested function has access to variables defined in the nested function. Please refer to the code shown below to declare nested functions:

def declare():
 name= 'Python'

def getName():
 print(name)

Files in Python

We can perform file-related operations in Python. For example, we can perform operations like writing data, reading data from files, etc. Refer to the example shown below:

f = open('employee.txt', a);

We have used the “open” function to read the file, where ‘a’ stands for append. There are some other access modes in Python. These are:

  1. ‘w’ – write
  2. ‘r’ – read
  3. ‘rb’ – read in binary mode
  4. ‘wb’ – write in binary mode

Lambda Function

This is an anonymous function that doesn’t have any names. It doesn’t have a “def” keyword.

result = lambda x: x+10

Which is equal to:

def result(x):
 x=x+10

We can call the lambda function as follows:

result(10)

Creating Object-Oriented Code in Python

The steps to do object-oriented programming in Python are similar to those in other languages. These steps are as follows:

  1. Creating class
  2. Constructors to initialize the attributes of a class
  3. Defining variables
  4. Inheritances

Creating class

Creating a class in Python has a very similar structure to any other programming language. Below is the syntax to create a class in Python.

class Employee:
  def EmployeeName(self, name):
    print(name)

emp = Employee()
emp.EmployeeName('Python')

NOTE: Here, we pass self-argument to function, which must be the first Python argument. Don’t be confused. Try to think of this keyword as any other programming language.

Constructors to Initialize

The constructor method in Python is defined as “__init__()”. Let’s try to replace the above function method as a constructor method. This is used to initialize the attributes of a class. When an object is created, the constructor is automatically invoked.

class Employee:
  def __init__(self, name):
    print(name)

emp = Employee("Python")

Let’s explore more about the “Self” keyword. It is a keyword that is aware of all the properties in the class. Confused? Let’s understand with the help of an example:

class Employee:
  def setName(self, name):
    self.name = name

def getName(self):
 print(self.name)

emp = Employee()
emp.setName('Python')
emp.getName()

In the example shown above, I am using setters and getters to set employee names. The employee name is set in “self.name”. So, this means that now the Employee class is aware that the name property exists in the class and can be used anywhere in the class as I am using here in a “getName” method.

Global and Other Variables

We can also define variables that do not change with the instances. Refer to the example shown below:

class Employee:
 company = "Test co.in"

 def __init__(self, name):
 print(name +' ' + self.company)

emp = Employee("Python")
emp = Employee("Employee")

print(Employee.company)

#Output

Python Test co.in
Employee Test co.in
Test co.in

In the code shown above, we have created two instances of the Employee class, but you will notice that the output company remains the same.

Also, you might have noticed that we can even directly access the company without creating the class instance.

However, Global variables in Python are declared outside the function or in the global scope. They can be used by any function in the code, making them “global.”

Additional Notes about Variable

  • Instance Variables: These are defined within methods and are used only by those methods. Their scope is limited to the instance they are part of.
  • Class Variables: Variables that are shared by all instances of a class. They are defined within the class but outside any method.
  • Local Variables: Defined inside a method and can only be used in that method.
  • Global Variables: These can be dangerous in OOP because they can be accessed and modified anywhere in the program, potentially leading to confusing and hard-to-debug code.

Here’s a simple guideline:

  • Prefer instance variables for data unique to an instance.
  • Use class variables for attributes and methods shared by all instances.
  • Global variables should be used sparingly and preferably avoided within the OOP paradigm to maintain encapsulation and minimize unexpected alterations.

Practicing these concepts in various scenarios will help solidify your understanding and effective use of constructors and variables in Python OOP.

Inheritance

Inheritance is one of the core concepts of object-oriented programming (OOP) and plays a crucial role in code reusability. In Python, inheritance allows a class (derived or child class) to inherit attributes and behaviors (methods) from another class (base or parent class).

Here’s a basic structure of how inheritance fits into OOP in Python:

Understanding Inheritance

1. Base Class (Parent Class)

The class whose properties and methods are inherited by another class.

2. Derived Class (Child Class)

The class that inherits properties and methods from another class.

When learning object-oriented programming, it is essential to know how inheritance works. So, let’s dig into it to learn how inheritance works in Python.

Suppose we have the Employee class that we defined earlier. But now we need to have the “EmployeeCompany” class to find out where the employee works.

That means we must inherit the Employee class in the “EmployeeCompany” class.

class Employee:
 company = "Test co.in"
 def __init__(self, name):
  print(name +' ' + self.company)

 def get_company(self):
  print(self.company)

class EmployeeCompany(Employee):
 company = "Example co.in"

 def get_company(self):
  print(self.company)

emp = EmployeeCompany("Python")
emp.get_company()

emp = Employee("Python")
emp.get_company()

NOTE: In the above example, Employee is the parent class, and “EmployeeCompany” is the derived class.

A lot of code!!! Let’s understand it chunk by chunk.

  1. In the code shown above, I can call the Employee constructor despite creating an instance of “EmployeeCompany”. The reason is clear that class is inherited.
  2. I am also able to overwrite the methods in two classes. You can notice that I have the “get_company” method defined in both classes. If I call the “get_company” method by creating an instance of the “EmployeeCompany” class, the “EmployeeCompany” class “get_company” method will be executed.
  3. And if I call the “get_company” method by creating an instance of Employee class the Employee class “get_company”, the method will be executed.

Importance of Inheritance in OOP

  • Reusability: Promotes code reusability by allowing a class to use methods and attributes of an existing class.
  • Extensibility: This enables you to add more features to a class without modifying it by creating a derived class.
  • Maintainability: Enhances code organization and readability, making managing and updating the codebase easier.
  • Polymorphism: Allows the use of a single type entity (method, object, etc.) to represent different types in different scenarios.

Inheritance is a powerful feature that helps create a hierarchy of classes. It can lead to cleaner, more readable, and maintainable code in object-oriented programming in Python and other OOP-supported languages when used wisely.

super() keyword

We also have a super keyword we can call methods of a parent class. Let’s find out how with the help of the example shown below:

class Employee:
 company = "Test co.in"
 def __init__(self, name):
  print(name +' ' + self.company)

 def get_company(self):
  print(self.company)

class EmployeeCompany(Employee):
 company = "Example co.in"

 def get_company(self):
  super().get_company()
  print(self.company + " From EmployeeCompany class")

emp = EmployeeCompany("Python")
emp.get_company()

In the example shown above, you will notice that in the “EmployeeCompany” class, the “get_company” method is a super keyword. It will call the “get_company” method of the Employee class. So, the output from the above code will be:

#Output
Python Example co.in
Example co.in
Example co.in From EmployeeCompany class

Modules in Python

We can break our code among various files.

For example:

  1. Our Employee class is in the ’employee.py’ file
  2. Our EmployeeCompany class is in the ’employeeCompany.py’ file

And we have our main.py file having the following lines of code:

emp = EmployeeCompany("Python")
emp.get_company()

If we run the code, we will get an error because the ‘main.py’ file doesn’t know about EmployeeCompany class. We can fix this by using an import statement as shown below:

from employeeCompany import EmployeeCompany

Adding the above line at the top of the main.py file will fix the issue.

Additional tips

How do you convert Python code into an executable file?

The answer is ‘pyinstaller’.

To install the ‘pyinstaller‘, run the following command:

pip3 install pyinstaller

NOTE: pip or pip3 is a package manager for Python.

To create an executable (.exe) file:

pyinstaller <rootfile.py>

That’s it! It will generate an ‘exe’ file. But you will notice that there will be a lot of files that will be generated in the dist folder. 

To generate a single executable file, we can pass ‘–onefile’ flag to ‘pyinstaller’ as shown below:

pyinstaller --onefile <rootfile.py>

Now we will find only one executable file in the dist folder.

For more info about ‘pyinstaller’ go through pyinstaller documentation.

We don’t ship our code as an ‘exe’ file. Right? We need users to install our application to use it. We need a setup wizard for the application to be installed on pc. Let’s see how we can generate a user setup wizard to install the application.

‘innosetup’ is used for windows to generate a setup manager. ‘innosetup’ is self-explanatory as it walks us through various steps to generate a setup file. Please go through the documentation if you have any questions.

I hope this guide was useful for Python’s object-oriented programming (OOP) concepts.

Tavish lives in Hyderabad, India, and works as a result-oriented data scientist specializing in improving the major key performance business indicators. 

He understands how data can be used for business excellence and is a focused learner who enjoys sharing knowledge.

Need help?

Let us know about your question or problem and we will reach out to you.