Python45 min read

Python Interview Questions: 50 Essential Questions for Developers

Comprehensive collection of 50 essential Python interview questions covering data structures, OOP, decorators, generators, and advanced Python concepts. Free Python interview questions with answers. Python interview prep guide.

Emily Chen
December 16, 2025
0.0k0

This comprehensive guide covers 50 essential Python interview questions that every Python developer should know. These questions cover fundamental concepts, data structures, object-oriented programming, advanced features, and best practices commonly asked in technical interviews.

Core Concepts

Understanding Python's core concepts is essential for any Python developer. These questions test your knowledge of data types, variables, operators, and Python's unique features.

Data Structures

Python provides powerful built-in data structures. Master these questions to demonstrate your understanding of lists, dictionaries, tuples, sets, and their use cases.

Object-Oriented Programming

Python supports OOP with classes, inheritance, polymorphism, and encapsulation. These questions cover class design, method resolution order, and Python's OOP features.

Advanced Features

Python's advanced features include decorators, generators, context managers, and metaprogramming. These questions test your ability to use Python's powerful features effectively.

Best Practices & Libraries

Production-ready Python code requires understanding best practices, popular libraries, and Pythonic code style. These questions cover PEP guidelines, testing, and common libraries.

#Python#Python3#Python Programming#Interview#Data Structures#OOP#Advanced#Python Interview#Python Tutorial

Common Questions & Answers

Q1

What is Python and what are its main features?

A

Python is a high-level, interpreted programming language. Main features include dynamic typing, automatic memory management, extensive standard library, multiple programming paradigms (OOP, functional, procedural), simple syntax, and cross-platform compatibility.

Q2

What is the difference between list and tuple?

A

Lists are mutable (can be modified), use square brackets [], slower for large data. Tuples are immutable (cannot be modified), use parentheses (), faster, can be used as dictionary keys. Use lists for dynamic data, tuples for fixed data.

python
# List - mutable
my_list = [1, 2, 3]
my_list.append(4)  # OK

# Tuple - immutable
my_tuple = (1, 2, 3)
my_tuple.append(4)  # Error
Q3

What is a dictionary in Python?

A

Dictionary is unordered collection of key-value pairs. Keys must be immutable (strings, numbers, tuples). Access values by key. Fast lookups O(1). Created with {} or dict(). Methods: get(), keys(), values(), items().

python
my_dict = {'name': 'John', 'age': 30}
print(my_dict['name'])  # John
print(my_dict.get('age'))  # 30
print(my_dict.keys())  # dict_keys(['name', 'age'])
Q4

What is list comprehension?

A

List comprehension is concise way to create lists. Syntax: [expression for item in iterable if condition]. More readable and faster than loops. Can be nested. Alternative to map() and filter().

python
# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(10) if x % 2 == 0]
nested = [[i*j for j in range(3)] for i in range(3)]
Q5

What is the difference between == and is?

A

== compares values (equality). is compares object identity (same object in memory). Use == for value comparison, is for checking if variables point to same object. Small integers and strings may be interned.

python
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)  # True (same values)
print(a is b)  # False (different objects)

c = a
print(a is c)  # True (same object)
Q6

What are Python decorators?

A

Decorators are functions that modify other functions. Use @decorator syntax. Wraps function, adds functionality before/after execution. Used for logging, timing, authentication, caching. Can accept arguments.

python
def my_decorator(func):
    def wrapper():
        print("Before")
        func()
        print("After")
    return wrapper

@my_decorator
def say_hello():
    print("Hello")
Q7

What is a generator in Python?

A

Generator is function that yields values instead of returning. Uses yield keyword. Lazy evaluation - produces values on demand. Memory efficient for large sequences. Returns generator object. Can be iterated once.

python
def countdown(n):
    while n > 0:
        yield n
        n -= 1

for num in countdown(5):
    print(num)  # 5, 4, 3, 2, 1
Q8

What is the difference between append() and extend()?

A

append() adds single element to end of list. extend() adds all elements from iterable to list. append() increases length by 1, extend() increases by length of iterable.

python
my_list = [1, 2, 3]
my_list.append(4)  # [1, 2, 3, 4]
my_list.append([5, 6])  # [1, 2, 3, 4, [5, 6]]

my_list = [1, 2, 3]
my_list.extend([4, 5])  # [1, 2, 3, 4, 5]
Q9

What is *args and **kwargs?

A

*args collects positional arguments into tuple. **kwargs collects keyword arguments into dictionary. Allows functions to accept variable number of arguments. *args before **kwargs. Useful for wrapper functions.

python
def my_func(*args, **kwargs):
    print(args)  # (1, 2, 3)
    print(kwargs)  # {'a': 4, 'b': 5}

my_func(1, 2, 3, a=4, b=5)
Q10

What is lambda function?

A

Lambda is anonymous function defined with lambda keyword. Syntax: lambda arguments: expression. Single expression only. Used for short functions, map(), filter(), sorted(). More concise than def.

python
# Lambda
square = lambda x: x**2
print(square(5))  # 25

# With map
numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x**2, numbers))  # [1, 4, 9, 16]
Q11

What is the difference between range() and xrange()?

A

range() (Python 3) returns range object (lazy), memory efficient. xrange() (Python 2) was similar. In Python 3, xrange() removed, range() behaves like old xrange(). Both generate numbers on demand.

python
# Python 3
r = range(10)  # range object
print(list(r))  # [0, 1, 2, ..., 9]

for i in range(5):
    print(i)  # 0, 1, 2, 3, 4
Q12

What is __init__ method?

A

__init__ is constructor method called when object is created. Initializes object attributes. First parameter is self (instance). Can accept arguments. Not required but commonly used. Called automatically.

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

person = Person("John", 30)
Q13

What is the difference between class method and static method?

A

Class method takes cls (class) as first parameter, can access/modify class state. Static method takes no special first parameter, cannot access class/instance. Use @classmethod and @staticmethod decorators.

python
class MyClass:
    @classmethod
    def class_method(cls):
        return cls.__name__
    
    @staticmethod
    def static_method():
        return "Static"

MyClass.class_method()  # 'MyClass'
MyClass.static_method()  # 'Static'
Q14

What is inheritance in Python?

A

Inheritance allows class to inherit attributes and methods from parent class. Child class extends parent. Use super() to call parent methods. Supports multiple inheritance. Method Resolution Order (MRO) determines method lookup.

python
class Animal:
    def speak(self):
        return "Sound"

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

dog = Dog()
print(dog.speak())  # Woof
Q15

What is the difference between shallow copy and deep copy?

A

Shallow copy creates new object but references nested objects. Deep copy creates new object and recursively copies nested objects. Use copy.copy() for shallow, copy.deepcopy() for deep. Shallow is faster.

python
import copy

original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)

original[0][0] = 99
# shallow: [[99, 2], [3, 4]]
# deep: [[1, 2], [3, 4]]
Q16

What is a context manager?

A

Context manager manages resources with __enter__ and __exit__ methods. Used with with statement. Ensures cleanup even if exception occurs. Examples: file operations, database connections. Can use @contextmanager decorator.

python
# Using with statement
with open('file.txt', 'r') as f:
    content = f.read()
# File automatically closed

# Custom context manager
class MyContext:
    def __enter__(self):
        return self
    def __exit__(self, *args):
        pass
Q17

What is the difference between __str__ and __repr__?

A

__str__ returns human-readable string, used by str() and print(). __repr__ returns unambiguous string, used by repr(). __repr__ should be valid Python code. __str__ calls __repr__ if not defined.

python
class Person:
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return f"Person({self.name})"
    def __repr__(self):
        return f"Person('{self.name}')"

p = Person("John")
print(str(p))  # Person(John)
print(repr(p))  # Person('John')
Q18

What is the Global Interpreter Lock (GIL)?

A

GIL is mutex that allows only one thread to execute Python bytecode at a time. Prevents true parallelism in multi-threading for CPU-bound tasks. I/O-bound tasks can benefit from threads. Use multiprocessing for CPU parallelism.

python
# Threading (limited by GIL)
import threading
def task():
    pass
thread = threading.Thread(target=task)
thread.start()

# Multiprocessing (bypasses GIL)
import multiprocessing
process = multiprocessing.Process(target=task)
process.start()
Q19

What is the difference between module and package?

A

Module is single Python file (.py) containing code. Package is directory containing modules and __init__.py file. Packages organize related modules. Import modules with import, packages with dot notation.

python
# Module: math.py
import math
print(math.pi)

# Package: mypackage/
#   __init__.py
#   module1.py
#   module2.py
from mypackage import module1
Q20

What is set in Python?

A

Set is unordered collection of unique elements. No duplicates allowed. Mutable. Fast membership testing O(1). Created with {} or set(). Methods: add(), remove(), union(), intersection(). Use for unique values.

python
my_set = {1, 2, 3, 3}  # {1, 2, 3}
my_set.add(4)
my_set.remove(2)

set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1.union(set2))  # {1, 2, 3, 4, 5}
Q21

What is the difference between remove() and discard()?

A

remove() raises KeyError if element not found. discard() does nothing if element not found. Use remove() when you want to know if element exists, discard() when you don't care.

python
my_set = {1, 2, 3}
my_set.remove(2)  # OK
my_set.remove(4)  # KeyError

my_set = {1, 2, 3}
my_set.discard(2)  # OK
my_set.discard(4)  # No error
Q22

What is dictionary comprehension?

A

Dictionary comprehension creates dictionaries concisely. Syntax: {key: value for item in iterable if condition}. Similar to list comprehension but creates key-value pairs. More readable than dict() with loops.

python
# Dictionary comprehension
squares = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

filtered = {k: v for k, v in my_dict.items() if v > 10}
Q23

What is the difference between pass, continue, and break?

A

pass does nothing, placeholder for empty blocks. continue skips to next iteration in loop. break exits loop immediately. pass for syntax, continue for skipping, break for exiting.

python
for i in range(5):
    if i == 2:
        pass  # Do nothing
    if i == 3:
        continue  # Skip to next iteration
    if i == 4:
        break  # Exit loop
    print(i)  # 0, 1, 3
Q24

What is __name__ == "__main__"?

A

__name__ is special variable. When script runs directly, __name__ is "__main__". When imported as module, __name__ is module name. Used to execute code only when script is run directly, not when imported.

python
# mymodule.py
def my_function():
    pass

if __name__ == "__main__":
    # This runs only when script executed directly
    my_function()
Q25

What is the difference between iterable and iterator?

A

Iterable is object that can be iterated (has __iter__). Iterator is object with __next__ method. Iterators are iterables. iter() converts iterable to iterator. Generators are iterators.

python
# Iterable
my_list = [1, 2, 3]
for item in my_list:  # OK
    print(item)

# Iterator
my_iter = iter(my_list)
print(next(my_iter))  # 1
print(next(my_iter))  # 2
Q26

What is the difference between method and function?

A

Method is function defined inside class, takes self as first parameter. Function is defined outside class, no self parameter. Methods belong to class/instance, functions are standalone.

python
# Function
def my_function(x):
    return x * 2

# Method
class MyClass:
    def my_method(self, x):
        return x * 2
Q27

What is exception handling in Python?

A

Exception handling uses try, except, else, finally blocks. try contains code that might raise exception. except catches exceptions. else runs if no exception. finally always runs. Use for error handling.

python
try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero")
except Exception as e:
    print(f"Error: {e}")
else:
    print("No error")
finally:
    print("Always runs")
Q28

What is the difference between del, remove(), and pop()?

A

del removes item by index, raises IndexError if invalid. remove() removes first occurrence by value, raises ValueError if not found. pop() removes and returns item by index, raises IndexError if invalid.

python
my_list = [1, 2, 3, 2]
del my_list[0]  # [2, 3, 2]
my_list.remove(2)  # [3, 2] - removes first 2
value = my_list.pop()  # 2, my_list = [3]
Q29

What is the difference between __getattr__ and __getattribute__?

A

__getattr__ called only when attribute not found through normal lookup. __getattribute__ called for every attribute access. __getattribute__ must call super() or raise AttributeError. Use __getattr__ for fallback.

python
class MyClass:
    def __getattr__(self, name):
        return f"Attribute {name} not found"
    
    def __getattribute__(self, name):
        # Called for all attribute access
        return super().__getattribute__(name)
Q30

What is the difference between @staticmethod and @classmethod?

A

@staticmethod doesn't take self or cls, cannot access class/instance. @classmethod takes cls, can access class but not instance. Static for utility functions, classmethod for alternative constructors.

python
class MyClass:
    @staticmethod
    def static_method():
        return "Static"
    
    @classmethod
    def class_method(cls):
        return cls.__name__
Q31

What is the purpose of __slots__?

A

__slots__ restricts attributes that can be set on instance. Reduces memory usage by preventing __dict__ creation. Faster attribute access. Trade-off: cannot add new attributes dynamically. List of allowed attribute names.

python
class Person:
    __slots__ = ['name', 'age']
    
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person("John", 30)
p.city = "NYC"  # AttributeError
Q32

What is the difference between map(), filter(), and reduce()?

A

map() applies function to each item, returns iterator. filter() returns items where function is True. reduce() (from functools) applies function cumulatively, returns single value. All take function and iterable.

python
numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x**2, numbers))  # [1, 4, 9, 16]
evens = list(filter(lambda x: x % 2 == 0, numbers))  # [2, 4]

from functools import reduce
sum = reduce(lambda x, y: x + y, numbers)  # 10
Q33

What is the difference between __new__ and __init__?

A

__new__ creates instance (class method), called before __init__. __init__ initializes instance. __new__ returns instance. Use __new__ for immutable types, singletons. __init__ for normal initialization.

python
class MyClass:
    def __new__(cls):
        instance = super().__new__(cls)
        return instance
    
    def __init__(self):
        self.value = 10
Q34

What is the purpose of enumerate()?

A

enumerate() adds counter to iterable. Returns (index, value) tuples. Useful when you need both index and value in loop. More Pythonic than range(len()). Can specify start index.

python
items = ['a', 'b', 'c']
for index, value in enumerate(items):
    print(index, value)  # 0 a, 1 b, 2 c

for i, item in enumerate(items, start=1):
    print(i, item)  # 1 a, 2 b, 3 c
Q35

What is the difference between is and == for None?

A

Use is for None comparison (identity check). == works but is preferred. None is singleton, all None references are same object. PEP 8 recommends "is None" and "is not None".

python
x = None
print(x is None)  # True (preferred)
print(x == None)  # True (works but not preferred)
print(x is not None)  # False
Q36

What is the purpose of zip()?

A

zip() combines multiple iterables element-wise. Returns iterator of tuples. Stops at shortest iterable. Useful for parallel iteration. Can unzip with * operator. Common in loops and data processing.

python
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]

for name, age in zip(names, ages):
    print(f"{name} is {age}")

# Unzip
pairs = list(zip(names, ages))
names, ages = zip(*pairs)
Q37

What is the difference between mutable and immutable types?

A

Mutable types can be changed after creation: list, dict, set. Immutable types cannot: int, float, str, tuple, frozenset. Immutable objects are hashable (can be dict keys). Mutable objects are not hashable.

python
# Mutable
my_list = [1, 2, 3]
my_list.append(4)  # OK

# Immutable
my_tuple = (1, 2, 3)
my_tuple.append(4)  # Error

my_dict = {my_tuple: "value"}  # OK (tuple is hashable)
my_dict = {my_list: "value"}  # Error (list not hashable)
Q38

What is the purpose of collections module?

A

collections provides specialized data structures. Counter (count elements), defaultdict (default values), OrderedDict (ordered dict), deque (double-ended queue), namedtuple (tuple with names). More efficient than built-ins for specific use cases.

python
from collections import Counter, defaultdict, deque

counter = Counter([1, 2, 2, 3, 3, 3])  # {3: 3, 2: 2, 1: 1}
dd = defaultdict(list)  # Default value is list
dq = deque([1, 2, 3])  # Fast append/pop from both ends
Q39

What is the difference between import and from import?

A

import loads entire module, access with module.name. from import loads specific items, use directly. import preserves namespace, from import pollutes namespace. Use import for clarity, from for convenience.

python
# import
import math
print(math.pi)

# from import
from math import pi
print(pi)
Q40

What is the purpose of __init__.py?

A

__init__.py makes directory a Python package. Can be empty or contain initialization code. Python 3.3+ supports namespace packages without __init__.py, but it's still recommended. Used for package initialization.

python
# mypackage/__init__.py
# Can be empty or contain:
from .module1 import function1
__all__ = ['function1']
Q41

What is the difference between append() and insert()?

A

append() adds element to end of list. insert() adds element at specific index. append() is O(1), insert() is O(n) due to shifting. Use append() when possible for better performance.

python
my_list = [1, 2, 3]
my_list.append(4)  # [1, 2, 3, 4]
my_list.insert(0, 0)  # [0, 1, 2, 3, 4]
my_list.insert(2, 1.5)  # [0, 1, 1.5, 2, 3, 4]
Q42

What is the purpose of __all__?

A

__all__ defines public API of module. List of names exported by "from module import *". Controls what gets imported. Good practice for modules. If not defined, all non-underscore names are exported.

python
# mymodule.py
__all__ = ['public_function', 'PUBLIC_CONSTANT']

def public_function():
    pass

def _private_function():
    pass
Q43

What is the difference between single and double underscore?

A

Single underscore (_name) indicates "internal use" convention, not enforced. Double underscore (__name) triggers name mangling, makes attribute harder to access. Use _ for private convention, __name__ for special methods.

python
class MyClass:
    def __init__(self):
        self._internal = 1  # Convention: internal
        self.__private = 2  # Name mangling
    
obj = MyClass()
print(obj._internal)  # 1 (accessible but convention)
print(obj.__private)  # AttributeError
print(obj._MyClass__private)  # 2 (mangled name)
Q44

What is the purpose of functools module?

A

functools provides higher-order functions. lru_cache (memoization), partial (partial function application), wraps (preserve metadata), reduce (cumulative operation). Useful for functional programming and optimization.

python
from functools import lru_cache, partial

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

# Partial application
def multiply(x, y):
    return x * y
double = partial(multiply, 2)
Q45

What is the difference between str and repr?

A

str() calls __str__, returns human-readable string. repr() calls __repr__, returns unambiguous string (ideally valid Python code). repr() used in debugger, containers. str() used by print().

python
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __str__(self):
        return f"({self.x}, {self.y})"
    def __repr__(self):
        return f"Point({self.x}, {self.y})"

p = Point(1, 2)
print(str(p))  # (1, 2)
print(repr(p))  # Point(1, 2)
Q46

What is the purpose of itertools module?

A

itertools provides iterator building blocks. Functions: count(), cycle(), repeat(), chain(), combinations(), permutations(), product(). Memory efficient, lazy evaluation. Useful for iteration patterns.

python
from itertools import count, cycle, combinations

# Infinite counter
for i in count(10, 2):
    if i > 20:
        break
    print(i)  # 10, 12, 14, 16, 18, 20

# Combinations
print(list(combinations([1, 2, 3], 2)))  # [(1,2), (1,3), (2,3)]
Q47

What is the difference between tuple and namedtuple?

A

Tuple is immutable sequence. namedtuple (from collections) is tuple subclass with named fields. Access by index or name. More readable than regular tuples. Immutable like tuples. Lightweight alternative to class.

python
from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
print(p.x)  # 1 (by name)
print(p[0])  # 1 (by index)
print(p)  # Point(x=1, y=2)
Q48

What is the purpose of contextlib module?

A

contextlib provides utilities for context managers. @contextmanager decorator creates context managers from generators. closing() for objects with close(). Useful for creating custom context managers easily.

python
from contextlib import contextmanager

@contextmanager
def my_context():
    print("Enter")
    yield "value"
    print("Exit")

with my_context() as val:
    print(val)  # value
Q49

What is the difference between threading and multiprocessing?

A

threading uses threads (shared memory, limited by GIL for CPU tasks). multiprocessing uses processes (separate memory, true parallelism). Use threads for I/O-bound, processes for CPU-bound. Processes have overhead.

python
# Threading
import threading
def task():
    pass
thread = threading.Thread(target=task)
thread.start()

# Multiprocessing
import multiprocessing
process = multiprocessing.Process(target=task)
process.start()
Q50

What is the purpose of unittest module?

A

unittest is Python testing framework. Create test classes inheriting unittest.TestCase. Use assert methods (assertEqual, assertTrue, etc.). Run with unittest.main() or pytest. Supports fixtures, mocking. Standard library testing solution.

python
import unittest

class TestMath(unittest.TestCase):
    def test_add(self):
        self.assertEqual(1 + 1, 2)
    
    def test_multiply(self):
        self.assertTrue(2 * 3 == 6)

if __name__ == '__main__':
    unittest.main()