An object in Python(and in general OOP) is a collection of data, and the behavior associated with the different aspects of the object.
Object-Oriented Programming(OOP) refers to, objects being the primary focus while programming and modeling all real-world entities as objects.
So when we analyze a problem, we need to identify 2 things-
Class is the blueprint of an object. Class is the definition we write for building an object.
Let’s see how can we define a class in Python-
Define a Class
Here is the minimum example of a class that we can define. This is a class that represents a “Product”, and currently there is no property or behavior defined.
class Product:
pass
PythonLet’s add some behavior to the product.
Here we have defined a method named “someMethod” that is responsible for doing something.
class Product:
def someMethod(self):
print("Some method called from the Product class")
my_product = Product()
my_product.someMethod()
print("Type: ", type(my_product))
print("Object id: ", id(my_product))
PythonOutput:
Some method called from the Product class
Type: <class '__main__.Product'>
Object id: 140079743073296
Plaintext__init__
class Product:
def __init__(self):
print("Product class initiated")
def someMethod(self):
print("Some method called from the Product class")
my_product = Product()
my_product.someMethod()
PythonOutput:
Product class initiated
Some method called from the Product class
PlaintextWARNING
We dont need to pass the “self” parameter value while calling any method. Python will internally pass the “self” param value.
Attributes
Class attributes are used to store data inside an object. We can set an attribute to an object, just by using a dot notation.
class Product:
pass
product = Product()
# Print attributes
print("Attributes after instantiation:", vars(product))
product.name = "BigBox Pro"
product.price = 99.99
print(product.name)
print(product.price)
# Print attributes
print("Attributes after setting:", vars(product))
PythonNOTE
It is not mandatory to declare the attributes in the class. We can set any attribute to an object after the object is created.
There are ways to restrict this behavior, which we have discussed later.
Output:
The values of the attributes are set, and we can access them after setting them.
Attributes after instantiation: {}
BigBox Pro
99.99
Attributes after setting: {'name': 'BigBox Pro', 'price': 99.99}
PlaintextWe can also access those attributes inside a class by using dot(.) notation on self.
class Product:
def printDetails(self):
print(f"Name: {self.name}") # this will fail if we do not set name attribute
print(f"Price: {self.price}") # this will fail if we do not set price attribute
product = Product()
product.name = "BigBox Pro"
product.price = 99.99
product.printDetails()
PythonOutput:
Name: BigBox Pro
Price: 99.99
PlaintextThe problem with setting attributes this way, is we don’t have full track of the attributes, and an attribute might or might not be set.
This behavior can be restricted by declaring which attributes would the object accept, by setting “__slots__” property. We just need to assign an array of the property name(as string), to the “__slots__”.
class Product:
__slots__ = ['name', 'price']
def printDetails(self):
print(f"Name: {self.name}")
print(f"Price: {self.price}")
product = Product()
product.name = "BigBox Pro"
product.price = 99.99
product.discount = 9.5
product.printDetails()
PythonOutput:
We will not be able to set any attribute other than “name” and “price”, as we have declared only these attributes in the “__slots__”.
Setting any other attribute(like “discount” in this case) will throw an “AttributeError”.
Traceback (most recent call last):
File "oop_example.py", line 12, in <module>
product.discount = 9.5
AttributeError: 'Product' object has no attribute 'discount'
PlaintextWe can initiate the values in the __init__ method. Here we have 2 instance attributes “name” and “price”. We use these to store information related to the object.
class Product:
def __init__(self)-> None:
self.name = "Default product name"
self.price = 10
product = Product()
# Print attributes
print("Attributes after instantiation:", vars(product))
product.name = "BigBox Pro"
product.price = 99.99
print(product.name)
print(product.price)
# Print attributes
print("Attributes after setting:", vars(product))
PythonOutput:
Attributes after instantiation: {'name': 'Default product name', 'price': 10}
BigBox Pro
99.99
Attributes after setting: {'name': 'BigBox Pro', 'price': 99.99}
PlaintextWe can also pass the values to __init__ to set the attributes.
class Product:
def __init__(self, name: str, price: float) -> None:
self.name = name
self.price = price
def printDetails(self) -> None:
print("Name:", self.name)
print("Price:", self.price)
my_product = Product("Sonic Wireless Headphone", 99.99)
my_product.printDetails()
PythonOutput:
Name: Sonic Wireless Headphone
Price: 99.99
PlaintextWe can also define class attributes, which are available in every object. These class attributes are shared in all objects of the class.
class Product:
# Class attribute
# Shared for all object of the class
obj_count: int = 0
def __init__(self, name: str, price: float = 0):
self.name = name
self.price = price
Product.obj_count += 1
product = Product("BigBox Pro", 99.99)
print(f'Name: {product.name}')
print(f'Price: {product.price}')
print(f'Product Object Count: {Product.obj_count}')
product2 = Product("Second product", 10)
print(f'Name: {product2.name}')
print(f'Price: {product2.price}')
print(f'Product Object Count: {Product.obj_count}')
PythonOutput:
Name: BigBox Pro
Price: 99.99
Product Object Count: 1
Name: Second product
Price: 10
Product Object Count: 2
PlaintextMethods
We have already seen the usage of methods. Methods are used to perform some tasks.
The method may or may not accept any parameter.
NOTE
Python supplies the first parameter of a method in the class, and the first parameter is a reference to the object(referenced by self) itself.
You can use any name in place of “self” but by convension “self” is used everywhere.
Object Comparison
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
# Example usage:
circle1 = Circle(5)
circle2 = Circle(10)
circle3 = Circle(5)
print(circle1 == circle2)
print(circle1 == circle3)
PythonOutput:
False
False
PlaintextMethod | Comparison | Description |
---|---|---|
__eq__(self, other) | obj1 == obj2 | equal to |
__ne__(self, other) | obj1 != obj2 | not equal to |
__gt__(self, other) | obj1 > obj2 | greater than |
__ge__(self, other) | obj1 >= obj2 | greater than or equal to |
__lt__(self, other) | obj1 < obj2 | less than |
__le__(self, other) | obj1 <= obj2 | less than or equal to |
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
# Check if equal
def __eq__(self, other):
return self.radius == other.radius
# Check if not equal
def __ne__(self, other):
return self.radius != other.radius
# Check if less than
def __lt__(self, other):
return self.radius < other.radius
# Check if less than or equal
def __le__(self, other):
return self.radius <= other.radius
# Check if greater than
def __gt__(self, other):
return self.radius > other.radius
# Check if greater than or equal
def __ge__(self, other):
return self.radius >= other.radius
# Example usage:
circle1 = Circle(5)
circle2 = Circle(10)
circle3 = Circle(5)
print(circle1 == circle2)
print(circle1 == circle3)
print(circle1 != circle2)
print(circle1 < circle2)
print(circle1 <= circle2)
print(circle1 > circle2)
print(circle1 >= circle2)
PythonOutput:
False
True
True
True
True
False
False
PlaintextObject to String
Define method __str__() for the string representation of the object. Whenever we try to print the object the __str__() will be called.
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
def __str__(self):
return f"Circle with radius {self.radius}, has area {self.area():.2f}"
# Example usage:
circle1 = Circle(5)
circle2 = Circle(10)
print(circle1)
print(circle2)
PythonOutput:
Circle with radius 5, has area 78.54
Circle with radius 10, has area 314.16
PlaintextOr we can use __repr__() to represent the object. Purpose of __repr__ is to be clear, and generally used for debugging.
Use function repr() to print the output of __repr__().
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
def __str__(self):
return f"Circle with radius {self.radius}, has area {self.area():.2f}"
def __repr__(self):
return f"Circle({self.radius})\nArea: {self.area():.2f}"
# Example usage:
circle1 = Circle(5)
print(repr(circle1))
PythonOutput:
Circle(5)
Area: 78.54
Plaintext