PYnative

Python Programming

  • Learn Python
    • Python Tutorials
    • Python Basics
    • Python Interview Q&As
  • Exercises
  • Quizzes
  • Code Editor
Home » Python » Interview Questions » Top 20 Python OOP Interview Questions & Answers

Top 20 Python OOP Interview Questions & Answers

Updated on: April 16, 2025 | Leave a Comment

Mastering Object-oriented programming (OOP) concepts is crucial for any Python developer, and acing the technical interview is a key step in landing your dream job.

This guide provides 25+ essential questions with detailed answers, covering classes, inheritance, polymorphism, and encapsulation. Practical examples and clear explanations ensure you’re prepared to confidently demonstrate your object-oriented programming expertise.

Whether you’re a seasoned professional brushing up on your skills or a recent graduate preparing for your first interview, this resource will equip you with the knowledge and confidence to showcase your Python OOP expertise.

Also Read:

  • Python Interview Questions and Answers: A guide to prepare for Python Interviews
  • Python OOP: Complete Guide to learn Object Oriented Programming in Python
  • Python OOP Exercise

Let’s get started.

1. What is Object-Oriented Programming (OOP)?

Level: Beginner

Object-Oriented Programming (OOP) is a programming paradigm based on the concept of “objects,” which are instances of classes. These objects encapsulate data (attributes) and behavior (methods) and interact with one another to perform tasks. OOP is widely used because it promotes modularity, code reusability, and scalability.

Benefits of OOP:

  • Modularity: Code is organized into classes, making it easier to manage and understand.
  • Reusability: Through inheritance and polymorphism, code can be reused and extended.
  • Maintainability: Encapsulation and abstraction make it easier to modify and maintain the code.
  • Scalability: OOP helps design scalable and robust systems.

2. Explain OOP Core Principles

Level: Beginner

  1. Encapsulation: Encapsulation is the bundling of data (attributes) and methods (functions) that operate on that data into a single unit, i.e., a class. It restricts direct access to some of the object’s components, which is typically done by making attributes private and accessing them through public getter and setter methods. This ensures data security and hides implementation details from the user.
  2. Inheritance: Inheritance allows one class (child) to acquire the properties and behaviors of another class (parent). This promotes code reuse and creates a natural hierarchy of classes. The child class can override or extend the functionality of the parent class.
  3. Polymorphism: The ability of an object to take on many forms. This often involves methods with the same name but different implementations (e.g., a draw() method that behaves differently for a Circle object vs. a Square object).
  4. Abstraction: Abstraction involves hiding the complex implementation details and showing only the essential features of an object. For example, using a smartphone you tap an app icon, not the underlying operating system code.

3. What is Class and Object in Python?

Level: Beginner

Class:

A class in Python is a blueprint for creating objects. It defines a set of attributes (data) and methods (functions) that the objects created from the class will have. Classes allow you to bundle related data and behavior together.

Object:

Object is a specific instance of a class. Every object in Python is created from a class. It’s a tangible thing you can work with in your code.

Each object has a unique identity it means each object contains its own set of attribute values, independent of other objects of the same class.

Example:

For example, think of a class as a blueprint or a template, and an object as a concrete realization of that blueprint. It’s like the difference between the idea of a Vehicle (the class) and a specific car built from those plans (the object).

  • Attributes: Attributes are variables that store data associated with an object. They represent the object’s state. For example, if you have a class called Dog, some attributes might be breed, age, name, and weight.
  • Behavior: Methods that define the actions an object can perform. They represent the object’s behavior. For the Dog class, methods might include bark(), fetch(), eat(), and wag_tail().

You can have multiple objects of the same class, each with its own set of data. Like Car object will have its own set of attributes and methods and Bus object will have its own set of attributes and methods.

Let’s see how to create a class and object in Python.

Code:

class Car:
    # Constructor method to initialize instance attributes
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    # Method to describe the car
    def describe(self):
        return f"{self.year} {self.make} {self.model}"

# Creating an object (instance) of the Car class
my_car = Car("Toyota", "Corolla", 2020)

# Accessing attributes and methods
print(my_car.describe())  # Output: 2020 Toyota CorollaCode language: Python (python)

4. Explain Object-Oriented Programming (OOP) using some real world example

Level: Beginner, Intermediate

Let’s explain Object-Oriented Programming (OOP) using the real-world example of vehicles.

Imagine you’re designing a software system to manage different types of vehicles. OOP helps you organize this system in a logical and reusable way.

I. Classes (Blueprints):

  • Vehicle: This is a general class representing any vehicle. It defines common attributes and behaviors that all vehicles share.
    • Attributes: make, model, year, color, speed, number_of_wheels
    • Methods: start_engine(), stop_engine(), accelerate(), brake()
  • Car: This class inherits from the Vehicle class. It’s a more specific type of vehicle.
    • Attributes (in addition to Vehicle): number_of_doors, sunroof (could be present or not)
    • Methods (in addition to Vehicle, or overridden): open_sunroof(), close_sunroof()
  • Truck: inherits from Vehicle.
    • Attributes (in addition to Vehicle): cargo_capacity, number_of_axles
    • Methods (in addition to Vehicle, or overridden): load_cargo(), unload_cargo()

II. Objects (Instances):

  • my_car = Car("Toyota", "Camry", 2023, "Silver") This creates a specific car object.
  • my_truck = Truck("Ford", "F-150", 2020, "Red") This is a specific truck object.

Each of these objects has its own set of attribute values. my_car has a specific make, model, year, and color and so on.

III. OOP Principles in Action:

  • Abstraction: The Vehicle class provides a general abstraction for all vehicles. You don’t need to know the intricate details of how each type of engine works to interact with a vehicle object. The start_engine() method abstracts away those details.
  • Encapsulation: The data (attributes) and the methods that operate on that data are bundled together within each object. This keeps the data safe and organized. You wouldn’t directly manipulate my_car.speed without using the accelerate() or brake() methods (ideally).
  • Inheritance: The Car and Truck classes inherit from the Vehicle class. This avoids code duplication. They automatically get the attributes and methods defined in the Vehicle class, and then they can add their own specific attributes and methods.
  • Polymorphism: The accelerate() method might behave differently depending on the type of vehicle. A car might accelerate faster than a truck. Polymorphism allows you to call the same method (accelerate()) on different objects, and each object will respond in its own way.

5. What is a constructor (__init__) in Python? What is its purpose?

Level: Beginner, Intermediate

The __init__ method in Python is a special method, also known as the constructor, that’s automatically called when you create a new object from a class

Its primary purpose is to initialize the object’s attributes – the data associated with that object. Think of it as setting up the initial state of the object when it’s born.

For example, if we have a Dog class, the __init__ method would be where we set the dog’s name, breed, and age when we create a new dog object. It ensures that the object has the necessary data to start with.

Example:

class Dog:
    def __init__(self, name, breed, age):  # Constructor
        self.name = name  # Initialize attributes
        self.breed = breed
        self.age = age

    def bark(self):
        print("Woof!")

# Creating Dog objects:
my_dog = Dog("Buddy", "Golden Retriever", 5)  # __init__ is called automatically
another_dog = Dog("Max", "German Shepherd", 3) # __init__ is called automatically

# Accessing the initialized attributes:
print(my_dog.name)      # Output: Buddy
print(another_dog.age)   # Output: 3
my_dog.bark()         # Output: Woof!Code language: Python (python)

6. What is self in Python?

Level: Beginner, Intermediate

In Python, self is a reference to the current instance of a class and using it you access and modify the object’s attributes from within the class.

It’s used inside a class’s methods to refer to the specific object the method is being called on. When you call a method on an object, Python implicitly passes the object itself as the first argument to that method. We conventionally name this first argument self.

For example, if you have a Dog class and you create a my_dog object, when you call my_dog.bark(), Python internally translates this to something like Dog.bark(my_dog). So, self inside the bark() method becomes a reference to my_dog. It’s how the bark() method knows which dog to bark (in case you have multiple dog objects).

Also, using self, we can then access and modify the attributes of that specific dog. For example, inside the bark() method, we might want to print the dog’s name: print(f"{self.name} is barking!"). self.name refers to the name attribute of the particular dog object the bark() method is called on.

Example:

class Dog:
    def __init__(self, name):
        self.name = name  # self.name is the object's attribute, name is the argument

    def bark(self):
        print(f"{self.name} is barking!") # self.name refers to the specific dog's name

dog1 = Dog("Buddy")
dog2 = Dog("Max")

dog1.bark()  # Output: Buddy is barking!
dog2.bark()  # Output: Max is barking!Code language: Python (python)

7. What are class variables and class methods?

Level: Beginner

Class variables:

class variables are shared among all instances (objects) of a class. They are defined within the class but outside of any method.

Think of them as attributes that belong to the class itself, rather than to individual objects. If you modify a class variable, that change is reflected for all objects of that class. It can be accessed using either the class name or an instance.

Class Methods:

Class methods are bound to the class and not the instance of the class. They take the class itself (cls) as the first argument instead of the instance (self). You define them using the @classmethod decorator.

Class methods are often used for factory methods (creating objects of the class in different ways) or for operations that relate to the class as a whole.

“Class variables are useful for data that should be shared across all instances, like constants, counters, and configuration values.
Class methods are great for creating alternate constructors or modifying class-wide behavior.”

Example:

class Dog:
    species = "Canine"  # Class variable

    def __init__(self, name):
        self.name = name  # Instance variable

    @classmethod
    def describe_species(cls):  # Class method
        print(f"All dogs are {cls.species}.")

dog1 = Dog("Buddy")
dog2 = Dog("Max")

print(dog1.name)       # Output: Buddy
print(dog2.name)       # Output: Max

print(Dog.species)    # Output: Canine (accessing class variable)

Dog.describe_species() # Output: All dogs are Canine. (calling class method)
<a href="https://github.com/shrutilalwani07/Ineuron-Assignments" target="_blank" rel="noreferrer noopener"></a>
This example shows:Code language: Python (python)
  • species is a class variable, shared by all Dog instances.
  • name is an instance variable, unique to each Dog.
  • describe_species is a class method, accessed using Dog.describe_species(). It can access the class variable species using cls.species.

8. What is the difference between instance methods, class methods, and static methods?

Level: Intermediate

  • Instance Methods: Operate on a specific instance of the class (an object). They receive self (the instance itself) as the first argument. They can access and modify instance-specific data. Most common type of method.
  • Class Methods: Operate on the class itself. They receive cls (the class) as the first argument. They can access and modify class-level data (like class variables). Often used for factory methods (creating instances in different ways).
  • Static Methods: Neither operate on an instance nor the class directly. They don’t receive self or cls as arguments. They are essentially regular functions that are part of the class’s namespace. Used for utility functions that are related to the class but don’t need access to instance or class data.

9. Explain inheritance in Python with an example

Level: Beginner

Inheritance in Python is a powerful mechanism that allows you to create new classes (called derived or child classes) based on existing classes (called base or parent classes). It’s like a parent passing down traits to their child. The derived class inherits the attributes and methods of the base class, which promotes code reuse and allows you to model “is-a” relationships naturally.

Here’s a simple example: Imagine we have a Animal class:

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

    def speak(self):
        print("Generic animal sound")

    def eat(self):
        print("Eating...")Code language: Python (python)

Now, let’s say we want to create a Dog class. A dog is a animal, so we can use inheritance:

class Dog(Animal):  # Dog inherits from Animal
    def bark(self):
        print("Woof!")

    def speak(self): # Overriding the speak method
        print("Woof!")Code language: Python (python)

Here, Dog inherits from Animal. This means:

  • Dog automatically gets the name attribute and the speak() and eat() methods from Animal. We don’t have to redefine them.
  • Dog can also have its own methods, like bark(), which are specific to dogs.
  • Dog can override methods from the parent class. For example, the speak() method in Dog overrides the speak() method in Animal to provide a more specific “Woof!” sound. This is polymorphism in action.

Now, we can create Dog objects:

my_dog = Dog("Buddy")
print(my_dog.name)  # Output: Buddy (inherited from Animal)
my_dog.speak()      # Output: Woof! (overridden method)
my_dog.eat()        # Output: Eating... (inherited method)
my_dog.bark()      # Output: Woof! (dog-specific method)Code language: Python (python)

Inheritance helps us avoid writing the same code multiple times. We define common attributes and methods in the base class, and then more specialized classes inherit those features, adding or modifying what’s necessary. It makes our code more organized, reusable, and easier to maintain. It’s important to remember that inheritance models an “is-a” relationship. A Dog is a Animal. “

10. What is method overriding in Python?

Level: Intermediate

Method overriding lets a subclass provide its own version of a method that its superclass already has. It allows the subclass to customize or specialize the behavior inherited from the superclass. Think of it as the child class “redefining” a behavior inherited from the parent class to make it more appropriate for itself.

For example, let’s say we have an Animal class with a speak() method:

class Animal:
    def speak(self):
        print("Generic animal sound")Code language: Python (python)

And then we have a Dog class that inherits from Animal:

class Dog(Animal):
    def speak(self):  # Overriding the speak() method
        print("Woof!")Code language: Python (python)

The Dog class overrides the speak() method. Now, if we create a Dog object and call speak():

my_dog = Dog()
my_dog.speak()  # Output: Woof!Code language: Python (python)

We get “Woof!” because the Dog class’s speak() method is used, not the Animal class’s. This is method overriding. It’s a key part of polymorphism, allowing objects of different classes to respond to the same method call in their own way.”

11. What is super() in Python? How is it used?

Level: Intermediate

super() in Python is a function used within a class to call methods from a parent (or super) class.

When you have a class that inherits from another class, you might want to extend or modify the behavior of a method in the parent class, but you still want to use the parent class’s implementation as part of your new method. That’s where super() comes in.

It’s particularly helpful when you’re working with inheritance and method overriding.

Example: Managing Employee Data (Company)

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def get_details(self):
        return f"Name: {self.name}, Salary: {self.salary}"

class Manager(Employee):
    def __init__(self, name, salary, department):
        super().__init__(name, salary)
        self.department = department

    def get_details(self):
        base_details = super().get_details()  # Get basic details from Employee
        return f"{base_details}, Department: {self.department}"

emp = Employee("Alice", 60000)
print(emp.get_details())  # Output: Name: Alice, Salary: 60000

mgr = Manager("Bob", 80000, "Sales")
print(mgr.get_details())  # Output: Name: Bob, Salary: 80000, Department: SalesCode language: Python (python)

In this case, Manager inherits from Employee. The Manager‘s get_details() method uses super().get_details() to get the basic employee information and then adds the department information. This avoids code duplication and keeps the logic for getting basic employee details centralized in the Employee class.

12. What is encapsulation in OOP? How is it achieved in Python?

Level: Intermediate

Encapsulation in OOP is the bundling of data (attributes) and the methods (functions) that operate on that data into a single unit – the object. It also involves controlling access to the internal data of the object, preventing direct and inappropriate modification from outside. Think of it as a capsule containing both the data and the tools to work with that data.

Achieving Encapsulation in Python:

Python doesn’t have strict access modifiers like private or protected (as in some other languages). However, encapsulation is achieved through naming conventions and property decorators:

  • Naming Conventions (Weak Encapsulation): A single leading underscore _ before an attribute or method name is a convention that indicates it’s intended for internal use within the class. It’s a signal to other developers that they shouldn’t directly access it from outside the class. However, it’s still possible to access these members (it’s just discouraged). This is sometimes called “weak” or “convention-based” encapsulation.
  • Name Mangling (Stronger Encapsulation): A double leading underscore __ before an attribute name triggers name mangling. Python changes the name of the attribute to include the class name, making it more difficult (but not impossible) to access directly from outside the class. This provides a somewhat stronger form of encapsulation.
  • Property Decorators: These provide a more Pythonic way to control access to attributes and implement getters, setters, and deleters. They allow you to define methods that behave like attributes, providing controlled access and allowing you to add validation or other logic.

Example:

class MyClass:
    def __init__(self):
        self._internal_variable = 0  # Weak encapsulation (convention)
        self.__private_variable = 1  # Name mangling (stronger, but not truly private)

    def get_internal_variable(self): # getter method to access internal variable
        return self._internal_variable

    def set_internal_variable(self, value): # setter method to modify internal variable
        if value >= 0:
            self._internal_variable = value
        else:
            print("Value must be non-negative")

    @property # property decorator - allows accessing the method like an attribute
    def my_property(self):
        return self._internal_variable * 2

obj = MyClass()
print(obj._internal_variable)  # Accessing is possible but not recommended
# print(obj.__private_variable) # This will give AttributeError. Name is mangled to _MyClass__private_variable
print(obj._MyClass__private_variable) # Accessing is possible but not recommended
obj.set_internal_variable(10)
print(obj.get_internal_variable()) # Output: 10
obj.set_internal_variable(-1) # Output: Value must be non-negative
print(obj.get_internal_variable()) # Output: 10 (value not changed since it was negative)
print(obj.my_property) # Output: 20Code language: Python (python)

13. What is polymorphism in Python? Give an example

Level: Intermediate

In Python, polymorphism is the ability of objects of different classes to respond to the same method call in their own specific ways.

Polymorphism lets you treat objects of different classes in a uniform way. Imagine you have a Shape class, and then you have subclasses like Circle, Square, and Triangle. Each of these shapes has an area() method, but the way you calculate the area is different for each shape.

The core idea is that the same method name can have different implementations depending on the class of the object.

Example:

class Shape:
    def area(self):
        raise NotImplementedError("Area method must be implemented by subclasses")

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

    def area(self):  # Overriding the area method
        return 3.14159 * self.radius**2

class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):  # Overriding the area method
        return self.side * self.side

# Example of polymorphism
shapes = [Circle(5), Square(7), Circle(3)]

for shape in shapes:
    print(shape.area())  # Output: 78.53975, 49, 28.27431Code language: Python (python)

In this example, we have a list of Shape objects, but some are actually Circle objects, and others are Square objects. When we call the area() method on each shape, Python automatically calls the correct version of the area() method based on the object’s actual type. This is polymorphism. We can iterate through the list of shapes and call area() without needing to check if it’s a circle or a square. The magic of polymorphism handles it for us.

Polymorphism makes your code more flexible, reusable, and easier to maintain. You can add new shape types later without having to modify the code that works with the list of shapes.”

14. How to check if the object belongs to a particular class?

Level: Beginner

The isinstance() function is the most straightforward and generally preferred way to check if an object is an instance of a class (or a tuple of classes). It also handles inheritance correctly. 1

Example:

class Vehicle:
    pass

class Car(Vehicle):
    pass

my_car = Car()
my_vehicle = Vehicle()

print(isinstance(my_car, Car))      # Output: True
print(isinstance(my_car, Vehicle))   # Output: True (Caris a subclass of Vehicle)
print(isinstance(my_vehicle, Car))   # Output: False
print(isinstance(my_vehicle, (Dog, Vehicle))) # Output: True (checking against a tuple of classes)Code language: Python (python)

Why isinstance() is generally preferred:

  • Handles Inheritance: isinstance() correctly identifies that an object of a subclass is also an instance of its parent class. This is crucial for polymorphism and working with class hierarchies.
  • More Pythonic: Using isinstance() is the standard and more Pythonic way to perform this check.
  • Clearer Intent: isinstance() clearly expresses the intent of checking for an instance of a class, making your code more readable.

15. How to check object is a subclass of a particular class?

Level: Intermediate

To check if a class is a subclass of another class in Python, use the issubclass() function. issubclass(potential_subclass, potential_superclass) returns True if the first argument is a subclass (or the same class) of the second argument, and False otherwise.

It handles inheritance hierarchies correctly. It’s distinct from isinstance(), which checks if an object is an instance of a class.

Example:

class Animal:
    pass

class Dog(Animal):
    pass

class Puppy(Dog):
    pass

class Cat:
    pass

print(issubclass(Dog, Animal))   # Output: True  (Dog is a subclass of Animal)
print(issubclass(Animal, Dog))   # Output: False (Animal is not a subclass of Dog)
print(issubclass(Dog, Dog))      # Output: True (A class is a subclass of itself)
print(issubclass(Puppy, Animal)) # Output: True (Puppy inherits from Dog, which inherits from Animal)
print(issubclass(Cat, Animal))    # Output: False (Cat is not related to Animal)
print(issubclass(Dog, (Animal, Cat))) # Output: True (Dog is a subclass of Animal)
print(issubclass(Cat, (Animal, Dog))) # Output: False (Cat is not a subclass of Animal or Dog)Code language: Python (python)

16. Can we Can we overload the constructors in Python?

Level: Intermediate

No, you cannot directly overload constructors (the __init__ method) in Python in the same way you might in languages like C++ or Java. Python doesn’t support multiple constructors with different signatures within the same class.

If you try to define multiple __init__ methods in a class, the last definition will simply override any previous ones. However, you can achieve a similar effect through Default Arguments:

class MyClass:
    def __init__(self, arg1, arg2=None, arg3=None):
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3

obj1 = MyClass("value1")         # arg2 and arg3 are None
obj2 = MyClass("value1", "value2")  # arg3 is None
obj3 = MyClass("value1", "value2", "value3")Code language: Python (python)

17. What are abstract classes and interfaces in Python? How do you create them?

Level: Intermediate

An abstract class is a class that cannot be instantiated directly. It serves as a blueprint or a template for other classes (its subclasses).

Abstract classes can contain both concrete methods (methods with implementations) and abstract methods (methods without implementations). The purpose of an abstract class is to define a common interface or structure for its subclasses.

To create an abstract class in Python, we use the abc module (Abstract Base Classes):

Example:

from abc import ABC, abstractmethod

class Shape(ABC):  # Shape is an abstract class
    @abstractmethod
    def area(self):  # Abstract method (no implementation)
        pass  # Or raise NotImplementedError

    def describe(self):  # Concrete method
        print("This is a shape.")

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

    def area(self):  # Implementing the abstract method
        return 3.14159 * self.radius**2

# shape = Shape()  # This will raise a TypeError because you cannot instantiate an abstract class

circle = Circle(5)
print(circle.area())  # Output: 78.53975
circle.describe()  # Output: This is a shape.Code language: Python (python)

Key points about abstract classes:

  • You use the ABC metaclass (from the abc module) to define an abstract class.
  • Abstract methods are declared using the @abstractmethod decorator. Subclasses must implement these abstract methods. If they don’t, you’ll get a TypeError when you try to instantiate the subclass.
  • Abstract classes can have concrete methods (like describe() in the example). These methods are inherited by the subclasses.
  • You cannot create an instance of an abstract class directly.

18. Explain the difference between __str__ and __repr__ methods in Python

Level: Intermediate

__str__ and __repr__ are both special methods in Python used to represent objects as strings, but they serve different purposes. Here’s how to explain it briefly to an interviewer:

__str__ is designed to provide a user-friendly, informal string representation of an object. It’s what you see when you use print(object) or str(object). It should be readable and informative for the end-user.

__repr__ is meant to provide a more unambiguous, developer-focused string representation of an object. It’s what you see when you inspect an object in the interactive interpreter or use repr(object). Ideally, eval(repr(object)) should recreate the object. So, it should contain enough information to uniquely identify the object and its state.

Think of it this way: __str__ is for the user, __repr__ is for the developer.

Example:

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

    def __str__(self):
        return f"({self.x}, {self.y})"  # User-friendly

    def __repr__(self):
        return f"Point({self.x}, {self.y})"  # Developer-focused

p = Point(2, 3)

print(p)        # Output: (2, 3)      (Uses __str__)
print(repr(p))   # Output: Point(2, 3)  (Uses __repr__)

p2 = eval(repr(p))
print(p2) # Output: (2, 3)
print(p2.x) # Output: 2
print(p2.y) # Output: 3Code language: Python (python)

Key Differences:

  • Audience: __str__ is for users, __repr__ is for developers.
  • Readability: __str__ should be easy to read, __repr__ should be unambiguous.
  • eval() compatibility: eval(repr(object)) should ideally recreate the object. eval(str(object)) is not expected to do this.
  • Default fallback: If __str__ is not defined, Python will fall back to using __repr__ for print(). If __repr__ is not defined either, you get a less informative default representation.

In short, use __str__ for a nice, user-friendly output, and __repr__ for a more detailed, developer-focused representation that could potentially be used to recreate the object. Defining both gives you the most flexibility.

19. What are metaclasses in Python? When are they useful?

Level: Intermediate, Advance

Metaclasses allow you to customize how classes are created. When you define a class, Python’s default metaclass (type) is used to create that class object. By defining your own metaclass, you can intercept and modify this class creation process.

When are they useful? They’re generally used for very specific and advanced use cases:

  1. Automatic attribute or method registration: You might want to automatically register classes with a central registry when they are defined. A metaclass can handle this.
  2. Enforcing coding conventions or patterns: You can ensure that all classes that use a particular metaclass adhere to certain coding standards (e.g., they must define certain attributes or methods).
  3. Creating domain-specific languages (DSLs): Metaclasses can be used to create mini-languages tailored to a particular problem domain.
  4. Implementing design patterns: Some design patterns can be more elegantly implemented using metaclasses.
  5. Modifying class behavior at creation time: You can dynamically add methods or attributes to a class when it’s being created.

Example:

class MyMeta(type):  # MyMeta inherits from the default metaclass 'type'
    def __new__(cls, name, bases, attrs):  # Overriding the __new__ method
        # Modify the attributes (attrs) before the class is created
        attrs['new_attribute'] = 'Hello from metaclass'
        return super().__new__(cls, name, bases, attrs)  # Call type.__new__

class MyClass(metaclass=MyMeta):  # Using MyMeta as the metaclass
    pass

print(MyClass.new_attribute)  # Output: Hello from metaclassCode language: Python (python)

In this simplified example, MyMeta is a metaclass. When MyClass is defined, the MyMeta.__new__ method is called. This method modifies the class’s attributes (adds new_attribute) before the class object is actually created.

20. Explain the concept of composition over inheritance. When is composition preferred?

Level: Intermediate, Advance

“Composition over inheritance is a design principle in object-oriented programming that emphasizes building complex objects by combining simpler objects, rather than creating hierarchies of classes through inheritance. It’s about has-a relationships rather than is-a relationships.

Here’s the key idea: Instead of saying “A car is a vehicle,” we say “A car has a engine,” “A car has wheels,” and “A car has a transmission.” These has-a relationships are best modeled through composition.

When is composition preferred?

Composition is generally preferred over inheritance in the following situations:

  1. When the relationship is not truly “is-a”: If the relationship isn’t a clear “is-a” relationship, inheritance can lead to problems. For example, a Penguin is a Bird, but it doesn’t fly. Inheriting from a Bird class with a fly() method would be incorrect for Penguin. Composition is a better choice here: A Penguin has a wings attribute, but the wings don’t support flying.
  2. To avoid the “diamond problem” (in languages with multiple inheritance): If a class inherits from two classes that have a common ancestor, the “diamond problem” can arise, where it’s ambiguous which version of a method or attribute to inherit. Composition avoids this.
  3. For greater flexibility and loose coupling: Composition allows you to easily change the components of an object at runtime. For example, you could easily swap out the engine in a Car object without affecting the Car class itself. With inheritance, changing the parent class affects all subclasses, which can be less flexible.
  4. To avoid inheriting unwanted behavior: As seen in the penguin example, inheritance can force you to inherit methods or attributes that are not relevant to the subclass. Composition avoids this.
  5. Favoring “has-a” over “is-a”: In many real-world scenarios, “has-a” relationships are more prevalent than “is-a” relationships.

Example:

class Engine:
    def start(self):
        print("Engine started")

class Wheels:
    def rotate(self):
        print("Wheels rotating")

class Car:  # Composition
    def __init__(self):
        self.engine = Engine()    # Car has an engine
        self.wheels = Wheels()  # Car has wheels

    def drive(self):
        self.engine.start()
        self.wheels.rotate()

my_car = Car()
my_car.drive()
Code language: Python (python)

In this example, Car is composed of Engine and Wheels. It’s more flexible than inheriting from Engine and Wheels because we can easily change the type of engine or wheels the car has without altering the Car class hierarchy.

In summary, composition offers more flexibility and loose coupling compared to inheritance, making it the preferred approach when the relationship is a “has-a” rather than a strict “is-a” relationship. It helps avoid many of the pitfalls that can arise with complex inheritance hierarchies.”

21. Explain how Python handles object destruction and garbage collection

Level: Intermediate, Advance

Python uses automatic garbage collection to manage memory and object destruction. It employs a combination of reference counting and cyclic garbage collection.

Here’s a brief overview:

  1. Reference Counting: Python keeps track of how many references point to an object. When an object’s reference count drops to zero (meaning no part of the program is using it anymore), the object’s memory is automatically reclaimed. This is the primary mechanism for garbage collection.
  2. Cyclic Garbage Collection: Reference counting alone can’t handle circular references where two or more objects refer to each other, even if they are no longer accessible by the main program. Python’s cyclic garbage collector detects these cycles and reclaims the memory occupied by the objects involved. It uses a mark-and-sweep algorithm.
  3. Destructors (__del__): Classes can define a __del__ method (the destructor). This method is called when an object is about to be destroyed by the garbage collector. However, relying on __del__ for resource cleanup is generally discouraged because the timing of its execution is not guaranteed. It’s better to use context managers (with statement) or explicit cleanup methods for resource management.
  4. Garbage Collection Module: The gc module provides some control over the garbage collector. You can manually trigger garbage collection, disable it (though generally not recommended), or inspect information about collected objects.

So, in essence, Python automatically manages object destruction and memory reclamation through reference counting and cyclic garbage collection. While destructors exist, they are not the preferred way to handle resource cleanup. Context managers and explicit cleanup methods are better for that purpose.

Summary and Next Steps

These questions should give you a good starting point for your Python OOP interview preparation. Remember to not only know the answers but also be able to explain the concepts clearly and provide relevant examples. Good luck!

Here’s a summary and breakdown of how to prepare and perform well in Python OOP interview:

1. Solid Foundation:

  • Core Concepts: Master the fundamental principles of OOP:
    • Abstraction: Hiding complexity and showing only essential details.
    • Encapsulation: Bundling data and methods that operate on that data, controlling access.
    • Inheritance: Creating new classes from existing ones, inheriting attributes and methods.
    • Polymorphism: Objects of different classes responding to the same method call in their own way.

2. Deepen Your Knowledge:

  • Advanced Concepts: Familiarize yourself with more advanced OOP topics:
    • Abstract Classes and Interfaces (using abc module): Understand their purpose and how to define them.
    • Metaclasses: Know what they are and when they might be useful (though they are less frequently asked about in interviews).
    • Method Resolution Order (MRO): Understand how Python handles method calls in multiple inheritance scenarios.
    • Decorators (especially class and method decorators): Be able to explain and use them.
    • Magic Methods (Dunder Methods): Know some common ones like __len__, __getitem__, __add__, and how they can be used to customize object behavior.
  • Design Principles: Study SOLID principles (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion) and understand how they relate to good OOP design.
  • Design Patterns: While Python’s dynamic nature sometimes makes classic design patterns less essential, it’s still valuable to be aware of common patterns like Factory, Singleton, Observer, and Strategy. Understand the problems they solve and when they might be applicable.
  • Composition over Inheritance: Understand the benefits of composition and when it’s preferred over inheritance.

3. Prepare for Interview Questions:

  • Solve Python OOP Exercises: Be prepared for coding challenges that involve implementing classes, inheritance, and polymorphism. Practice writing clean, well-documented code.
  • Scenario-Based Questions: You might be asked to design a class hierarchy for a given scenario (e.g., a library management system, a game). Think through the classes, attributes, and methods you would need and explain your design choices.
  • Given a piece of code, identify potential OOP design flaws and suggest improvements. This assesses your understanding of good OOP practices.

By following these steps, you’ll be well-prepared to face your Python OOP interview with confidence. Remember that demonstrating a deep understanding of the principles and the ability to apply them to real-world problems are the most important things.

Filed Under: Interview Questions, Python, Python Object-Oriented Programming (OOP)

Did you find this page helpful? Let others know about it. Sharing helps me continue to create free Python resources.

TweetF  sharein  shareP  Pin

About Vishal

Image

I’m Vishal Hule, the Founder of PYnative.com. As a Python developer, I enjoy assisting students, developers, and learners. Follow me on Twitter.

Related Tutorial Topics:

Interview Questions Python Python Object-Oriented Programming (OOP)

Python Exercises and Quizzes

Free coding exercises and quizzes cover Python basics, data structure, data analytics, and more.

  • 15+ Topic-specific Exercises and Quizzes
  • Each Exercise contains 10 questions
  • Each Quiz contains 12-15 MCQ
Exercises
Quizzes

Leave a Reply Cancel reply

your email address will NOT be published. all comments are moderated according to our comment policy.

Use <pre> tag for posting code. E.g. <pre> Your entire code </pre>

Posted In

Interview Questions Python Python Object-Oriented Programming (OOP)
TweetF  sharein  shareP  Pin

  Python Interview Q&A

  • Python Interview
  • Beginner Python Interview Questions
  • Python Loops Interview Questions
  • Python String Interview Questions
  • Python Functions Interview Questions
  • Python List Interview Questions
  • Python OOP Interview Questions

 Explore Python

  • Python Tutorials
  • Python Exercises
  • Python Quizzes
  • Python Interview Q&A
  • Python Programs

All Python Topics

Python Basics Python Exercises Python Quizzes Python Interview Python File Handling Python OOP Python Date and Time Python Random Python Regex Python Pandas Python Databases Python MySQL Python PostgreSQL Python SQLite Python JSON

About PYnative

PYnative.com is for Python lovers. Here, You can get Tutorials, Exercises, and Quizzes to practice and improve your Python skills.

Explore Python

  • Learn Python
  • Python Basics
  • Python Databases
  • Python Exercises
  • Python Quizzes
  • Online Python Code Editor
  • Python Tricks

Follow Us

To get New Python Tutorials, Exercises, and Quizzes

  • Twitter
  • Facebook
  • Sitemap

Legal Stuff

  • About Us
  • Contact Us

We use cookies to improve your experience. While using PYnative, you agree to have read and accepted our:

  • Terms Of Use
  • Privacy Policy
  • Cookie Policy

Copyright © 2018–2025 pynative.com

Advertisement