0% found this document useful (0 votes)
7 views52 pages

Python Interview Questions Guide

This document is a comprehensive preparation guide for Python coding interviews, featuring 50 curated questions that cover a range of topics from basic concepts to advanced features. It includes explanations and answers to help candidates understand Python's key features, memory management, data types, and more. The guide aims to equip job seekers with the knowledge and confidence needed to excel in technical assessments and interviews.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views52 pages

Python Interview Questions Guide

This document is a comprehensive preparation guide for Python coding interviews, featuring 50 curated questions that cover a range of topics from basic concepts to advanced features. It includes explanations and answers to help candidates understand Python's key features, memory management, data types, and more. The guide aims to equip job seekers with the knowledge and confidence needed to excel in technical assessments and interviews.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Placement Preparation Series

Complete
Preparation Book for
Python

QUESTIONS + EXPLANATIONS + ANSWERS

Crack Your Next Job with


Confidence

[Link]
Python

Python

Concept Introduction:
Python has become one of the most widely used programming
languages in the world, powering everything from web applications
and data science to machine learning and automation. Its simplicity,
versatility, and “batteries included” philosophy make it a favorite
among developers as well as companies like Google, Amazon,
Microsoft, Infosys, and TCS.
This sheet is a carefully curated collection of 50 handpicked Python
interview questions that are most commonly asked in coding
interviews, placement drives, and technical assessments. The
questions are designed to test not only your coding ability but also your
understanding of Python’s core concepts, internals, and real-world
problem-solving skills.
Some of these questions have been actually asked in top tech
companies, while others are highly relevant and expected in interviews.
Together, they cover everything from Python basics and OOP to
advanced topics like multithreading, decorators, generators, and
performance optimization.
If you go through these questions thoroughly, practice coding, and
understand the explanations, you’ll be well-prepared to tackle Python
rounds in placements, internships, and job interviews.

Q1. What are Python’s key features? Why is it called “batteries included”? (TCS)

Explanation:

Python is popular because of its simplicity and powerful ecosystem.

Its key features are:

• Simple and readable syntax → close to English, easy to learn.

• Interpreted → runs line by line, no compilation step needed.

• Dynamically typed → no need to declare variable types.

1
Python

• High-level language → abstracts low-level details like memory management.

• Cross-platform → works on Windows, macOS, Linux, etc.

• Object-oriented and functional support → flexibility in programming styles.

• Large standard library → built-in modules for file I/O, regex, math, networking,
JSON, databases, etc.

• Extensible and integrable → can integrate with C/C++, Java, or call APIs.

• Huge community and libraries → e.g., NumPy, Pandas, Django, TensorFlow.

The phrase “batteries included” means Python comes with a very rich standard
library—you don’t need to install extra packages for many common tasks like
handling files, JSON, XML, math, databases, or HTTP. This saves time and effort.

Answer:

Python is simple, readable, interpreted, dynamically typed, cross-platform, and


supports multiple paradigms like OOP and functional programming.

It’s called ‘batteries included’ because its standard library already covers a wide
range of tasks—like file handling, networking, and data processing—without
needing external packages.

Q2. Difference between Python 2 and Python 3. (Infosys)

Explanation:

Python 2 (released 2000) and Python 3 (released 2008) are two major versions.
Python 2 is now deprecated (support ended in 2020), while Python 3 is the present
and future.

Key differences:

1. Print statement vs function → Python 2 uses print "Hello", Python 3 uses


print("Hello").

2. Integer division → In Python 2, 5/2 = 2; in Python 3, 5/2 = 2.5 (true division). Use
// for floor division.

3. Unicode handling → Python 2 str is ASCII by default; Unicode is a separate type


unicode. In Python 3, str is Unicode by default.

4. xrange vs range → Python 2 has xrange() (memory efficient) and range()


(creates a list). Python 3 merged them—range() behaves like xrange().

5. Error handling → In Python 2: except Exception, e:; in Python 3: except


Exception as e:.

2
Python

6. Library support → Most modern libraries only support Python 3.

Because of these differences, Python 2 code often doesn’t run directly on Python 3
without modification.

Answer:

Python 2 is old and no longer supported, while Python 3 is the current standard.
Main differences: print is a function in Python 3, division gives float by default,
strings are Unicode by default, range() works like xrange(), and exception syntax
changed to as. Most libraries now support only Python 3.

Q3. What are Python namespaces? (Capgemini)

Explanation:

A namespace in Python is like a container (or a mapping) that holds names


(identifiers like variables, functions, classes) and maps them to their corresponding
objects in memory.

Think of it as a dictionary where:

• Keys → variable/function/class names

• Values → the actual objects they refer to

Python uses namespaces to avoid naming conflicts. For example, if two functions
have variables with the same name, their namespaces keep them separate.

Types of namespaces in Python:

1. Built-in namespace → contains built-in functions and constants (print(), len(),


int, str).

2. Global namespace → names defined at the top level of a script/module.

3. Enclosing namespace → names in outer functions (for nested functions).

4. Local namespace → names inside a function (parameters, local variables).

Python resolves names in order using the LEGB Rule:

• Local → Enclosing → Global → Built-in.

So, when you use a variable, Python checks in this order until it finds it.

Answer:

A namespace in Python is a container that maps names to objects, like a dictionary.

Python has built-in, global, enclosing, and local namespaces, and it resolves names
using the LEGB rule—Local → Enclosing → Global → Built-in.
3
Python

Q4. Explain Python’s memory management.

Explanation:

Python manages memory automatically, which means developers don’t have to


allocate or free memory manually. Its memory management has three main parts:

1. Private Heap Space

o All Python objects and data structures are stored in a private heap.

o The Python interpreter manages this heap; you can’t access it directly.

2. Memory Allocation & Management

o Python uses Python Memory Manager to allocate memory for objects.

o It handles object caching, reusing memory blocks for efficiency.

3. Garbage Collection (GC)

o Python mainly uses reference counting: every object keeps track of


how many references point to it.

o When reference count becomes 0, the memory is released.

o To handle circular references (like objects referencing each other),


Python also has a cyclic garbage collector.

Extra points:

• The gc module allows programmers to interact with the garbage collector.

• del keyword reduces the reference count of an object but doesn’t always
delete immediately if references still exist.

• Memory efficiency can also be improved using tools like __slots__ in classes.

Answer:

Python uses a private heap for storing objects, managed by the interpreter.

It uses reference counting for automatic garbage collection, and a cyclic garbage
collector handles circular references.

Developers can also use the gc module for fine control, but in most cases memory
is managed automatically.

Q5. How are Python lists different from arrays in C?

Explanation:

• Python Lists:

4
Python

o They are dynamic → can grow or shrink in size at runtime.

o They are heterogeneous → can store different data types in the same list
([1, "hello", 3.5]).

o Internally, a Python list is more like an array of pointers to objects, not


raw data.

o Because of this, lists are flexible but take more memory and are slower
for numerical computations.

• C Arrays:

o They are static → fixed size once declared.

o They are homogeneous → all elements must be of the same data type
(e.g., int arr[5]).

o They store elements in contiguous memory locations → makes them


memory-efficient and fast for numerical operations.

o No automatic resizing or built-in high-level operations (like append,


slicing).

So, Python lists are powerful and flexible, while C arrays are low-level, memory-
efficient, and type-restricted.

Answer:

Python lists are dynamic and can hold mixed data types, internally storing
references to objects.

C arrays are static, fixed in size, and store only one type in contiguous memory,
making them more memory-efficient but less flexible.

Q6. What are Python’s mutable and immutable data types? (Accenture)

Explanation:

In Python, mutability means whether an object can be changed after creation.

• Mutable Data Types (can be changed in place):

o List → you can add, remove, or change elements.

o Dictionary → keys/values can be updated.

o Set → elements can be added or removed.

o bytearray → can be modified.

• Immutable Data Types (cannot be changed in place):


5
Python

o int, float, complex → numbers are immutable.

o str → strings cannot be modified (any change creates a new object).

o tuple → fixed after creation.

o frozenset → immutable version of set.

o bytes → immutable sequence of bytes.

Why this matters:

• Immutables can be used as dictionary keys or elements of a set (because they


have fixed hash values).

• Mutables cannot be hashed (so they can’t be dict keys).

• Immutability improves safety and predictability, mutability gives flexibility.

Answer:

In Python, mutable objects are those that can be modified after creation, like lists,
dictionaries, sets, and bytearrays.

For example, in a list, I can change an element, append new elements, or remove
existing ones without creating a new object.

On the other hand, immutable objects cannot be changed once created. These
include integers, floats, strings, tuples, frozensets, and bytes. If I try to modify them,
Python actually creates a new object in memory.

For example, changing a string creates a new string rather than modifying the
original.

This difference is important because immutable objects are hashable, so they can
be used as dictionary keys or set elements, while mutable ones cannot. In short,
mutables offer flexibility, whereas immutables provide stability and are safer in
situations where fixed values are required.

Q7. Explain Python’s garbage collection mechanism.

Explanation:

Python manages memory automatically and reclaims unused memory using


garbage collection (GC). The core idea is to free memory occupied by objects that
are no longer in use.

Python primarily uses:

1. Reference Counting → Each object keeps a count of how many references


point to it. When the count drops to zero, the memory is immediately freed.
6
Python

2. Generational Garbage Collector → Reference counting can’t handle circular


references (e.g., two objects referencing each other). To fix this, Python also
has a cyclic garbage collector which groups objects into “generations” and
periodically checks for unreachable objects.

3. gc Module → Developers can interact with the garbage collector (e.g., disable,
enable, or manually trigger collection).

This hybrid approach (reference counting + cyclic GC) makes memory


management mostly automatic, though developers should still be cautious with
circular references and large objects.

Answer:

Python uses automatic garbage collection. The main mechanism is reference


counting—each object tracks how many references point to it, and when this count
goes to zero, the memory is freed.

However, since reference counting alone can’t handle circular references, Python
also uses a cyclic garbage collector that works in generations to find and clean
them. If needed, developers can control this behavior using the gc module.

Q8. Difference between is and ==.

Explanation:

== (Equality Operator):

• Compares the values of two objects.

• Uses the object’s __eq__() method to check if contents are the same.

is (Identity Operator):

• Checks whether two variables point to the same object in memory.

• It does not compare values, only identity.

7
Python

Answer:

In Python, == checks for value equality, meaning whether the contents of two
objects are the same.

On the other hand, is checks for identity—whether both variables refer to the exact
same object in memory.

For example, two lists with the same elements will be equal with ==, but not
identical with is. A common use of is is checking against None, while == is used for
comparing values.

Q9. What is Python’s Global Interpreter Lock (GIL)? (Amazon, Microsoft)

Explanation:

The Global Interpreter Lock (GIL) is a mutex (lock) used in CPython (the default
Python implementation).

It ensures that only one thread executes Python bytecode at a time, even on multi-
core processors.

This design simplifies memory management (especially reference counting) but


limits the effectiveness of multi-threading for CPU-bound tasks.

Impact:

1. CPU-bound tasks (e.g., heavy computations): Multiple threads won’t run in


true parallel due to the GIL → no real performance gain. For these, we usually
use the multiprocessing module to take advantage of multiple cores.

2. I/O-bound tasks (e.g., file operations, network requests): Threads still work
well because Python releases the GIL during blocking I/O, allowing
concurrency.

Answer:

The Global Interpreter Lock, or GIL, is a mutex in CPython that allows only one
thread to execute Python bytecode at a time. It makes memory management
simpler but prevents true parallel execution of CPU-bound threads.

That’s why threading in Python is best for I/O-bound tasks, while multiprocessing
is preferred for CPU-heavy tasks.

Q10. How does Python dictionary handle collisions internally?

Explanation:

8
Python

A dictionary in Python is implemented as a hash table.

• When you insert a key, Python computes its hash value (using __hash__()),
then maps it to an index in the table.

• Sometimes, two different keys may produce the same hash index → this is
called a collision.

How Python handles collisions:

• Python dictionaries use open addressing with probing.

• If the target index is already occupied, Python looks for the next available slot
in the table (linear probing).

• During lookup, it checks the hash and then the actual key (using __eq__) to
ensure correctness.

• If the table gets too full, Python automatically resizes (rehashes) to reduce
collisions.

This strategy keeps dictionary operations—insert, search, delete—average O(1) time


complexity, even with collisions.

Answer:

Python dictionaries are hash tables. When two keys hash to the same index, a
collision occurs.

Python resolves this using open addressing with probing—it searches for the next
free slot and stores the item there.

During lookups, it checks both the hash and the key for correctness. If the table
gets too full, it resizes to keep performance close to O(1).

Q11. Why are Python sets unordered? (Flipkart)

Explanation:

• A set in Python is implemented internally as a hash table (similar to a


dictionary but only storing keys).

• When an element is added, Python computes its hash value and places it in
a slot based on that hash.

• Because the position depends on the hash, and because the hash table can
resize dynamically when it grows, the order of elements is not preserved.

• This is why sets appear “unordered” and why their iteration order can change
between runs or after modifications.

9
Python

• The main goal of sets is fast membership testing (O(1) average time), not
maintaining order.

(Note: From Python 3.7+, dictionaries preserve insertion order, but sets still do not
guarantee it. In practice, you may see some order, but it’s not reliable or part of the
specification.)

Answer:

Python sets are unordered because they are implemented as hash tables.

The position of each element depends on its hash value and may change when the
table resizes, so order is not guaranteed. Sets are designed for fast membership
checks, not for maintaining order.

Q12. Implement a stack and queue using Python lists. (Capgemini)

Explanation:

• Stack (LIFO → Last In, First Out):

o Operations: push() → add to top, pop() → remove from top.

o In Python, we can use append() to push and pop() to remove the last
element.

• Queue (FIFO → First In, First Out):

o Operations: enqueue() → add to rear, dequeue() → remove from front.

o In Python, we can use append() to add elements, and pop(0) to remove


the first element.

o Note: pop(0) is not very efficient (O(n)), so for real queues we use
[Link]. But in interviews, list-based implementation is fine.

# Stack implementation using list


stack = []
# Push
[Link](10)
[Link](20)
[Link](30)
print("Stack after pushes:", stack)

10

# Queue implementation using list


Python

# Pop
print("Popped element:", [Link]())
print("Stack after pop:", stack)

# Queue implementation using list


queue = []

# Enqueue
[Link](10)
[Link](20)
[Link](30)
print("Queue after enqueues:", queue)

# Dequeue
print("Dequeued element:", [Link](0))
print("Queue after dequeue:", queue)

Output :

Stack after pushes: [10, 20, 30]


Popped element: 30
Stack after pop: [10, 20]

Queue after enqueues: [10, 20, 30]


Dequeued element: 10
Queue after dequeue: [20, 30]

11
Python

Answer:

A stack can be implemented using a Python list with append() for push and pop()
for pop, since lists allow fast operations at the end.

A queue can also be implemented using a list, with append() for enqueue and
pop(0) for dequeue, though for efficiency [Link] is usually preferred.

Q13. How does Python’s dict maintain insertion order? (Python 3.7+) (Microsoft)

Explanation:

In earlier versions of Python (before 3.6), dictionaries were unordered collections,


meaning the order of items wasn’t guaranteed.

• Python 3.6: In CPython implementation, dictionaries started preserving


insertion order as a side effect of their new, memory-efficient
implementation.

• Python 3.7+: It was officially made part of the language specification that dict
will always maintain the order of key insertion.

How it works internally:

• Python dictionaries use a hash table to store key-value pairs.

• Along with the hash table, Python maintains a compact array of insertion
order.

• This array keeps track of the sequence in which keys are inserted, ensuring
iteration happens in that order.

• When you delete a key, its slot in the order array is marked as deleted, but
the insertion order of the remaining items is preserved.

This makes operations like iteration predictable and consistent, which is useful in
modern Python programming.

Answer:

In Python 3.7 and above, dictionaries maintain insertion order as an official


language feature.

Internally, a dict uses a hash table for fast lookups and an additional compact array
that records the order in which keys were inserted.

This allows iteration over a dictionary to return elements in the order they were
added. If a key is deleted, the order of the remaining elements remains unchanged.

12
Python

Q14. How do you implement a priority queue in Python? (Google)

Explanation:

A priority queue is like a normal queue, but instead of being processed in the order
items arrive (FIFO), elements are processed based on their priority (higher priority
first).

In Python, we have multiple ways to implement it:

1. Using heapq (most common & efficient):

o Python’s heapq module implements a min-heap, meaning the smallest


element has the highest priority.

o If you want a max-heap, you can store negative priorities.

o Operations like heappush() and heappop() run in O(log n) time.

2. Using [Link]:

o This is a thread-safe class from the queue module.

o It internally uses heapq but is better when working with multi-threaded


applications.

3. Using custom classes with sorting:

o You could push items into a list and sort each time, but that’s inefficient
(O(n log n)) compared to heaps.

Answer:

In Python, a priority queue can be implemented using the heapq module, which
provides an efficient min-heap data structure. We use heappush() to insert
elements and heappop() to remove the element with the highest priority (smallest
value).

For example:

13
Python

Here, tasks are processed in order of priority. If we need a thread-safe version, we


can use [Link].

Q15. What are *args and **kwargs in Python?

Explanation:

In Python, when defining functions, sometimes we don’t know in advance how


many arguments a function might receive.

That’s where *args and **kwargs come in.

• *args (Non-Keyword Arguments):

o Collects multiple positional arguments into a tuple.

o Example:

o Here, args is a tuple (1,2,3) or (5,10) depending on input.

• **kwargs (Keyword Arguments):

o Collects multiple keyword arguments into a dictionary.

o Example:

14
Python

When combined:

You can use both in a function, but the order must be:

def func(positional, *args, **kwargs)

Answer:

In Python, *args allows a function to accept multiple positional arguments, which


are stored as a tuple, while **kwargs allows multiple keyword arguments, stored as
a dictionary.

For example:

Q16. Explain decorators with an example. (Amazon)

Explanation:

A decorator in Python is a special function that allows you to modify or extend the
behavior of another function without changing its actual code.

Functions in Python are first-class objects, meaning they can be passed around as
arguments, returned from other functions, and stored in variables.

A decorator typically:

1. Takes a function as input.

2. Adds some functionality before/after calling it.

3. Returns the modified function.

Why use decorators?

• Code reusability.

• Clean way to add functionality like logging, authentication, performance


measurement, etc.

15
Python

Basic Example:

Here:

• @my_decorator is shorthand for say_hello = my_decorator(say_hello).

• The wrapper function adds behavior before and after the original function.

Answer:

A decorator in Python is a function that takes another function as an argument,


adds extra functionality to it, and returns the modified function.

They are widely used for logging, authentication, and performance measurement.

Q17. Difference between @staticmethod, @classmethod, and instance methods.


(Infosys)

Explanation:

In Python classes, we can define three types of methods:

1. Instance Methods (normal methods)

• The most common type.

• Take self as the first argument, which refers to the object instance.

16
Python

• Can access and modify both object attributes and class attributes.

• Example:

2. Class Methods (@classmethod)

• Decorated with @classmethod.

• Take cls as the first argument, which refers to the class itself (not an object).

• Used to access/modify class-level data.

• Example:

3. Static Methods (@staticmethod)

• Decorated with @staticmethod.

• Don’t take self or cls as arguments.

• Behave like normal functions inside the class (placed there for logical
grouping).

• Example:

17
Python

Key Differences:

• Instance Method: Works with object data.

• Class Method: Works with class data.

• Static Method: Just a function inside a class; neither class nor object data is
passed automatically.

Answer:

In Python, instance methods take self and are used to access or modify object data.

Class methods use @classmethod, take cls, and are meant for class-level data
shared across all objects.

Static methods use @staticmethod, don’t take self or cls, and are just utility
functions placed inside a class for logical grouping.

Q18. Explain method resolution order (MRO) in Python. (TCS)

Explanation:

In Python, Method Resolution Order (MRO) defines the sequence in which classes
are searched when calling a method or attribute.

• It is mainly relevant in multiple inheritance (when a class inherits from more


than one class).

• Python uses the C3 Linearization algorithm to determine this order.

• The search goes from left to right in the inheritance list, but it also respects
the order of parent classes.

You can see the MRO of any class using:

o ClassName.__mro__ (returns a tuple)

o [Link]() (returns a list)

Example:

18
Python

This means: When we call a method on D, Python looks in D → B → C → A → object.

Answer:

MRO in Python is the order in which classes are searched when resolving a method
or attribute.

It’s especially important in multiple inheritance. Python follows the C3 Linearization


algorithm, which generally means left-to-right order while respecting the parent
class hierarchy. We can check it using [Link]() or ClassName.__mro__.

Q19. What is duck typing in Python? (Wipro)

Explanation:

Duck typing is a concept in Python (and other dynamically typed languages) where
the type or class of an object is less important than the methods and properties it
has.

The phrase comes from:

“If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is
a duck.”

In Python:

• You don’t check an object’s type explicitly.

• Instead, you just use it as long as it has the required behavior


(methods/attributes).

• This is possible because Python is dynamically typed.

Example:

19
Python

Here, make_it_quack() doesn’t care whether the object is Duck or Person. As long
as it has a .quack() method, it works.

Answer:

Duck typing in Python means that instead of checking an object’s type, we focus
on whether it has the required methods or attributes.

If an object behaves like the expected type, it can be used, regardless of its actual
class. For example, if two classes both have a quack() method, they can be used
interchangeably, even if they are different types.

Q20. How does Python handle multiple inheritance? (Accenture)

Explanation:

Multiple Inheritance means a class can inherit from more than one base class.

Example:

Here, C inherits from both A and B.


20
Python

The problem:

When two parent classes have the same method, which one should Python call?

This is known as the diamond problem:

If B and C both override a method from A, and D inherits from both, should D call
B’s version, C’s version, or both?

How Python solves this:

• Python uses Method Resolution Order (MRO) with the C3 Linearization


algorithm.

• MRO defines the exact order in which classes are searched when calling a
method.

Rules of MRO:

1. Python looks from left to right in the inheritance list.

2. Each parent is only called once (avoids duplicate calls).

3. Order respects the hierarchy and ensures consistency.

You can check the MRO using:

Answer:

In Python, multiple inheritance allows a class to inherit from more than one parent.

21
Python

The main challenge is the diamond problem, where the same method might exist
in multiple parent classes. Python handles this using the Method Resolution Order
(MRO), based on the C3 linearization algorithm.

MRO defines a consistent order of searching: left to right in the inheritance list,
ensuring each parent is called only once, and respecting the hierarchy.

For example, if a class D inherits from B and C, which both inherit from A, the MRO
might be [D, B, C, A, object]. This ensures there’s no ambiguity in method calls.

Q21. What are abstract classes in Python? (Deloitte)

Explanation:

An abstract class is a class that cannot be instantiated directly and is meant to be


a blueprint for other classes.

Defined in Python using the abc (Abstract Base Class) module.

It can contain:

1. Abstract methods → methods declared but not implemented (must be


implemented by child classes).

2. Concrete methods → normal methods with implementation.

Purpose: To enforce a common interface across all subclasses.

Why use abstract classes?

• They ensure that subclasses implement required methods.

• Help maintain consistency in large projects.

from abc import ABC, abstractmethod

class Animal(ABC):
@abstractmethod
def sound(self):
pass

class Dog(Animal):

22
Python

def sound(self):
return "Bark"

class Cat(Animal):
def sound(self):
return "Meow"

# a = Animal() Error (can't instantiate abstract class)


d = Dog()
print([Link]()) # Bark

Here:

• Animal is abstract because it has an abstract method sound().

• Any subclass (Dog, Cat) must implement sound().

• You cannot create an object of Animal directly.

Answer:

In Python, an abstract class is a class that cannot be instantiated and serves as a


blueprint for other classes. It is defined using the abc module.

Abstract classes can have abstract methods (without implementation) which must
be implemented by all subclasses, along with normal methods.

They are mainly used to enforce a common interface and ensure consistency across
different subclasses.

Q22. Difference between __str__ and __repr__. (Capgemini)

Explanation:

In Python, both __str__ and __repr__ are special methods that return a string
representation of an object. But their purposes are different:

1. __str__ (User-friendly representation)

o Called by str(obj) or print(obj).

23
Python

o Goal → Return a readable and nicely formatted string for end users.

o Meant to be human-readable.

2. __repr__ (Developer/debug representation)

o Called by repr(obj) or just typing the object in the interpreter.

o Goal → Return a string that is unambiguous and ideally could be used to


recreate the object.

o Meant for debugging and developers.

If __str__ is not defined, Python falls back to __repr__.

Answer:

__str__ is used to provide a user-friendly string representation of an object, mainly


for display to end users, like when using print().

__repr__ is used to provide an unambiguous developer-oriented representation,


mainly for debugging, and ideally should return a string that could recreate the
object.

If __str__ is not defined, Python uses __repr__ as a fallback.

Q23. Explain Python’s data classes.

Explanation:

24
Python

A data class in Python is a class that is mainly used to store data rather than
behavior.

Normally, when you create a class to store data, you have to write a lot of boilerplate
code — __init__, __repr__, __eq__, etc.

For example:

This is repetitive.

Python’s dataclasses module (introduced in Python 3.7) automatically generates


these methods for you. You just add the @dataclass decorator, and Python creates
__init__, __repr__, __eq__, and others behind the scenes.

This reduces boilerplate code and makes your classes cleaner.

Key features of data classes:

• Automatically generates common methods (__init__, __repr__, __eq__, etc.).

• Supports default values and type hints.

• Provides comparison support (if enabled).

• Still allows adding custom methods if needed.

Answer:

In Python, data classes are classes decorated with @dataclass from the dataclasses
module, introduced in Python 3.7.

They are mainly used to store data and automatically generate common methods
like __init__, __repr__, and __eq__, which reduces boilerplate code.

They also support type hints, default values, and custom methods if needed.

Q24. Difference between finally, else, and with statements in exception handling.

Explanation:

25
Python

In Python exception handling (try…except), we can also use finally, else, and with —
but each serves a different purpose:

1. finally block

o Always executes whether an exception occurs or not.

o Commonly used to release resources (close file, release lock, disconnect


DB, etc.).

o Guarantees cleanup.

2. else block

o Runs only if no exception occurs in the try block.

o Useful for code that should only execute when the try succeeds.

3. with statement (Context Manager)

o Not exactly part of try-except, but often used with exception handling.

o Simplifies resource management by automatically handling setup and


cleanup (like closing files).

o Internally uses __enter__ and __exit__ methods.

o Helps avoid mistakes of forgetting finally cleanup.

26
Python

Answer:

In Python exception handling, finally always executes, regardless of whether an


exception occurs, and is mainly used for cleanup. else runs only if no exception is
raised in the try block, making it useful for success-only logic.

with is not part of try-except but works as a context manager to handle resources
safely, ensuring automatic setup and cleanup without needing an explicit finally.

Q25. How do you handle custom exceptions in Python?

Explanation:

Exceptions in Python are errors that disrupt the normal flow of a program. While
Python has many built-in exceptions (ValueError, TypeError, etc.), sometimes we
need our own exception type to make error handling more meaningful.

That’s where custom exceptions come in.

How to create a custom exception:

• Create a new class that inherits from Python’s built-in Exception class (or any
subclass of it).

• You can add a custom message or extra attributes if needed.

• Raise it using raise.

• Handle it using try-except.

Why useful?

• Makes debugging easier (clearer error messages).

27
Python

• Lets you define application-specific errors.

Answer:

In Python, we create custom exceptions by defining a class that inherits from the
built-in Exception class.

We then raise it using the raise keyword and handle it in a try-except block. This is
useful when we want more meaningful, application-specific error messages.

Q26. Explain the use of with open() for file handling. (TCS)

Explanation:

In Python, working with files usually involves two steps:

1. Open the file with open().

2. Close the file with [Link]() after work is done.

The problem is — if an exception occurs, the file may remain open, leading to
memory leaks or file locks.

To solve this, Python provides the with statement (context manager). When used
with open(), it:

• Automatically opens the file.

• Ensures the file is closed automatically once the block ends — even if an error
occurs.

28
Python

• Makes code shorter, safer, and cleaner.

So instead of:

We can simply write:

Answer:

In Python, with open() is used for file handling because it automatically manages
resources.

It opens the file, allows operations inside the block, and ensures the file is closed
automatically after the block ends, even if an exception occurs.

This makes the code cleaner and safer compared to manually calling close().

Q27. What happens if you don’t close a file in Python? (Amazon)

Explanation:

When you open a file in Python with open(), the operating system allocates
resources (like file handles, memory buffers, locks) for that file.

If you don’t close the file:

1. Resource leakage → The file handle remains occupied, which can cause your
program or system to run out of available file handles if many files are left
open.

2. Data loss → For write operations, data is often stored in a buffer before being
written to disk. If the file isn’t closed, some data may never be flushed (saved)
to the file.

29
Python

3. File locking issues → On some OS (like Windows), the file might stay locked,
preventing other programs or even your own program from
accessing/modifying it.

4. Unpredictable behavior → Python’s garbage collector may eventually close it,


but you can’t rely on when that happens.

Answer:

If you don’t close a file in Python, it can lead to resource leaks, unsaved data
(because buffers may not flush), and file locking issues.

Although Python may close the file when the object is garbage-collected, relying
on that is unsafe.

That’s why it’s best practice to always close files, usually by using with open() which
ensures automatic closure.

Q28. What is the difference between pip and conda? (Google)

Explanation:

Both pip and conda are package managers in Python, but they are not the same:

1. pip (Python Package Installer):

o Default package manager for Python.

o Installs packages only from PyPI (Python Package Index).

o Manages Python libraries only, not system-level dependencies.

o Works inside any Python environment (virtualenv, venv, conda, etc.).

Example:

pip install numpy

2. conda (Anaconda Package Manager):

o Comes with Anaconda/Miniconda distribution.

o Can install packages from Anaconda repo as well as non-Python


dependencies (C libraries, compilers, etc.).

o Also manages environments (like conda create -n env_name).

o Cross-language → can handle Python, R, C/C++, etc.

Example:

conda install numpy

30
Python

Key Differences:

• Scope: pip → only Python packages, conda → Python + non-Python +


environment management.

• Source: pip → PyPI, conda → Anaconda repo (optimized binaries).

• Environment: conda itself creates isolated environments, while pip needs


venv/virtualenv for that.

Answer:

If you only need a Python library:

pip install requests

If you need a package with C dependencies (like NumPy with optimized binaries):

conda install numpy

Q29. How does Python’s venv/virtualenv work?

Explanation:

In Python, if you install packages globally, every project shares the same set of
dependencies. This often leads to “dependency conflicts” (different projects
needing different versions of the same library).

Solution: Virtual environments (venv in standard library, or virtualenv as a third-


party tool).

How they work:

1. When you create a virtual environment (python -m venv myenv), it makes a


separate directory that contains:

o A copy (or symlink) of the Python interpreter.

o Its own site-packages/ folder (where packages are installed).

o Scripts (pip, python) pointing to this isolated environment.

2. When you activate the environment, your shell modifies the PATH so that the
python and pip you use point to the ones inside the virtual environment.

3. Any package installed with pip install goes only inside that environment, not
affecting global Python.

So effectively, venv/virtualenv works by creating a lightweight, isolated


environment with its own Python binary and dependencies.

Answer:
31
Python

Python’s venv or virtualenv creates an isolated environment by copying the Python


interpreter and maintaining a separate site-packages directory.

When activated, the environment overrides the system Python path so that python
and pip commands use the isolated environment. This allows different projects to
have their own dependencies without conflicts.

Q30. Difference between Python standard modules os, sys, and subprocess.

Explanation:

1. os module

o Provides functions to interact with the operating system.

o Deals with file system operations, directories, environment variables,


process management.

o Examples: [Link](), [Link](), [Link], [Link]().

2. sys module

o Provides functions and variables related to the Python interpreter itself.

32
Python

o Used to access command-line arguments, control interpreter behavior,


or exit the program.

o Examples: [Link], [Link](), [Link], [Link].

3. subprocess module

o Used to spawn new processes and interact with system commands.

o More powerful than [Link]().

o Allows running external programs and capturing their output.

o Examples: [Link](), [Link]().

Key differences at a glance:

• os → Interacts with the OS (files, dirs, environment).

• sys → Interacts with the Python interpreter (arguments, version, exit).

• subprocess → Runs and manages external system processes/commands.

Answer:

In Python, os is used to interact with the operating system like file, directory, and
environment operations. sys interacts with the Python interpreter itself, giving
access to arguments, path, and exit functions.

subprocess is used to run and manage external system commands or processes.


So, os deals with OS resources, sys deals with the interpreter, and subprocess is for
executing external programs.

Q31. Explain __init__.py. (Amazon)

Explanation:

33
Python

In Python, packages are directories that contain multiple modules (files). To tell
Python that a directory should be treated as a package, it traditionally needed an
__init__.py file inside it.

Key points about __init__.py:

1. Package indicator

o Before Python 3.3, a directory without __init__.py was not recognized as


a package.

o From Python 3.3+, it’s optional (namespace packages exist), but still
widely used for clarity.

2. Initialization code

o It runs automatically when the package is imported.

o Can be used to set up variables, import specific modules, or perform


initialization tasks.

3. Control imports

o You can define __all__ inside __init__.py to specify what gets imported
when someone does from package import *.

Answer:

__init__.py is a special file used in Python packages. It marks a directory as a


package and is executed when the package is imported.

It can contain initialization code, import specific modules, or define what gets
exposed when using from package import *. While optional since Python 3.3 (due
to namespace packages), it’s still commonly used for clarity and control.

Q32. Difference between iterators and generators. (Amazon, Accenture)

Explanation:

Iterators

• An iterator is an object that implements two methods:

o __iter__() → returns the iterator itself.

o __next__() → returns the next item or raises StopIteration.

• Example:

34
Python

Iterators can be built manually by creating a class with __iter__ and __next__, but
that’s often verbose.

Generators

• Generators are a simpler way to create iterators using the yield keyword.

• A generator automatically implements __iter__ and __next__.

• They are lazy — values are generated on the fly, not stored in memory.

• Example:

Key Differences:

1. Creation:

o Iterator → requires a class with __iter__ and __next__.

o Generator → written with a simple function using yield.

2. Memory efficiency:

o Iterator → may store data in memory (if built from lists, etc.).

o Generator → produces values one at a time (lazy evaluation).

3. Ease of use:

o Generators are shorter, cleaner, and more Pythonic than manual


iterators.

Answer:
35
Python

An iterator in Python is any object that implements __iter__() and __next__() to


return items one by one.

A generator is a simpler way to create iterators using the yield keyword. Generators
are more memory-efficient because they generate values lazily on demand, while
iterators often hold data in memory.

In short, all generators are iterators, but not all iterators are generators.

Q33. What are Python closures? Give an example. (Flipkart)

Explanation:

A closure is a function that remembers the variables from its enclosing scope even
after that scope has finished executing.

In Python, closures happen when:

1. A function is defined inside another function.

2. The inner function uses variables from the outer function.

3. The outer function returns the inner function, and the inner function still has
access to those outer variables.

Closures are commonly used in decorators, callbacks, and when you want to
“remember” a value without using global variables or classes.

Answer:

A closure in Python is a function that retains access to variables from its enclosing
scope even after the outer function has finished execution.

It allows data to be preserved across function calls without using global variables.

Closures are often used in decorators and callbacks.

36
Python

Q34. Explain yield keyword. (Microsoft)

Explanation:

In Python, the yield keyword is used inside a function to make it a generator


function instead of a normal function.

• A normal function executes all its code at once and returns a single value
using return.

• A generator function uses yield to pause execution and return a value


temporarily, while maintaining its internal state. When the generator is called
again, it resumes execution from where it left off, instead of starting from the
beginning.

This allows you to produce a sequence of values lazily (one at a time) instead of
generating and storing everything in memory at once, which is very efficient for
large data or infinite sequences.

Key Points about yield:

• Turns a function into a generator.

• Can return multiple values one by one.

• Saves memory (lazy evaluation).

• Maintains state between calls.

• Works well with loops like for.

Example:

37
Python

Answer:

The yield keyword in Python is used to create generator functions. Unlike return,
which ends the function, yield pauses execution and returns a value, resuming
from the same point on the next call.

This enables lazy evaluation, efficient memory usage, and iteration over large or
infinite sequences.

Q35. What are Python’s context managers? (Amazon)

Explanation:

A context manager in Python is a construct that properly manages resources, such


as files, database connections, or network sockets. It defines setup and teardown
actions around a block of code (e.g., opening/closing a file).

Context managers are commonly used with the with statement, which ensures
resources are released automatically, even if exceptions occur.

Internally, a context manager uses two special methods:

o __enter__() → runs when the block starts (e.g., opens a file).

o __exit__() → runs when the block ends (e.g., closes a file).

Example 1: File Handling (Built-in Context Manager)

Even if an exception occurs inside the block, the file will still be closed.

Example 2: Custom Context Manager

38
Python

Answer:

A context manager in Python is used to manage resources efficiently by defining


setup and cleanup actions.

It uses __enter__ and __exit__ methods and is mostly used with the with statement.
For example, with open("[Link]") as f: ensures the file is automatically closed after
use, even if an error occurs.

Q36. Difference between multiprocessing and multithreading in Python.

Explanation:

Multithreading means running multiple threads (smaller units of a process) inside


the same process. All threads share the same memory space.

In Python, however, due to the Global Interpreter Lock (GIL), only one thread can
execute Python bytecode at a time.

So multithreading is useful mostly for I/O-bound tasks (like file handling, network
requests, waiting for user input) where threads spend time waiting and not much
CPU work.

Multiprocessing means running multiple processes. Each process has its own
Python interpreter and memory space.

This bypasses the GIL, so multiple CPU cores can actually be used in parallel.

Multiprocessing is best for CPU-bound tasks (like heavy computations, data


processing, mathematical operations).

39
Python

Key differences:

1. Memory – Threads share memory; Processes don’t.

2. Speed – Threads are lightweight; Processes are heavier but give true
parallelism.

3. Use case – Threads for I/O-bound tasks, Processes for CPU-bound tasks.

Answer:

Multiprocessing creates separate processes, each with its own Python interpreter
and memory space, so it achieves true parallelism and is best for CPU-bound tasks.

Multithreading, on the other hand, runs multiple threads within the same process
and shares memory, but due to the GIL only one thread executes Python code at a
time, so it’s better suited for I/O-bound tasks.

Q37. How does async/await work in Python? (Google)

Explanation:

Normally, Python code runs synchronously — each line waits for the previous one
to finish. But in cases like web requests, database queries, or reading files, a lot of
time is spent just waiting. That’s where asynchronous programming comes in.

async/await is Python’s way of writing asynchronous code using coroutines.

o A function defined with async def becomes a coroutine function.

o Inside it, you can use await to pause execution until an awaited task is done
(like waiting for a network call).

o While one coroutine is waiting, Python can run another coroutine, making
the program more efficient.

This doesn’t create new threads or processes. Instead, it uses an event loop (from
the asyncio module) that schedules and switches between tasks.

In short:

• async → marks a function as asynchronous.

• await → pauses the function until the awaited task finishes, without blocking
other tasks.

• Best for I/O-bound tasks (web scraping, APIs, DB calls), not for CPU-heavy
tasks.

40
Python

Answer:

In Python, async and await are used to write asynchronous code with coroutines.
An async def function defines a coroutine, and await pauses its execution until the
awaited task completes.

While one coroutine waits, the event loop can run other coroutines, which makes
async programming very efficient for I/O-bound tasks like network calls or file
operations.

Q38. What are coroutines? (Microsoft)

Explanation:

Coroutines are special functions in Python that can pause and resume their
execution, unlike regular functions that run from start to finish.

They are created using async def and executed using an event loop (usually from
the asyncio module).

Coroutines allow Python to handle asynchronous tasks efficiently, like multiple


network requests or file operations, without creating new threads or processes.

Key points:

41
Python

1. Use async def to define a coroutine.

2. Use await inside it to pause until a task finishes.

3. Multiple coroutines can run concurrently, giving efficient I/O handling.

Answer:

Coroutines are Python functions defined with async def that can pause execution
with await and resume later.

They allow concurrent execution of I/O-bound tasks within a single thread, making
asynchronous programming efficient without using multiple threads or processes.

Q39. How does Python internally store integers and strings?

Explanation:

Integers:

• Python integers are objects of the int class. Internally, they are stored as
objects, not as raw C integers.

• Each integer object contains:

1. Reference count (for memory management)

2. Type information (pointer to int type)

3. Actual value stored in a structure (Python uses arbitrary precision, so


big integers are stored across multiple memory blocks).

• Small integers (usually -5 to 256) are interned, meaning Python reuses the
same object to save memory.

Strings:

• Strings are immutable objects in Python.

• Internally, Python stores a string as a sequence of Unicode code points, along


with:

1. Reference count

2. Type pointer

3. Length of the string

4. Pointer to actual character data

• Python may intern small strings (like identifiers or short literals) to save
memory, meaning identical strings can share the same memory.

42
Python

Key idea: everything in Python is an object, so even integers and strings are stored
as objects with metadata, not raw primitive types like in C.

Answer:

Python stores integers and strings as objects.

An integer object contains its value, reference count, and type info, with small
integers (-5 to 256) being interned for memory efficiency.

Strings are immutable objects stored as sequences of Unicode code points along
with metadata like reference count, type, and length, and short strings may also be
interned to save memory.

Q40. What is interning of strings in Python?

Explanation:

String interning is a memory optimization technique in Python where identical


strings are stored only once in memory.

When a string is interned:

1. Python keeps it in a global pool.

2. Any new reference to the same string will reuse the existing object, instead
of creating a new one.

This helps reduce memory usage and makes string comparisons faster (because
comparing object references is faster than comparing character by character).

Python automatically interns:

• Short strings

• Strings that look like identifiers (letters, numbers, underscores)

You can also manually intern strings using [Link]() if you want to enforce it.

Answer:

String interning in Python is a memory optimization where identical strings are


stored only once and reused.

This reduces memory usage and speeds up string comparisons. Python


automatically interns short strings and identifiers, and you can manually intern
strings using [Link]().

Q41. Why is Python slower compared to Java/C++? (Infosys)

43
Python

Explanation:

Python is an interpreted language, while Java and C++ are compiled languages.

This means Python code is executed line by line, whereas C++/Java code is
compiled into machine code or bytecode, which runs faster.

• Dynamic typing: Python checks types at runtime, adding overhead. In


contrast, C++ and Java have static typing, so types are known at compile-
time.
• Global Interpreter Lock (GIL): In CPython (the standard Python
implementation), only one thread executes Python bytecode at a time, which
limits CPU-bound multithreading.
• Memory management: Python uses reference counting and garbage
collection, which introduces extra runtime overhead.
• Abstraction and flexibility: Python provides a lot of high-level features (like
dynamic lists, dictionaries, flexible objects), which are convenient but slower
than low-level memory operations in C++.

Answer:

Python is slower than Java or C++ because it is an interpreted, dynamically typed


language with runtime type checking and a global interpreter lock.

Its high-level abstractions, dynamic memory management, and flexibility


introduce extra overhead, whereas Java and C++ are compiled and statically typed,
allowing faster execution.

Q42. Explain memory leaks in Python. (Wipro)

Explanation:

A memory leak occurs when a program keeps allocating memory but never
releases it, causing the program to use more and more memory over time.

In Python, memory management is mostly automatic via reference counting and


garbage collection.

However, memory leaks can still happen due to:

1. Circular references – Two or more objects reference each other, preventing


reference count from dropping to zero.

2. Global variables or caches – Objects are unintentionally kept alive.

3. Unreleased resources – Like open files, sockets, or database connections.

Python’s garbage collector can handle circular references, but developers still need
to manage memory carefully in long-running applications.
44
Python

Tools like gc module, tracemalloc, or memory profilers can help detect and fix leaks.

Answer:

A memory leak in Python occurs when objects are no longer needed but are not
released, causing increased memory usage.

This can happen due to circular references, global variables, or unreleased


resources.

Python’s garbage collector handles most cases, but developers need to manage
memory carefully in long-running applications.

Q43. What are Python’s weak references?

Explanation:

In Python, every object is normally kept alive as long as there’s at least one
reference pointing to it. These are called strong references.

But sometimes, you might want to refer to an object without preventing Python
from cleaning it up when it’s no longer needed. That’s where weak references come
in.

A weak reference allows you to reference an object without increasing its reference
count, so it doesn’t block garbage collection.

This is very useful in scenarios like caches, object registries, or tracking objects
where you don’t want your references to unintentionally keep objects in memory.

Python provides the weakref module for creating weak references. If the object is
deleted, the weak reference becomes None or can trigger a callback function,
allowing you to safely handle the cleanup.

Essentially, weak references let you keep tabs on objects without interfering with
Python’s memory management, reducing the risk of memory leaks.

Answer:

In Python, a weak reference is a reference to an object that does not increase its
reference count.

This means the object can still be garbage collected when no strong references
exist.

Weak references are useful for cases like caches or tracking objects, where you
want to access objects if they exist but don’t want to prevent Python from freeing
memory when they are no longer needed.

45
Python

They can be created using the weakref module, and optionally, you can attach a
callback to handle object cleanup. Using weak references helps manage memory
efficiently and prevents unintentional memory retention.

Q44. How do you optimize Python code performance?

Explanation:

Optimizing Python code involves improving speed, memory usage, and efficiency.
Python is high-level and easy to write, but sometimes naive code can be slow.

Common ways to optimize include:

1. Use built-in functions and libraries: Python’s built-ins (like sum(), min(), max())
and standard libraries (itertools, collections) are implemented in C, so they
are much faster than custom Python loops.

2. Choose the right data structures: Use lists, sets, dictionaries, and tuples
appropriately. For example, membership checks in sets/dictionaries are
faster than lists.

3. Avoid unnecessary loops: Use list comprehensions or generator expressions


instead of explicit loops when possible.

4. Lazy evaluation: Use generators (yield) to process large data without loading
everything into memory.

5. Profile and identify bottlenecks: Use cProfile or timeit to find slow parts and
focus optimization there.

6. Avoid global variables: Accessing globals is slower than locals.

7. Use concurrency wisely: For I/O-bound tasks, use asyncio or multithreading;


for CPU-bound tasks, use multiprocessing.

8. Minimize object creation: Reuse objects if possible and avoid creating


unnecessary temporary objects.

Answer:

To optimize Python code performance, you should first profile your code to identify
bottlenecks using tools like cProfile or timeit.

Then, use built-in functions and libraries for efficiency, choose appropriate data
structures (sets/dictionaries for fast lookups), avoid unnecessary loops by using list
comprehensions or generators, and reuse objects to reduce overhead.

For large or I/O-bound tasks, use asyncio or multithreading, and for CPU-bound
tasks, consider multiprocessing. Efficient memory and object management,

46
Python

combined with targeted optimization after profiling, ensures both speed and
maintainable code.

Q45. What are Python’s built-in optimization techniques like lru_cache? (Google)

Explanation:

Python provides built-in tools and decorators to improve performance without


rewriting code. One common example is functools.lru_cache.

lru_cache stands for Least Recently Used cache: it stores the results of expensive
function calls so that future calls with the same arguments return instantly from
the cache instead of recomputing.

This is particularly useful for recursive functions like Fibonacci, where the same
computation happens multiple times.

Other Python optimization techniques include:

1. @cached_property – caches a property value in a class after the first


computation.

2. __slots__ – reduces memory overhead for classes by restricting dynamic


creation of instance dictionaries.

3. Using built-in modules – functions in itertools, collections, heapq, etc., are


implemented in C and faster than custom Python code.

4. Generator expressions – avoid creating large intermediate lists in memory.

Answer:

Python’s built-in optimization techniques help improve performance and reduce


redundant computations.

The functools.lru_cache decorator, for example, caches the results of expensive


function calls and returns the cached result for repeated inputs, which is useful for
functions like Fibonacci.

Other techniques include @cached_property for caching class properties, __slots__


to reduce memory usage, using built-in modules like itertools and collections for
faster operations, and generator expressions to handle large data efficiently.

These tools allow Python programs to run faster and use memory more effectively
without major code changes.

Q46. What is the difference between deepcopy() and shallow copy in Python?
(Asked in Accenture)
47
Python

Explanation:

In Python, when you copy objects, there are two common approaches: shallow
copy and deep copy. A shallow copy means a new object is created, but the nested
objects inside it are still references to the original. So if you modify a nested element
in the copied object, it also changes in the original. This is done using the
[Link]() method.

On the other hand, a deep copy creates a completely independent copy of the
object, including all nested objects. Any change in the copied object will not affect
the original. This is achieved using [Link]().

This difference is very important when working with nested lists, dictionaries, or
custom objects, because a shallow copy can sometimes lead to bugs when both
the original and copied objects unintentionally share data.

Answer:

A shallow copy (using [Link]()) creates a new object but keeps references to
nested objects, so changes in nested elements affect both.

A deep copy (using [Link]()) recursively copies everything, ensuring the


new object is completely independent.

For example, copying a nested list with shallow copy still links inner lists, while
deepcopy duplicates them.

Q47. What are metaclasses in Python? (Asked in Google)

Explanation:

In Python, everything is an object, including classes themselves. Normally, we


create objects from classes, but who creates classes? The answer is metaclasses.

A metaclass is essentially the “class of a class.” Just like a class defines the behavior
of its objects, a metaclass defines the behavior of classes. By default, Python uses
type as the metaclass. That’s why when you check type(MyClass), it returns <class
'type'>.

You can customize metaclasses to automatically modify or add methods/attributes


when a class is created. They’re advanced features and often used in frameworks
(like Django ORM) to enforce rules, auto-register classes, or inject common logic.

Answer:

A metaclass in Python is the class that creates classes. By default, Python classes
are created using the type metaclass. Developers can define custom metaclasses
to control class creation, such as modifying attributes or enforcing constraints.

48
Python

While not needed in daily coding, metaclasses are powerful tools used internally
by frameworks like Django to automate behavior.

Q48. How does Python handle function overloading since it doesn’t support it
directly? (Asked in Infosys)

Explanation:

Unlike Java or C++, Python does not allow multiple functions with the same name
but different parameters. If you define a function again, the new one simply
replaces the old.

However, Python provides flexibility through:

1. Default parameters → We can assign default values to parameters, making a


single function work with different numbers of arguments.

2. Variable arguments (*args and **kwargs) → These allow us to accept any


number of positional and keyword arguments, essentially mimicking
function overloading.

3. Manual handling inside the function → You can check argument types and
counts, then implement behavior accordingly.

Answer:

Python doesn’t support traditional function overloading. Instead, it achieves similar


functionality through default parameters, *args, and **kwargs.

A single function can thus accept different numbers or types of arguments and
handle them conditionally.

For example, a function add(a, b=0) can be called with one or two arguments,
behaving like two different versions.

Q49. What is monkey patching in Python? (Asked in Wipro)

Explanation:

Monkey patching is the practice of modifying or extending code at runtime without


changing the original source code. In Python, because everything is dynamic, you
can replace methods or attributes of classes and modules on the fly.

For example, if a library function has a bug or doesn’t behave as expected, you can
redefine it in your own code without altering the library files. While this is powerful,
it should be used carefully because it can make code harder to understand and
maintain.

49
Python

Answer:

Monkey patching in Python means changing or extending a class/module at


runtime.

For example, you can override a method in a built-in class or third-party library
without touching its source. While useful for quick fixes or testing, it is risky
because it may lead to unpredictable behavior and maintenance challenges.

Q50. Explain Python’s slots and why they are used. (Asked in Amazon)

Explanation:

Normally, Python objects use a dynamic dictionary (__dict__) to store attributes.


This makes them flexible, but it also consumes more memory, especially when
creating many instances of a class.

The __slots__ mechanism allows you to explicitly declare fixed attributes for a class.
By doing this, Python does not create a __dict__ for each instance, which saves
memory and improves access speed.

However, the trade-off is that you cannot add new attributes dynamically outside
of those declared in __slots__. This makes classes less flexible but more efficient.

Answer:

__slots__ in Python are used to define a fixed set of attributes in a class, preventing
the creation of a per-instance dictionary. This reduces memory usage and speeds
up attribute access, which is especially beneficial when creating millions of objects.
The limitation is that you can’t add new attributes beyond those defined in
__slots__.

50
Thank You
For Reading !
Success doesn’t come from
what you do occasionally, it
comes from what you do
consistently.

CREATED BY - TOPPERWORLD

Designed by: Kritika Jain

[Link]

You might also like