Python Interview Cheat Sheet
Top 50 interview questions with concise answers. Print this page or save as PDF for offline study.
1. What are Python's key features?
Python key features: simple and readable syntax, dynamically typed, interpreted, garbage-collected memory management, extensive standard library, multi-paradigm (OOP, functional, procedural), large ecosystem (pip packages), cross-platform, and strong community. Popular for web (Django, Flask), data science (pandas, numpy), ML/AI, scripting, and automation.
2. What is the difference between Python 2 and Python 3?
Python 2 (EOL 2020): print is a statement, integer division by default, str is bytes, unicode is separate type, range() returns a list. Python 3: print() is a function, true division by default, str is unicode, bytes is separate, range() returns a lazy iterator. Python 3 is the only supported version now - all new code should use Python 3.
3. What are Python data types?
Python built-in data types: int, float, complex (numbers), str (text), bool (True/False), list (mutable ordered sequence), tuple (immutable ordered sequence), dict (key-value mapping), set (unique unordered elements), frozenset (immutable set), bytes, bytearray, NoneType. Everything in Python is an object including these primitives.
4. Explain Python variable scoping rules.
Python scoping follows LEGB rule: Local (inside function), Enclosing (in outer function for nested functions), Global (module level), Built-in (Python built-ins). Variables are looked up in this order. global keyword declares a variable is from global scope. nonlocal keyword (Python 3) modifies variable from enclosing function scope.
5. How does Python manage memory?
Python uses reference counting as primary memory management - each object tracks how many references point to it. When count drops to 0, memory is freed immediately. A cyclic garbage collector handles reference cycles (A references B, B references A). CPython manages memory in pools and arenas via pymalloc. You can force GC with gc.collect() but rarely need to.
6. What are lists, tuples, sets, and dictionaries?
List: mutable, ordered, allows duplicates [1, 2, 3]. Tuple: immutable, ordered, allows duplicates (1, 2, 3) - faster than list, used for fixed data. Set: mutable, unordered, unique elements {1, 2, 3} - O(1) membership test. Dict: mutable, key-value pairs, keys must be hashable {key: value}. Choose based on: mutability, order needs, uniqueness, and lookup pattern.
7. Explain Python functions.
Python functions are first-class objects - assignable to variables, passable as arguments, returnable from other functions. def defines a function, lambda creates anonymous single-expression functions. Default parameter values, *args (variable positional args), **kwargs (variable keyword args). Closures capture variables from enclosing scope. Nested functions, decorators, and higher-order functions are all supported.
8. What are Python modules and packages?
Module: a single .py file. Package: a directory with __init__.py containing multiple modules. Import with import module or from module import name. Python searches sys.path for modules. pip installs third-party packages from PyPI into site-packages. Virtual environments (venv) isolate packages per project. __init__.py can define what the package exposes.
9. Explain Python exception handling.
Python exception handling: try block runs code that might fail. except catches specific exceptions (except ValueError) or all exceptions (except Exception). else runs if no exception occurred. finally always runs (cleanup). raise raises an exception manually. Use specific exception types - never bare except: which hides bugs. Create custom exceptions by subclassing Exception.
10. What are Python decorators?
Decorators are functions that wrap another function to add behavior without modifying its code. Applied with @decorator syntax above function definition. Common uses: logging, timing, authentication, caching. A decorator takes a function, returns a new function that calls the original. Use functools.wraps(func) to preserve the wrapped function's name and docstring.
11. How does Python handle file operations?
Python file I/O: open(filename, mode) returns a file object. Modes: "r" (read), "w" (write), "a" (append), "b" (binary), "x" (exclusive create). Use with open(...) as f: (context manager) to automatically close the file even if an exception occurs. Read: f.read(), f.readline(), f.readlines(). Write: f.write(). Use pathlib.Path for modern path handling.
12. Explain Python iterators and generators.
Iterator: object with __iter__() and __next__() methods that produces values one at a time. Generator: function using yield that automatically becomes an iterator - pauses at each yield and resumes from there. Generators are lazy (compute on demand, memory efficient). Use generators for large sequences where computing all values upfront would be expensive. Generator expression: (x**2 for x in range(10)).
13. How do Python comprehensions work?
Comprehensions create collections concisely. List: [expr for item in iterable if condition]. Dict: {k: v for k, v in items}. Set: {expr for item in iterable}. Generator: (expr for item in iterable) - lazy, memory efficient. More readable and often faster than equivalent for loops. Avoid deeply nested comprehensions - they become hard to read.
14. Explain *args and **kwargs.
*args captures any number of positional arguments as a tuple. def func(*args): receives func(1, 2, 3) as args = (1, 2, 3). **kwargs captures keyword arguments as a dict. def func(**kwargs): receives func(a=1, b=2) as kwargs = {"a": 1, "b": 2}. Order in function signature: positional, *args, keyword-only, **kwargs. Unpacking: func(*mylist) and func(**mydict).
15. What are Python's built-in structures for stacks and queues?
Stack: use a list with .append() (push) and .pop() (pop from end) - O(1). Or collections.deque for thread-safe stack. Queue (FIFO): collections.deque with .append() and .popleft() - O(1) both ends. queue.Queue for thread-safe producer-consumer. PriorityQueue: queue.PriorityQueue or heapq module. Avoid using list.pop(0) for queue - it's O(n).
16. How does Python support OOP?
Python supports OOP with classes using class keyword. Features: encapsulation (instance variables and methods), inheritance (class Child(Parent)), polymorphism (method overriding), multiple inheritance (class C(A, B)). Special methods (__init__, __str__, __repr__, __len__) customize behavior. Python is duck-typed - objects are used based on behavior, not type.
17. How does Python handle memory for objects?
Python objects live on the heap. Each object has a reference count tracked by CPython. When you assign a = [1,2,3], the list object is created on the heap and "a" is a name binding to it. Multiple names can reference the same object (a = b = [] means a and b point to the same list). Objects are garbage collected when no references remain. Large objects may need explicit gc.collect().
18. How do you manage packages in Python?
Package management: pip install packagename installs from PyPI. pip freeze > requirements.txt exports dependencies. pip install -r requirements.txt installs from file. Virtual environments: python -m venv venv creates isolated environment. activate it before installing packages. Poetry and Pipenv are modern alternatives that handle virtual envs and dependencies together. conda for data science environments with binary packages.
19. Explain Python's Global Interpreter Lock (GIL).
The GIL (Global Interpreter Lock) is a mutex in CPython that allows only one thread to execute Python bytecode at a time. This means CPU-bound multi-threaded code doesn't actually run in parallel. I/O-bound threads still benefit (GIL is released during I/O). For CPU-bound parallelism, use multiprocessing (separate processes, no GIL) or C extensions that release the GIL.
20. What are __init__.py and __main__ used for?
__init__.py makes a directory a Python package (can be empty). Controls what gets imported with from package import *. __main__.py lets a package be run directly: python -m mypackage. if __name__ == "__main__": guard runs code only when script is executed directly (not when imported as a module). Essential for making scripts work both as executables and importable modules.
21. Explain Python's multiple inheritance and MRO.
Python supports multiple inheritance: class C(A, B). MRO (Method Resolution Order) determines which method to call using C3 linearization algorithm. Check with ClassName.__mro__ or ClassName.mro(). super() follows MRO correctly - critical for cooperative multiple inheritance. Diamond problem is resolved by MRO - each class appears once in the resolution order.
22. What are Python magic or dunder methods?
Dunder (double underscore) methods are special methods that Python calls implicitly. __init__ (construction), __str__ (string representation for print), __repr__ (detailed representation), __len__ (len()), __getitem__ (indexing []), __iter__ (iteration), __eq__ (==), __lt__ (<), __add__ (+), __enter__/__exit__ (with statement), __call__ (callable objects). Implementing them makes your classes work naturally with Python built-ins.
23. What are Python metaclasses?
Metaclass is the class of a class - controls how classes are created. Default metaclass is type. Custom metaclass: class MyMeta(type): override __new__ or __init__. Use class MyClass(metaclass=MyMeta). Use cases: ORM field registration (Django models), API validation, singleton enforcement, automatic method registration. Metaclasses are powerful but complex - often a class decorator or __init_subclass__ is simpler.
24. Difference between iterators and iterables.
Iterable: object you can loop over - has __iter__() returning an iterator. Lists, tuples, strings, dicts are iterables. Iterator: object with __next__() - stateful, remembers position, consumed once. Iterables can create multiple iterators. iter(mylist) creates an iterator from a list. Generators are iterators. For-loop calls iter() then repeatedly calls next() until StopIteration.
25. How do generators improve performance?
Generators use lazy evaluation - values produced one at a time on demand, not all at once. Memory efficient: a generator for 1 million numbers uses constant memory vs a list using millions of bytes. Pipelines: chain generators to process data step-by-step (like Unix pipes). yield pauses execution and returns a value; next() resumes from where it left off. Use for large files, infinite sequences, data pipelines.
26. Explain Python decorators in depth.
A decorator wraps a function: def my_decorator(func): def wrapper(*args, **kwargs): # before; result = func(*args, **kwargs); # after; return result; return wrapper. Apply with @my_decorator. Decorators with arguments need an extra level of nesting. Class decorators work on classes. functools.wraps preserves metadata. Built-in decorators: @property, @staticmethod, @classmethod, @functools.lru_cache.
27. How does Python handle closures?
A closure is a nested function that captures and remembers variables from its enclosing scope even after the outer function returns. The enclosed variables form a "cell" object. Closures are the mechanism behind decorators and factory functions. Variables must be declared nonlocal to be assigned inside a closure. Closures in loops: use default argument (i=i) to capture current value.
28. Explain with statement and context managers.
The with statement ensures resources are properly acquired and released. A context manager implements __enter__ (called when entering with block - can return a value to as clause) and __exit__ (called on exit - even if exception - handles cleanup). Common examples: open() for files, threading.Lock(), database connections. Create custom context managers with contextlib.contextmanager decorator.
29. How do you perform logging in Python?
Python logging: import logging, use logging.getLogger(__name__) to get a logger. Log levels: DEBUG, INFO, WARNING, ERROR, CRITICAL. Configure with basicConfig or a config dict. Add handlers (StreamHandler for console, FileHandler for file, RotatingFileHandler for log rotation). Use formatters to include timestamp, level, module. Prefer logging over print() in any code beyond simple scripts.
30. How do you handle exceptions and create custom exceptions?
Handle exceptions: try/except/else/finally blocks. Catch specific exceptions (except ValueError as e). Custom exceptions: class ValidationError(Exception): pass - inherit from Exception or a more specific base. Add custom attributes in __init__. Re-raise with raise (no args) inside except. Exception chaining: raise NewError() from original_error. Always clean up in finally.
31. How do you perform unit testing in Python?
Python unit testing: use unittest (built-in) or pytest (more popular). unittest: subclass TestCase, write test_* methods, use self.assertEqual/assertRaises/etc. pytest: just write functions starting with test_, use assert statements (pytest rewrites them for detailed failures). Fixtures, parametrize, and mocking with unittest.mock. Run with pytest or python -m unittest. Aim for 80%+ coverage.
32. Advanced use of *args and **kwargs.
Advanced *args/**kwargs: forwarding arguments to another function (func(*args, **kwargs)), combining with keyword-only args (def f(a, b, *, key_only)), unpacking in function calls (f(*list, **dict)). Keyword-only args (after *): def f(a, *, b) forces b to be passed by keyword. Positional-only args (before /): def f(a, b, /) forces a, b to be positional. Both together for fine-grained API control.
33. Explain Python's itertools module.
itertools provides efficient iterator building blocks. chain() combines multiple iterables. cycle() repeats infinitely. islice() slices iterators. groupby() groups consecutive elements. product() computes Cartesian product. permutations() and combinations(). count() infinite counter. dropwhile()/takewhile() conditional iterators. All are lazy (memory efficient). Used heavily in data processing pipelines.
34. Explain functools and lru_cache.
functools module for higher-order functions. lru_cache (Least Recently Used) memoizes a function - caches results keyed by arguments. @lru_cache(maxsize=128) or @cache (Python 3.9, unlimited). Perfect for recursive algorithms (fibonacci) or expensive computations called repeatedly with same inputs. partial() creates new function with pre-filled arguments. reduce() applies function cumulatively.
35. How do you handle file operations with context managers?
Context managers for file operations: with open("file.txt", "r") as f: data = f.read(). File closes automatically even if an exception occurs. Process files line-by-line to save memory: for line in f: process(line). Use pathlib.Path for modern file path manipulation: Path("dir") / "file.txt". Read CSV with csv module, JSON with json module. Write binary with "wb" mode.
36. How do you connect Python with databases?
Python database access: SQLite built-in (sqlite3 module). PostgreSQL/MySQL via psycopg2 or PyMySQL. ORMs: SQLAlchemy (most powerful, any DB) or Django ORM (built into Django). Use parameterized queries to prevent SQL injection (cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))). Always use context managers or try/finally to close connections.
37. How do you implement serialization and deserialization?
Serialization: convert Python objects to a format for storage or transmission. json module for JSON (human-readable, web-friendly). pickle module for Python-specific binary serialization (any Python object). marshal for Python bytecode. Third-party: marshmallow (validation + serialization), dataclasses with asdict(). Caution: never unpickle data from untrusted sources (security risk).
38. Explain Python's datetime and time modules.
datetime module: datetime.datetime for date+time, date for date-only, time for time-only, timedelta for durations. strftime(format) converts datetime to string. strptime(string, format) parses string to datetime. Use timezone-aware datetimes: datetime.now(timezone.utc). Python 3.9+ has zoneinfo for IANA timezone support. dateutil library simplifies parsing and timezone handling.
39. How do you implement caching in Python?
Caching in Python: functools.lru_cache for in-memory function result caching. dict for simple key-value caching. cachetools library for LRU, TTL, and other cache types. Redis (via redis-py) for distributed caching across processes. Django/Flask caching backends (memcached, Redis). Cache invalidation strategy: TTL-based expiry, event-based invalidation, or versioned cache keys.
40. How do you profile and optimize Python code?
Python profiling tools: cProfile (built-in, function-level timing - python -m cProfile script.py). profile module (pure Python, slower). line_profiler (@profile decorator, line-by-line timing). memory_profiler (memory usage per line). py-spy (sampling profiler, low overhead, production-safe). timeit for benchmarking small code snippets. Always profile before optimizing - don't guess bottlenecks.
41. What are Python descriptors and how are they used?
Descriptors implement __get__, __set__, __delete__ to control attribute access on a class. Used when an attribute needs validation or computation on access. @property is a built-in descriptor. Custom descriptors: class Validator: def __set__(self, obj, value): validate(value); obj.__dict__[name] = value. Used by SQLAlchemy and Django ORM to define column types and relationships.
42. What is __slots__ in Python and why use it?
__slots__ declares a fixed set of attributes for a class, replacing the per-instance __dict__. Benefits: lower memory usage (no dict overhead per instance), slightly faster attribute access, prevents dynamic attribute creation. Use when creating many instances of a small class. Downside: less flexible, can't add new attributes dynamically, inheritance with slots requires care.
43. Explain Python metaclasses and use cases.
Metaclass controls class creation. type is the default metaclass. Custom metaclass: class Meta(type): def __new__(mcs, name, bases, attrs): modify attrs, return super().__new__(...). Use cases: auto-register subclasses (plugin systems), validate class definition, add methods automatically (ORMs), enforce coding standards. __init_subclass__ (Python 3.6+) is simpler for most use cases.
44. How does Python handle threading and concurrency?
Python threading: threads share memory, good for I/O-bound tasks (network, file I/O). GIL prevents true CPU parallelism. threading.Thread, Lock, RLock, Semaphore, Event, Condition. For CPU-bound tasks use multiprocessing (separate processes, no GIL). concurrent.futures.ThreadPoolExecutor for thread pool. AsyncIO for high-concurrency I/O without threads (event loop, single thread, cooperative multitasking).
45. How do you implement asynchronous programming in Python?
asyncio enables async programming with a single thread and event loop. async def defines a coroutine. await suspends the coroutine while waiting for I/O (doesn't block the thread - other coroutines run). asyncio.run() starts the event loop. asyncio.gather() runs coroutines concurrently. Use for: many concurrent I/O operations (HTTP, DB, websockets) with far fewer threads than traditional threading.
46. How do Python coroutines work?
Coroutines are functions defined with async def that can suspend at await points. When a coroutine awaits something (asyncio.sleep, aiohttp request), control returns to the event loop which runs other coroutines. Unlike threads, coroutines switch cooperatively (at await) not preemptively. This enables thousands of concurrent operations with minimal memory compared to threads.
47. How do you implement concurrent futures for parallel tasks?
concurrent.futures provides a high-level interface for parallel tasks. ThreadPoolExecutor for I/O-bound parallel tasks. ProcessPoolExecutor for CPU-bound parallel tasks (bypasses GIL with separate processes). Submit tasks with executor.submit(fn, args) returns Future. executor.map(fn, iterable) for parallel map. Use with context manager: with ThreadPoolExecutor(max_workers=4) as executor:.
48. How does Python handle sockets and networking?
Python socket programming: socket.socket(AF_INET, SOCK_STREAM) for TCP. bind(), listen(), accept() for server. connect() for client. send() and recv() for data transfer. Use selectors module for non-blocking I/O. asyncio has async socket support. High-level: use requests (HTTP), websockets library, or Twisted for network protocols. Raw sockets rarely needed - use appropriate high-level library.
49. How do you make HTTP requests in Python?
HTTP requests in Python: requests library (simplest, synchronous). response = requests.get(url, headers={}, params={}, timeout=5). POST: requests.post(url, json=data). Session for connection pooling. For async HTTP: aiohttp or httpx. Always set timeouts. Handle errors with response.raise_for_status(). Use sessions for multiple requests to the same host (connection reuse).
50. What are Python design patterns?
Python design patterns: Singleton (one instance - use module-level variable or metaclass), Factory (create objects without specifying exact class), Observer (publish-subscribe with callbacks), Strategy (swap algorithms - pass functions), Decorator (functools.wraps), Command (encapsulate actions as objects). Python's duck typing and first-class functions make many patterns simpler than in Java/C++.