Python OOPS Tutorial (Harry)
Introduction to OOPs in Python | Python Tutorial - Day #56
Section titled “Introduction to OOPs in Python | Python Tutorial - Day #56”Introduction to Object-Oriented Programming in Python: In programming languages, mainly there are two approaches that are used to write program or code.
- 1). Procedural Programming
- 2). Object-Oriented Programming
The procedure we are following till now is the “Procedural Programming” approach. So, in this session, we will learn about Object Oriented Programming (OOP). The basic idea of object-oriented programming (OOP) in Python is to use classes and objects to represent real-world concepts and entities.
A class is a blueprint or template for creating objects. It defines the properties and methods that an object of that class will have. Properties are the data or state of an object, and methods are the actions or behaviors that an object can perform.
An object is an instance of a class, and it contains its own data and methods. For example, you could create a class called “Person” that has properties such as name and age, and methods such as speak() and walk(). Each instance of the Person class would be a unique object with its own name and age, but they would all have the same methods to speak and walk.
- Encapsulation is the practice of restricting direct access to an object’s data and allowing modifications only through methods. This protects data integrity and prevents unintended modifications. Example: using private variables (
_variableor__variable) and getters/setters. - Abstraction is the concept of hiding complex implementation details and exposing only the necessary functionalities. This helps in reducing complexity and increasing code usability. Example: using abstract classes and methods with the
ABCmodule in Python. - Inheritance allows new classes to be created that inherit the properties and methods of an existing class. This allows for code reuse and makes it easy to create new classes that have similar functionality to existing classes.
- Polymorphism is also supported in Python, which means that objects of different classes can be treated as if they were objects of a common class. This allows for greater flexibility in code and makes it easier to write code that can work with multiple types of objects.
In summary, OOP in Python allows developers to model real-world concepts and entities using classes and objects, encapsulate data, reuse code through inheritance, and write more flexible code through polymorphism.
Classes and Objects in Python | Python Tutorial - Day #57
Section titled “Classes and Objects in Python | Python Tutorial - Day #57”Python Class and Objects
Section titled “Python Class and Objects”A class is a blueprint or a template for creating objects, providing initial values for state (member variables or attributes), and implementations of behavior (member functions or methods). The user-defined objects are created using the class keyword.
Creating a Class:
Let us now create a class using the class keyword.
Example:
class Details: name = "Rohan" age = 20Creating an Object:
Object is the instance of the class used to access the properties of the class Now lets create an object of the class.
Example:
obj1 = Details()Now we can print values:
Example:
class Details: name = "Rohan" age = 20
obj1 = Details()print(obj1.name)print(obj1.age)Output:
Rohan20Instance Method & self parameter
Section titled “Instance Method & self parameter”The self parameter is a reference to the current instance of the class, and is used to access variables that belongs to the class.
- It must be provided as the extra parameter inside the method definition. ⭐
Example:
class Details: name = "Rohan" age = 20
def desc(self): print("My name is", self.name, "and I'm", self.age, "years old.")
obj1 = Details()obj1.desc()Output:
My name is Rohan and I'm 20 years old.Creating Attributes and Methods Dynamically (i.e. after object is created)
1. To Class
class Details: pass # Empty class
# Assigning variable to classDetails.name = "gaurav"
# Assigning function to classdef greet(self): print("hi")
Details.greet = greet2. To Object
class Details: pass
obj1 = Details()
# Assigning variable to objectobj1.name = "gaurav"
# Assigning function to objectimport typesdef greet(self): print("hi")
obj1.greet = types.MethodType(greet, obj1)- Use
types.MethodTypeto bind the function properly to the object.
Constructors in Python | Python Tutorial - Day #58
Section titled “Constructors in Python | Python Tutorial - Day #58”Constructors
Section titled “Constructors”A constructor is a special method in a class used to create and initialize an object of a class. There are different types of constructors. Constructor is invoked automatically when an object of a class is created.
- constructor -> A unique function that gets called automatically when an object is created of a class.
- The main purpose of a constructor is to initialize or assign values to the data members of that class. It cannot return any value other than None.
Syntax of Python Constructor
def __init__(self): # initializations__init__ is one of the reserved functions in Python. In Object Oriented Programming, it is known as a constructor.
Types of Constructors in Python
- Parameterized Constructor
- Default Constructor
1. Parameterized Constructor in Python
When the constructor accepts arguments along with self, it is known as parameterized constructor.
These arguments can be used inside the class to assign the values to the data members.
Example:
class Details: def __init__(self, animal, group): self.animal = animal self.group = group
obj1 = Details("Crab", "Crustaceans")print(obj1.animal, "belongs to the", obj1.group, "group.")Output:
Crab belongs to the Crustaceans group2. Default Constructor in Python
When the constructor doesn’t accept any arguments from the object and has only one argument, self, in the constructor, it is known as a Default constructor.
Example:
class Details: def __init__(self): print("animal Crab belongs to Crustaceans group")obj1=Details()Output:
animal Crab belongs to Crustaceans groupNote: - self refers to the current object, and is required to write in argument during member function definition, Even if you don’t use self inside the method. Because it Required in instance methods and Python automatically passes the object when you call obj.method(). It must be present to match the call.
def hello(): # Not write 'self' in argument -> Give error ❌ during Call print("Hello")
obj.hello() # Internally -> Person.hello(obj)Output:
❌ TypeError: hello() takes 0 positional arguments but 1 was givenDecorators in Python | Python Tutorial - Day #59
Section titled “Decorators in Python | Python Tutorial - Day #59”Decorators in Python
Section titled “Decorators in Python”Decorators -> A function that takes another function as an argument, modifies it, and returns the modified function.
Learning From Example
Example: defining a function greet() for Adding Greetings Before and After a Function
def greet(fx): # Takes a function as an argument def mfx(): # Modified function print("Good Morning") fx() # Call the original function print("Tata!") return mfx # Return the modified function1. Using greet() on a Defined Function
def hello(): print("Hello world")
greet(hello)() # Call with greet()Good MorningHello World # Main LogicTata!2. Using greet() as a decorator ⭐ by adding @greet before the function definition
@greetdef hello(): # greet() already attach to it print("Hello world")
hello() # greet() applied automatically whenever it is calledGood MorningHello World # Main LogicTata!Note:
- Decorators are not hoisted like regular Python functions, its mean the decorator must be defined before it is used.
- The
@decoratorsyntax is a cleaner way to apply a decorator instead of manually calling it.
*args and **kwargs in Python
Section titled “*args and **kwargs in Python”In Python, *args and **kwargs allow a function to accept any number of positional and keyword arguments.
A simple decorator works normally if the function it decorates does not take arguments -> Works fine ✅
greet(hello)() # Call with greet()Using greet() as a decorator @greet during function definition (Even if functions take argument) -> Works fine ✅
@greetdef add(a, b): print(a+b)
add()good Morning3Tata!But using greet() on a Defined Function that requires arguments -> Error ❌
def add(a,b): print(a+b)
greet(add)(1, 2)TypeError: mfx() takes 0 positional argument but 2 were givenWhy?
- The inner function
mfx()insidegreet()does not accept arguments, butadd(a, b)requires two arguments.
Solution: Modify greet() to Accept Arguments
modify mfx() to accept any number of arguments using *args and **kwargs. -> Corrected ✅
def greet(fx): def mfx(*args, **kwargs): # Modify to take All arguments print("Good Morning") fx(*args, **kwargs) # Pass the Arguments to Actual function print("Tata!") return mfxNow, it works with functions that take arguments:
def add(a,b): print(a+b)
greet(add)(1, 2)good Morning3Tata!*args-> Collects all positional arguments as a tuple (type:tuple)**kwargs-> Collects all keyword arguments as a dictionary (type:dict)- Real use cases: Logging, authentication, performance tracking, etc.
Theory Notes
Python decorators are a powerful and versatile tool that allow you to modify the behavior of functions and methods. They are a way to extend the functionality of a function or method without modifying its source code.
Decorator -> a function that takes another function as an argument and returns a new function that modifies the behavior of the original function. The new function is often referred to as a “decorated” function. The basic syntax for using a decorator is the following:
@decorator_functiondef my_function(): passThe @decorator_function notation is just a shorthand for the following code:
def my_function(): passmy_function = decorator_function(my_function)Decorators are often used to add functionality to functions and methods, such as logging, memoization, and access control.
Practical use case
- One common use of decorators is to add logging to a function. For example, you could use a decorator to log the arguments and return value of a function each time it is called:
import logging
def log_function_call(func): def decorated(*args, **kwargs): logging.info(f"Calling {func.__name__} with args={args}, kwargs={kwargs}") result = func(*args, **kwargs) logging.info(f"{func.__name__} returned {result}") return result return decorated
@log_function_calldef my_function(a, b): return a + b- In this example, the
log_function_calldecorator takes a function as an argument and returns a new function that logs the function call before and after the original function is called.
- In Flask
routedecorator binds the URL'/'to this function
@app.route('/')def home(): return 'Welcome to the Homepage'- the above code is equivalent to
def home(): return 'Welcome to the Homepage'app.add_url_rule('/home', view_func=home)- Flask maps the URL path
/aboutto the functionabout()usingadd_url_rule()under the hood.
Conclusion
Decorators are a powerful and flexible feature in Python that can be used to add functionality to functions and methods without modifying their source code. They are a great tool for separating concerns, reducing code duplication, and making your code more readable and maintainable.
In conclusion, python decorators are a way to extend the functionality of functions and methods, by modifying its behavior without modifying the source code. They are used for a variety of purposes, such as logging, memoization, access control, and more. They are a powerful tool that can be used to make your code more readable, maintainable, and extendable.