Skip to main content

Top 25 Python Interview Questions (2026 Edition)

Top 25 Python Interview Questions (2026 Edition)

The Python interview landscape has evolved. It’s no longer enough to just know the difference between a list and a tuple. Modern interviews (heading into 2026) focus on memory management, concurrency, the modern type system, and the architectural shifts happening in the Python ecosystem (like the no-GIL build and uv).

Here is the definitive list of the Top 25 Python Interview Questions for 2026, categorized by domain, with deep-dive answers and code examples.


Section 1: Core Mechanics & Memory

1. What happens if you use a mutable object as a default argument?

One of the most classic Python gotchas. Default arguments are evaluated once at function definition time, not at call time. If you mutate it, the mutation persists across calls.

# ❌ BAD
def append_to(element, target=[]):
    target.append(element)
    return target

# βœ… GOOD
def append_to(element, target=None):
    if target is None:
        target = []
    target.append(element)
    return target

2. How does Python manage memory? (Explain Reference Counting and the GC)

Python uses a hybrid approach: 1. Reference Counting: Every object keeps track of how many references point to it. When it hits 0, it is immediately deallocated. 2. Generational Garbage Collector: Ref counting fails on circular references (A points to B, B points to A). The GC periodically scans for these cycles. It divides objects into 3 generations (0, 1, 2). New objects go in Gen 0. Surviving objects are promoted. The GC scans younger generations more frequently.

3. What is the difference between __new__ and __init__?

  • __new__ is the allocator. It actually creates the object instance in memory. It is a static method that returns the new instance. Used primarily for subclassing immutable types (like str or tuple).
  • __init__ is the initializer. It configures the already created object. It returns None.

4. What are __slots__ and why would you use them?

By default, Python stores object attributes in a dynamic dictionary (__dict__). __slots__ explicitly declares a fixed set of attributes, preventing the creation of __dict__. * Pros: Saves massive amounts of RAM (crucial for millions of objects) and slightly faster attribute access. * Cons: You cannot dynamically add new attributes, and it complicates multiple inheritance.

5. Explain Deep vs. Shallow Copy.

  • Shallow Copy: Creates a new object, but populates it with references to the child objects found in the original. (e.g., copy.copy() or list(original_list)). Modifying a nested mutable object affects both.
  • Deep Copy: Recursively copies the original object and all objects contained within it. (e.g., copy.deepcopy()). They are completely independent.

6. How do Descriptors work?

Descriptors are objects that implement any of __get__, __set__, or __delete__. They allow you to customize attribute lookup. property, classmethod, and staticmethod are all built using descriptors. They power the ORM fields in SQLAlchemy and Django.

class VerifiedPassword:
    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, obj, objtype=None):
        return obj.__dict__[self.name]

    def __set__(self, obj, value):
        if len(value) < 8:
            raise ValueError("Password too short")
        obj.__dict__[self.name] = value

Section 2: OOP & Metaprogramming

7. Explain Method Resolution Order (MRO) in multiple inheritance.

When a class inherits from multiple parents, Python uses the C3 Linearization algorithm to determine the order in which base classes are searched for a method. It ensures that a child class is always checked before its parents, and the order of parents is preserved. You can view it via ClassName.mro().

graph TD
    A[Class D] --> B[Class B]
    A --> C[Class C]
    B --> D[Class A]
    C --> D

Search order: D -> B -> C -> A

8. What is a Metaclass and what is a practical use case?

A metaclass is the "class of a class." Just as a class defines how an instance behaves, a metaclass defines how a class behaves. Python uses type as the default metaclass. * Use Case: Enforcing coding standards, registering classes automatically (like a Plugin Registry), or preventing class creation if abstract methods aren't implemented (similar to abc.ABCMeta).

9. How do @classmethod, @staticmethod, and instance methods differ?

  • Instance Method: Receives self (the instance) as the first arg. Can read/write instance state.
  • Class Method: Receives cls (the class) as the first arg. Used for factory methods (alternative constructors).
  • Static Method: Receives neither self nor cls. Just a namespace for a utility function logically related to the class.

10. What are Data Classes?

Introduced in PEP 557 (Python 3.7), @dataclass auto-generates __init__, __repr__, and __eq__ based on type hints. It drastically reduces boilerplate. Use frozen=True to make them immutable.

11. Explain the __call__ method.

If a class implements __call__, its instances can be called like functions. This is often used for stateful function-like objects, or for creating class-based decorators (where the class takes the function in __init__ and wraps it in __call__).


Section 3: Concurrency & Asyncio

12. What is the GIL (Global Interpreter Lock)?

A mutex in CPython that allows only one thread to execute Python bytecode at a time. It exists because CPython's memory management (reference counting) is not thread-safe. * I/O Bound: Multithreading works fine; the GIL is released during I/O operations (network, file). * CPU Bound: Multithreading is useless; use Multiprocessing to bypass the GIL.

13. Python 3.13 introduced a "Free-Threaded" build (PEP 703). What does this mean?

This is the biggest change in Python's history. The free-threaded build (or no-GIL build) makes the GIL optional. This allows true parallel execution of Python threads across CPU cores. While experimental in 3.13, by 2026, it will be a major topic. C-Extensions will need to be adapted to be thread-safe to take advantage of this.

14. Multiprocessing vs. Multithreading vs. Asyncio?

  • Multithreading: Good for I/O-bound tasks. Threads share memory, but subject to the GIL.
  • Multiprocessing: Good for CPU-bound tasks. Each process has its own memory space and Python interpreter (bypasses GIL). High memory overhead.
  • Asyncio: Single-threaded, concurrent code using an event loop. Best for massive I/O-bound tasks (thousands of simultaneous network connections) with the lowest overhead.

15. How does the Asyncio Event Loop work?

The event loop runs in a single thread. It keeps track of coroutines and tasks. When a coroutine hits an await (an I/O operation), it yields control back to the event loop. The loop then executes other tasks. When the I/O is ready, the loop resumes the paused coroutine.

16. What are Async Generators?

Combining async def and yield. They allow you to asynchronously stream data.

async def fetch_stream():
    async for item in database_cursor:
        yield item

async def main():
    async for item in fetch_stream():
        print(item)

Section 4: Modern Python & The Type System

17. What is Duck Typing vs. Structural Subtyping (Protocols)?

  • Duck Typing: "If it walks like a duck and quacks like a duck, it's a duck." Python doesn't care about the actual type, only if the method exists at runtime.
  • Protocols (PEP 544): Static typing's version of duck typing. You define a Protocol class with methods. Any class that implements those methods satisfies the Protocol, without explicitly inheriting from it.

18. Explain TypeVar and Generics.

Generics allow you to write functions or classes that work with any type, while preserving type information.

from typing import TypeVar, Generic

T = TypeVar('T')

def first(items: list[T]) -> T:
    return items[0]

# The type checker knows first([1,2]) returns int, 
# and first(["a"]) returns str

19. What is ParamSpec?

Introduced to type decorators properly. It captures the parameter signature of a function, allowing you to write decorators that accept *args, **kwargs without losing the original function's type hints.

20. Explain Structural Pattern Matching (match/case).

Introduced in Python 3.10, it is much more powerful than C-style switch statements. It matches against structure, not just values.

def handle_command(command):
    match command.split():
        case ["quit"]:
            exit()
        case ["go", direction]:
            move(direction)
        case ["pick", "up", item]:
            add_to_inventory(item)
        case _:
            print("Unknown command")

21. pydantic vs dataclasses?

  • Dataclasses: Standard library, pure boilerplate reduction, no validation.
  • Pydantic: Third-party (now foundational to FastAPI). Validates data at runtime using type hints, automatically parsing strings to ints/dates, and raising ValidationError if data is malformed. Essential for API boundaries.

Section 5: Architecture & Ecosystem

22. How do you manage dependencies in 2026? (uv vs pip)

While pip and requirements.txt are classic, modern Python relies on pyproject.toml. uv (by Astral, makers of Ruff) is rapidly becoming the standard. It is a drop-in replacement for pip, pip-tools, virtualenv, and poetry, written in Rust for 10-100x faster dependency resolution and installation.

23. What is the difference between WSGI and ASGI?

  • WSGI: Synchronous web server interface (e.g., Gunicorn + Flask/Django). Handles one request per thread.
  • ASGI: Asynchronous Server Gateway Interface (e.g., Uvicorn + FastAPI/Django). Supports async/await, WebSockets, and long-polling HTTP connections.

24. How do you profile a Python application?

  1. cProfile: Built-in. python -m cProfile -s time my_script.py to see where time is spent.
  2. py-spy: A sampling profiler that runs without restarting the program or modifying code.
  3. memray: Modern memory profiler (developed by Bloomberg) that tracks memory allocations in Python code, C extensions, and the OS.

25. When should you use C-Extensions (or Cython/Rust)?

When Python is too slow for CPU-bound tasks and multiprocessing overhead is too high. Tools like Cython (Python-like syntax compiling to C) or PyO3 (writing native Python modules in Rust) allow you to rewrite the critical 5% of your codebase in a compiled language, bypassing the GIL and achieving native speed.

Related Articles