You are on page 1of 26

1.

Explain the role of Assertions & raising exceptions in Python with a


suitable program.
Ans.
Assertions:
Assertions are used to check whether a certain condition holds true in
your code. They are primarily used for debugging and testing. When an
assertion fails, it raises an AssertionError exception, indicating that
something is wrong with your code. Assertions are typically used to
catch programming errors, not user input errors or runtime issues.

Program:
def divide(a, b):
assert b != 0, "Division by zero is not allowed"
return a / b

numerator = 10
denominator = 0

try:
result = divide(numerator, denominator)
except AssertionError as e:
print(f"Assertion Error: {e}")
else:
print(f"Result: {result}")

Raising Exceptions:
Exceptions are used to handle errors and exceptional conditions during
the execution of your program. Unlike assertions, exceptions are not
limited to debugging purposes and are essential for robust error handling
in your code.

Program:
def divide(a, b):
if b == 0:
raise ValueError("Division by zero is not allowed")
return a / b

numerator = 10
denominator = 0

try:
result = divide(numerator, denominator)
except ValueError as e:
print(f"Value Error: {e}")
else:
print(f"Result: {result}")

2. Explain the program development concept ‘prototype and patch’


with suitable example.
Ans.
Prototype: In the initial phase, you create a rudimentary prototype of the
software. This prototype typically includes essential features and serves
as a proof of concept. It is not a fully functional or polished version of the
software but is just enough to demonstrate the core idea.

Patch: After receiving feedback from stakeholders and gaining a better


understanding of the requirements, you make incremental improvements
to the prototype. These improvements are delivered as patches or
updates. Each patch enhances the prototype by adding new features,
fixing issues, or refining existing functionality.

Prototype Phase:

Initial Prototype: Create a basic mobile app that allows users to add, view,
and delete tasks. The interface is minimal, focusing on core functionality.
Demonstration: Share the prototype with potential users, team members,
and stakeholders.
Feedback: Users and stakeholders provide feedback, suggesting
additional features such as task prioritization, due dates, and task
categories.

Patch Phase:

Patch 1: Add task prioritization (e.g., high, medium, low) to the app.
Patch 2: Implement due dates for tasks, allowing users to set deadlines.
Patch 3: Introduce task categories and the ability to organize tasks by
category.
Patch 4: Improve the user interface, incorporating feedback on usability
and aesthetics.
Continue this iterative process, adding features and addressing issues
based on feedback and evolving requirements.

3. Develop a function called Print_time that takes a time object &


print it in the form of hour:minute:second
Ans.
from datetime import time

def Print_time(input_time):
if isinstance(input_time, time):
formatted_time = input_time.strftime("%H:%M:%S")
print(formatted_time)
else:
print("Invalid input. Please provide a valid time object.")

# Example usage:
if __name__ == "__main__":
# Create a time object (hour=10, minute=30, second=45)
input_time = time(10, 30, 45)

# Print the time in the desired format


Print_time(input_time)

4. Explain the benefits of using logging module with an example


Ans.

Benefits of Using the logging Module:


1. Customizable Output: You can configure the logging module to log
messages to various outputs, such as console, files, email, or
external services. This flexibility allows you to tailor logging to your
specific needs.
2. Timestamps: The logging module automatically adds timestamps to
log messages, providing valuable information about when events
occurred.
3. Log Rotation: When writing log messages to files, you can enable
log rotation, which helps manage log file sizes and prevents them
from growing indefinitely.
4. Hierarchical Logging: You can create a hierarchical logging structure
with different loggers and handlers, allowing you to organize and
manage log messages for different parts of your application.
5. Exception Logging: The logging module can capture and log
exceptions automatically, making it easier to track down errors and
their context.
6. Integration with Libraries and Frameworks: Many third-party
libraries and frameworks use the logging module, making it easier
to integrate your application's logs with these tools.
Example Program:
import logging

# Configure the logging module


logging.basicConfig(
filename='example.log', # Log messages to a file
level=logging.INFO, # Set the logging level to INFO
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# Create a logger for your module (usually one logger per module)
logger = logging.getLogger(__name__)

def divide(a, b):


try:
result = a / b
logger.info(f"Division result: {result}")
except ZeroDivisionError:
logger.error("Division by zero occurred")

if __name__ == "__main__":
# Perform a division operation
divide(10, 2)
divide(10, 0) # This will log an error message

5. Explain operator overloading with a python program


Ans.
class ComplexNumber:
def __init__(self, real, imag):
self.real = real
self.imag = imag

def __str__(self):
if self.imag >= 0:
return f"{self.real} + {self.imag}i"
else:
return f"{self.real} - {-self.imag}i"

def __add__(self, other):


real_sum = self.real + other.real
imag_sum = self.imag + other.imag
return ComplexNumber(real_sum, imag_sum)

def __sub__(self, other):


real_diff = self.real - other.real
imag_diff = self.imag - other.imag
return ComplexNumber(real_diff, imag_diff)

def __mul__(self, other):


real_prod = (self.real * other.real) - (self.imag * other.imag)
imag_prod = (self.real * other.imag) + (self.imag * other.real)
return ComplexNumber(real_prod, imag_prod)
def __truediv__(self, other):
denominator = (other.real ** 2) + (other.imag ** 2)
real_quot = ((self.real * other.real) + (self.imag * other.imag)) /
denominator
imag_quot = ((self.imag * other.real) - (self.real * other.imag)) /
denominator
return ComplexNumber(real_quot, imag_quot)

# Create two complex numbers


c1 = ComplexNumber(3, 4)
c2 = ComplexNumber(1, 2)

# Demonstrate operator overloading


add_result = c1 + c2
sub_result = c1 - c2
mul_result = c1 * c2
div_result = c1 / c2

# Display the results


print(f"c1 + c2 = {add_result}")
print(f"c1 - c2 = {sub_result}")
print(f"c1 * c2 = {mul_result}")
print(f"c1 / c2 = {div_result}")
6. Develop a Time class with hour, min and sec as attributes.
Demonstrate how two Time objects would be added.
Ans.
class Time:
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second

def __str__(self):
return f"{self.hour:02d}:{self.minute:02d}:{self.second:02d}"

def __add__(self, other):


# Calculate the total seconds for both Time objects
total_seconds_self = self.hour * 3600 + self.minute * 60 + self.second
total_seconds_other = other.hour * 3600 + other.minute * 60 +
other.second

# Calculate the sum of total seconds


total_seconds_sum = total_seconds_self + total_seconds_other

# Calculate the new hours, minutes, and seconds


new_hour, remainder = divmod(total_seconds_sum, 3600)
new_minute, new_second = divmod(remainder, 60)
return Time(new_hour, new_minute, new_second)

# Create two Time objects


time1 = Time(10, 30, 45)
time2 = Time(4, 15, 20)

# Add the two Time objects


result_time = time1 + time2

# Display the results


print("Time 1:", time1)
print("Time 2:", time2)
print("Sum of Time 1 and Time 2:", result_time)

7. Explain polymorphism with a python program.


Ans.
class Animal:
def __init__(self, name):
self.name = name

def speak(self):
pass # This method will be overridden in the subclasses

class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"

class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"

class Duck(Animal):
def speak(self):
return f"{self.name} says Quack!"

# Create instances of different animals


dog = Dog("Buddy")
cat = Cat("Whiskers")
duck = Duck("Daffy")

# Create a list of animals


animals = [dog, cat, duck]

# Demonstrate polymorphism by calling the speak() method on each


animal
for animal in animals:
print(animal.speak())

8. Explain the methods __init__ and __str__ with suitable code


example to each.
Ans.
__init__ method:
The __init__ method is the constructor method for a class. It is
automatically called when an object of the class is created.
It is used to initialize the attributes or properties of an object.
It can take any number of parameters, but it typically initializes the
object's attributes based on the provided arguments.
The self parameter represents the instance of the class and is used to
access and modify its attributes.

Example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

person1 = Person("Alice", 30)

print(person1.name) # Output: Alice


print(person1.age) # Output: 30

__str__ method:
The __str__ method is used to define a human-readable string
representation of an object. It is called when the str() function is applied
to an object or when the print() function is used with the object.
It should return a string that represents the object in a clear and
informative way.
This method is helpful for debugging and providing a meaningful string
representation of an object.
Example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def __str__(self):
return f"Name: {self.name}, Age: {self.age}"

person2 = Person("Bob", 25)

person_str = str(person2)
print(person_str) # Output: Name: Bob, Age: 25

print(person2) # Output: Name: Bob, Age: 25

9. Explain type based despatch with an example


Ans.
Type-based dispatch, also known as method overloading or method
specialization, is a programming technique where the implementation of
a method or function is determined based on the types of its arguments
or operands.

Exaclass Shape:
def area(self):
pass

class Circle(Shape):
def __init__(self, radius):
self.radius = radius

def area(self):
return 3.14 * self.radius * self.radius

class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height

def area(self):
return self.width * self.height

def calculate_area(shape):
if isinstance(shape, Shape):
return shape.area()
else:
raise ValueError("Unsupported shape")

# Create instances of Circle and Rectangle


circle = Circle(5)
rectangle = Rectangle(4, 6)

# Calculate and print the areas using type-based dispatch


print(f"Area of Circle: {calculate_area(circle)}")
print(f"Area of Rectangle: {calculate_area(rectangle)}")mple:
10. Explain Pure functions & Modifiers with examples
Ans.
Pure Functions:
• Pure functions are functions that have the following characteristics:
• Their output (return value) is solely determined by their input
(arguments). Given the same inputs, a pure function will
always produce the same output.
• They do not have side effects, meaning they do not modify
any external state or variables.
• Pure functions are predictable, testable, and can be easily reasoned
about.
Example:
def add(a, b):
return a + b
result = add(3, 4)

Modifiers (Impure Functions/Procedures):


• Modifiers, also known as impure functions or procedures, are
functions that can:
• Produce different results for the same input.
• Modify external state or variables.
• Modifiers often have side effects, which means they can change the
state of the program or interact with the outside world.
Example:
total = 0

def add_to_total(value):
global total
total += value

add_to_total(5)
add_to_total(3)

11. Explain Shallow copy & Deep copy methods in class.


Ans.
Shallow Copy:
• A shallow copy of an object creates a new object that is a copy of
the original object, but it only duplicates the top-level structure of
the object.
• If the original object contains references to other objects (e.g., lists
or dictionaries), the shallow copy will copy those references, not
the objects themselves.
• Changes made to nested objects inside the copied object will be
reflected in both the original and the copied object.
• You can use the copy() method or the copy module's copy()
function to create a shallow copy.
Example:
import copy

original_list = [1, [2, 3], [4, 5]]

shallow_copied_list = copy.copy(original_list)

shallow_copied_list[1][0] = 99
print(original_list) # Output: [1, [99, 3], [4, 5]]
print(shallow_copied_list) # Output: [1, [99, 3], [4, 5]]

Deep Copy:
• A deep copy of an object creates a new object that is a fully
independent copy of the original object and all its nested objects.
• It recursively copies all objects referenced by the original object,
creating a new instance for each one.
• Changes made to nested objects inside the copied object do not
affect the original object, and vice versa.
• You can use the copy module's deepcopy() function to create a
deep copy.
Example:
import copy

original_list = [1, [2, 3], [4, 5]]

deep_copied_list = copy.deepcopy(original_list)

deep_copied_list[1][0] = 99

print(original_list) # Output: [1, [2, 3], [4, 5]]


print(deep_copied_list) # Output: [1, [99, 3], [4, 5]]

12. Define a class. Explain how classes & objects are created in
python
Ans.
A class is a blueprint or a template for creating objects. It defines the
structure and behavior of objects that will be created based on that class.
A class can have attributes (data members) and methods (functions) that
define the characteristics and actions of the objects it represents. Classes
provide a way to model real-world entities and their interactions in a
program, promoting code organization and reuse.

Example:
class MyClass:
# Constructor method (optional)
def __init__(self, attribute1, attribute2):
self.attribute1 = attribute1
self.attribute2 = attribute2

# Instance method
def display_attributes(self):
print(f"Attribute 1: {self.attribute1}")
print(f"Attribute 2: {self.attribute2}")

# Another instance method


def do_something(self):
print("Doing something...")

# Creating objects (instances) of the class


obj1 = MyClass(42, "Hello")
obj2 = MyClass(123, "World")

# Accessing object attributes and methods


obj1.display_attributes()
obj2.do_something()

13. Write a program to add two point objects by overloading +


operator. Overload __str__() to display point as a ordered pair
Ans.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __add__(self, other):


if isinstance(other, Point):
new_x = self.x + other.x
new_y = self.y + other.y
return Point(new_x, new_y)
else:
raise TypeError("Unsupported operand type for +: " +
str(type(other)))

def __str__(self):
return f"({self.x}, {self.y})"

# Create two Point objects


point1 = Point(2, 3)
point2 = Point(1, 4)

# Add the two points using the overloaded + operator


result = point1 + point2

# Display the result


print("Point 1:", point1)
print("Point 2:", point2)
print("Sum of Point 1 and Point 2:", result)

14. Develop a class Point representing a point on coordinate


system. Implement following functions – (i)A function read_point()
to receive x and y attributes of a Point object as user input. (ii)A
function distance() which takes two objects of Point class as
arguments and computes the Euclidean distance(distance formula)
between them. (iii)A function print_point() to display one point in
the form of orderedpair
Ans.
import math

class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y

def read_point(self):
self.x = float(input("Enter the x-coordinate: "))
self.y = float(input("Enter the y-coordinate: "))

def distance(self, other_point):


if isinstance(other_point, Point):
dx = self.x - other_point.x
dy = self.y - other_point.y
return math.sqrt(dx**2 + dy**2)
else:
raise TypeError("Distance can only be calculated between two
Point objects.")

def print_point(self):
print(f"({self.x}, {self.y})")

# Example usage
if __name__ == "__main__":
point1 = Point()
print("Enter coordinates for Point 1:")
point1.read_point()

point2 = Point()
print("Enter coordinates for Point 2:")
point2.read_point()

print("Point 1:", end=" ")


point1.print_point()

print("Point 2:", end=" ")


point2.print_point()

distance = point1.distance(point2)
print("Distance between Point 1 and Point 2:", distance)

15. Explain the following concepts in python with an example


(i)Polymorphism (ii)Inheritance
Ans.
Polymorphism in Python:
Polymorphism is a fundamental concept in object-oriented programming
that allows objects of different classes to be treated as objects of a
common superclass. It enables you to write code that can work with
objects of different classes in a consistent way, making your code more
flexible and reusable. Polymorphism is typically achieved through method
overriding and method overloading.
Example:
class Animal:
def speak(self):
pass

class Dog(Animal):
def speak(self):
return "Woof!"

class Cat(Animal):
def speak(self):
return "Meow!"

def animal_sound(animal):
return animal.speak()

dog = Dog()
cat = Cat()

print(animal_sound(dog)) # Outputs: Woof!


print(animal_sound(cat)) # Outputs: Meow!

Inheritance in Python:
Inheritance is a mechanism in object-oriented programming that allows a
new class (subclass or derived class) to inherit properties and methods
from an existing class (superclass or base class). It promotes code reuse
and the creation of a hierarchy of classes, where the subclass can extend
or override the behavior of the superclass.
Example:
class Vehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def display_info(self):
return f"{self.brand} {self.model}"

class Car(Vehicle):
def __init__(self, brand, model, num_doors):
super().__init__(brand, model)
self.num_doors = num_doors

def display_info(self):
return f"{super().display_info()}, {self.num_doors} doors"

class Motorcycle(Vehicle):
def __init__(self, brand, model, engine_capacity):
super().__init__(brand, model)
self.engine_capacity = engine_capacity

def display_info(self):
return f"{super().display_info()}, {self.engine_capacity} cc engine"

car = Car("Toyota", "Camry", 4)


motorcycle = Motorcycle("Honda", "CBR500R", 500)

print(car.display_info()) # Outputs: Toyota Camry, 4 doors


print(motorcycle.display_info()) # Outputs: Honda CBR500R, 500 cc
engine

You might also like