How to handle errors with try and except by using else and finally in Python

  • If try throws an error then except executes.
  • If try doesn’t throw an error then else part executes.
  • The finally block executes always. Error doesn’t matter here.
while True:
    try:
        age = int(input("Enter the age to check eligibility : "))
        break
    except ValueError:
        print("Please enter an integer")
    except:
        print("Unexpected error")
    else:
        print(f"User has entered {age}")
    finally:
        print("This is a program to verify age")

if age>18:
    print("You are eligible to play")
else:
    print("You aren't eligible to play")

Raising Errors with Python and understanding the concept of Abstract Methods

  • There are no abstract methods in Python.
  • The name came from JAVA Programming Language. But we can raise error to implement that kind of methods in Python too.
class Phone:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def cameraa(self):
        raise NotImplementedError("This method must be defined in each and every subclass")

class SmartPhone(Phone):
    def __init__(self, brand, model, camera):
        super().__init__(brand, model)
        self.camera = camera

class FlagshipPhone(Phone):
    def __init__(self, brand, model, front_camera, rear_camera):
        super().__init__(brand, model)
        self.front_camera = front_camera
        self.rear_camera = rear_camera

S = SmartPhone("POCO", "X2", "64MP")
print(S.cameraa())

OUTPUT :

Traceback (most recent call last):
  File "c:/Users/dsaya/OneDrive/Desktop/PythonPractise/OOP/RaiseError.py", line 21, in <module>
    print(S.cameraa())
  File "c:/Users/dsaya/OneDrive/Desktop/PythonPractise/OOP/RaiseError.py", line 7, in cameraa  
    raise NotImplementedError("This method must be defined in each and every subclass")        
NotImplementedError: This method must be defined in each and every subclass

Dunder methods, Operator Overloading, Polymorphism in Python

class Phone:
    def __init__(self, brand, model_name, price):
        self.brand = brand
        self.model_name = model_name
        self.price = price
        
    def full_name(self):
        return f"This phone is {self.brand} {self.model_name}"
    
    def make_a_call(self, number):
        return f"Dialling {number}..."

class Smartphone(Phone):
    def __init__(self, brand, model_name, price, ram, internal_memory, rear_camera):
        super().__init__(brand, model_name, price)
        # Phone.__init__(self, brand, model_name, price)
        self.ram = ram
        self.internal_memory = internal_memory
        self.rear_camera = rear_camera

    def full_name(self):
        return f"This phone is {self.brand} {self.model_name} and it has a rear camera of {self.rear_camera}"

class Flagshipphone(Smartphone):
    def __init__(self, brand, model_name, price, ram, internal_memory, rear_camera, front_camera):
        super().__init__(brand, model_name, price, ram, internal_memory, rear_camera)
        self.front_camera = front_camera

    def full_name(self):
        return f"This phone is {self.brand} {self.model_name} and it has a front camera of {self.front_camera}"

    def __str__(self):
        return f"{self.brand} {self.model_name}"

    def __repr__(self):
        return f"{self.brand} {self.model_name}" 

    def __len__(self):
        return len(self.full_name())

    def __add__(self, other):
        return int(self.price) + int(other.price)



S1 = Flagshipphone("OnePlus", "8 Pro", "75000", "8GB", "256GB", "64MP", "32MP")
S2 = Flagshipphone("OnePlus", "7 Pro", "76000", "8GB", "256GB", "64MP", "32MP")
# S2 = Flagshipphone("OnePlus", "8 Pro", "75000", "8GB", "256GB", "64MP", "32MP")
# print(S2.full_name())
# print(help(Smartphone))
# print(S1.__repr__())
# print(str(S1))
print(S1.__len__())
print(S1 + S2)

Multiple Inheritance in Python

  • This is something that is mostly not used by developers due to the confusion it causes.
  • It has a main problem with order. In the order, which comes first will be called first.
  • As in the example below, if we want to print the hello method of Class B, we need to inherit the B class first from the C class.
class A:
    def class_a_method(self):
        return "This is a method of Class A"
    
    def hello(self):
        return "Returning from Class A"

class B:
    def class_a_method(self):
        return "This is a method of Class A"
    
    def hello(self):
        return "Returning from Class B"

class C(A, B):
    pass

objC = C()

print(objC.hello())

print((help(C))

OUTPUT:

Returning from Class A
Help on class C in module __main__:

class C(A, B)
 |  Method resolution order:
 |      C
 |      A
 |      B
 |      builtins.object
 |
 |  Methods inherited from A:
 |
 |  class_a_method(self)
-- More  --

Checking Method Resolution Order (MRO) in Python

class Phone:
    def __init__(self, brand, model_name, price):
        self.brand = brand
        self.model_name = model_name
        self.price = price
        
    def full_name(self):
        return f"This phone is {self.brand} {self.model_name}"
    
    def make_a_call(self, number):
        return f"Dialling {number}..."

class Smartphone(Phone):
    def __init__(self, brand, model_name, price, ram, internal_memory, rear_camera):
        super().__init__(brand, model_name, price)
        # Phone.__init__(self, brand, model_name, price)
        self.ram = ram
        self.internal_memory = internal_memory
        self.rear_camera = rear_camera

    def full_name(self):
        return f"This phone is {self.brand} {self.model_name} and it has a rear camera of {self.rear_camera}"

class Flagshipphone(Smartphone):
    def __init__(self, brand, model_name, price, ram, internal_memory, rear_camera, front_camera):
        super().__init__(brand, model_name, price, ram, internal_memory, rear_camera)
        self.front_camera = front_camera

    def full_name(self):
        return f"This phone is {self.brand} {self.model_name} and it has a front camera of {self.front_camera}"
    
S1 = Smartphone("OnePlus", "8 Pro", "75000", "8GB", "256GB", "64MP")
S2 = Flagshipphone("OnePlus", "8 Pro", "75000", "8GB", "256GB", "64MP", "32MP")
print(S2.full_name())
print(help(Smartphone))

Output :

Help on class Smartphone in module __main__:

class Smartphone(Phone)
 |  Smartphone(brand, model_name, price, ram, internal_memory, rear_camera)
 |  
 |  Method resolution order:
 |      Smartphone
 |      Phone
 |      builtins.object
 |  
 |  Methods defined here:
 |
-- More  --

Multilevel Inheritance and Method Overriding in Python

class Phone:
    def __init__(self, brand, model_name, price):
        self.brand = brand
        self.model_name = model_name
        self.price = price
        
    def full_name(self):
        return f"This phone is {self.brand} {self.model_name}"
    
    def make_a_call(self, number):
        return f"Dialling {number}..."

class Smartphone(Phone):
    def __init__(self, brand, model_name, price, ram, internal_memory, rear_camera):
        super().__init__(brand, model_name, price)
        # Phone.__init__(self, brand, model_name, price)
        self.ram = ram
        self.internal_memory = internal_memory
        self.rear_camera = rear_camera

    def full_name(self):
        return f"This phone is {self.brand} {self.model_name} and it has a rear camera of {self.rear_camera}"

class Flagshipphone(Smartphone):
    def __init__(self, brand, model_name, price, ram, internal_memory, rear_camera, front_camera):
        super().__init__(brand, model_name, price, ram, internal_memory, rear_camera)
        self.front_camera = front_camera

    def full_name(self):
        return f"This phone is {self.brand} {self.model_name} and it has a front camera of {self.front_camera}"
    
S1 = Smartphone("OnePlus", "8 Pro", "75000", "8GB", "256GB", "64MP")
S2 = Flagshipphone("OnePlus", "8 Pro", "75000", "8GB", "256GB", "64MP", "32MP")
print(S1.full_name())
print(S2.full_name())

Inheritance in Python (2nd Method)

class Phone:
    def __init__(self, brand, model_name, price):
        self.brand = brand
        self.model_name = model_name
        self.price = price

    @property
    def full_name(self):
        return f"This phone is {self.brand} {self.model_name}"
    
    def make_a_call(self, number):
        return f"Dialling {number}..."

class Smartphone(Phone):
    def __init__(self, brand, model_name, price, ram, internal_memory, rear_camera):
        Phone.__init__(self, brand, model_name, price)
        self.ram = ram
        self.internal_memory = internal_memory
        self.rear_camera = rear_camera
    
S1 = Smartphone("OnePlus", "8 Pro", "75000", "8GB", "256GB", "64MP")

print(S1.full_name)