Beginner Python Interview Questions and Answers

๐Ÿ“‹ Table of Contents โ–พ
  1. Questions & Answers
  2. 📝 Knowledge Check

🐍 Beginner Python Interview Questions

This lesson covers the fundamental Python concepts every developer must know. Master data types, mutability, functions, comprehensions, OOP basics, modules, exceptions, and the standard library. These questions reflect what interviewers ask at junior and entry-level Python roles.

Questions & Answers

01 What is Python and what are its key characteristics?

Core Python is a high-level, interpreted, dynamically-typed, general-purpose programming language created by Guido van Rossum in 1991. It emphasises readability, has a vast standard library (“batteries included”), and supports multiple programming paradigms.

Key characteristics:

  • Interpreted โ€” executed line by line by CPython; no separate compile step needed
  • Dynamically typed โ€” variable types are determined at runtime, not declared
  • Strongly typed โ€” types do not coerce implicitly; "5" + 5 raises TypeError
  • Multi-paradigm โ€” procedural, object-oriented, and functional styles
  • Indentation-based โ€” code blocks defined by whitespace, not braces
  • Garbage collected โ€” automatic memory via reference counting + cyclic GC
  • Cross-platform โ€” runs on Windows, macOS, Linux unchanged
x = 42             # no type declaration
x = "now a string" # type changes at runtime (dynamic typing)
"5" + 5            # TypeError: can only concatenate str (not int)

02 What are Python’s built-in data types?

Data Types

Numeric: int (arbitrary precision), float, complex, bool (subclass of int)

Sequences: str (immutable), list (mutable), tuple (immutable), range (lazy)

Mapping: dict โ€” insertion-ordered key-value pairs since Python 3.7

Sets: set (mutable, unique), frozenset (immutable)

None: NoneType โ€” the singleton null value

n = 42; f = 3.14; b = True
s = "hello"; t = (1, 2); lst = [1, 2]
d = {"a": 1}; st = {1, 2, 3}

type(42)               # <class 'int'>
isinstance(True, int)  # True -- bool IS-A int

03 What is the difference between mutable and immutable objects?

Core Immutable objects cannot be changed after creation: int, float, str, tuple, frozenset. Mutable objects can be modified in place: list, dict, set.

lst = [1, 2, 3]
original_id = id(lst)
lst.append(4)
id(lst) == original_id   # True -- same object (mutated in place)

# Classic gotcha: mutable default argument
def append_item(item, lst=[]):   # BAD: default [] shared across ALL calls
    lst.append(item)
    return lst

append_item(1)  # [1]
append_item(2)  # [1, 2] -- NOT [2]!

# Fix: use None as sentinel
def append_item(item, lst=None):
    if lst is None: lst = []
    lst.append(item)
    return lst

04 What are list comprehensions? How do they compare to loops?

Syntax List comprehensions provide a concise, Pythonic way to create lists. They are generally faster than equivalent for loops.

# [expression  for item in iterable  if condition]
squares = [x**2 for x in range(10)]
evens   = [x for x in range(20) if x % 2 == 0]

# Dict and set comprehensions
word_len  = {w: len(w) for w in ["hello", "world"]}
unique_ln = {len(w) for w in ["hello", "world", "hi"]}

# Equivalent loop (slower, more verbose)
squares = []
for x in range(10): squares.append(x**2)

# Generator expression -- lazy, O(1) memory regardless of size
total = sum(x**2 for x in range(1_000_000))

05 What is the difference between *args and **kwargs?

Functions *args collects extra positional arguments into a tuple. **kwargs collects extra keyword arguments into a dict.

def demo(*args, **kwargs):
    print(args)    # tuple:  (1, 2, 3)
    print(kwargs)  # dict:   {'name': 'Alice', 'age': 30}

demo(1, 2, 3, name="Alice", age=30)

# Order: positional, *args, keyword-only, **kwargs
def full(a, b, *args, sep=", ", **kwargs): ...

# Unpacking with * and **
def add(a, b, c): return a + b + c
nums = [1, 2, 3]
add(*nums)               # add(1, 2, 3)
add(**{"a":1,"b":2,"c":3})

06 What are Python decorators? How do you write one?

Functions A decorator wraps a function with extra behaviour. It is syntactic sugar for func = decorator(func). Common uses: logging, timing, authentication, caching.

import functools, time

def timer(func):
    @functools.wraps(func)   # preserve __name__, __doc__
    def wrapper(*args, **kwargs):
        t0     = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"{func.__name__!r} took {time.perf_counter()-t0:.4f}s")
        return result
    return wrapper

@timer
def greet(name): return f"Hello, {name}!"

greet("Alice")   # 'greet' took 0.0000s

# Decorator with arguments (factory pattern)
def retry(max_attempts=3):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(max_attempts):
                try:    return func(*args, **kwargs)
                except: 
                    if i == max_attempts-1: raise
        return wrapper
    return decorator

07 What is the difference between is and == in Python?

Core == tests value equality (calls __eq__). is tests identity โ€” whether both names point to the exact same object in memory.

a = [1, 2, 3]; b = [1, 2, 3]; c = a
a == b   # True  -- equal values
a is b   # False -- different objects
a is c   # True  -- c is an alias for a

# Always use 'is' for None comparisons
x = None
x is None    # correct (only one None object)
x == None    # works but poor style

# CPython caches small integers (-5..256) -- never rely on this
x = 256; y = 256; x is y  # True  (cached)
x = 257; y = 257; x is y  # False (not cached)

08 What are generators and the yield keyword?

Iterators A generator produces values lazily one at a time using yield. It pauses execution and resumes from where it left off. Uses constant memory regardless of sequence size.

def countdown(n):
    while n > 0:
        yield n    # pause, send n, resume on next()
        n -= 1

for v in countdown(5): print(v)  # 5 4 3 2 1

# Generator expression -- lazy, memory-efficient
gen = (x**2 for x in range(1_000_000))
next(gen)   # 0
next(gen)   # 1

# yield from -- delegate to a sub-generator
def chain(*iterables):
    for it in iterables: yield from it

list(chain([1,2], [3,4]))  # [1, 2, 3, 4]

09 What is the difference between list, tuple, set, and dict?

Data Types

  • list โ€” ordered, mutable, duplicates allowed: [1,2,3]
  • tuple โ€” ordered, immutable, duplicates allowed: (1,2,3) โ€” usable as dict key
  • set โ€” unordered, mutable, unique values: {1,2,3} โ€” O(1) membership test
  • dict โ€” ordered (3.7+), mutable, key-value pairs: {"a":1} โ€” O(1) lookup
from collections import Counter, defaultdict

c = Counter("abracadabra")   # {'a':5,'b':2,'r':2,'c':1,'d':1}
d = defaultdict(list)        # auto-creates [] for missing keys

# Performance comparison
big_list = list(range(1_000_000))
big_set  = set(range(1_000_000))
999_999 in big_list   # O(n) -- slow
999_999 in big_set    # O(1) -- fast

10 What is a lambda function? When should you use one?

Functions A lambda is an anonymous one-expression function. Best used as a short key or callback; prefer def for anything more complex.

square = lambda x: x**2
add    = lambda x, y: x + y

# Sort by key
students = [{"name":"Bob","grade":85},{"name":"Alice","grade":92}]
students.sort(key=lambda s: s["grade"])

words = ["banana","apple","cherry"]
sorted(words, key=lambda w: len(w))    # sort by length

# map / filter
squares = list(map(lambda x: x**2, range(5)))
evens   = list(filter(lambda x: x%2==0, range(10)))

11 What is the GIL (Global Interpreter Lock) in CPython?

Concurrency The GIL is a mutex allowing only one thread to execute Python bytecode at a time. It simplifies memory management but limits true CPU parallelism.

  • I/O-bound tasks โ€” GIL released during I/O; threads run effectively concurrently. Use threading.
  • CPU-bound tasks โ€” only one thread runs at a time. Use multiprocessing or C extensions (NumPy releases the GIL).
import concurrent.futures

# I/O-bound -- threading is effective
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as ex:
    results = list(ex.map(download_url, urls))

# CPU-bound -- use separate processes
with concurrent.futures.ProcessPoolExecutor() as ex:
    results = list(ex.map(heavy_compute, data_chunks))

# Python 3.13 -- experimental free-threaded build (no GIL)
# python3.13t  -- disables GIL, enables true thread parallelism

12 What are Python’s dunder (magic) methods?

OOP Dunder methods allow custom objects to integrate with Python built-ins and operators. Python calls them implicitly in certain contexts.

class Vector:
    def __init__(self, x, y):  self.x, self.y = x, y
    def __repr__(self):        return f"Vector({self.x}, {self.y})"
    def __str__(self):         return f"({self.x}, {self.y})"
    def __add__(self, other):  return Vector(self.x+other.x, self.y+other.y)
    def __mul__(self, s):      return Vector(self.x*s, self.y*s)
    def __eq__(self, other):   return self.x==other.x and self.y==other.y
    def __hash__(self):        return hash((self.x, self.y))
    def __len__(self):         return 2
    def __iter__(self):        yield self.x; yield self.y
    def __getitem__(self, i):  return (self.x, self.y)[i]
    def __bool__(self):        return self.x!=0 or self.y!=0
    def __enter__(self):       return self
    def __exit__(self, *a):    pass

13 What is inheritance and polymorphism in Python?

OOP

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

class Dog(Animal):
    def speak(self): return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self): return f"{self.name} says Meow!"

# Polymorphism -- same interface, different behaviour
for animal in [Dog("Rex"), Cat("Whiskers")]:
    print(animal.speak())

# super() -- call parent method
class Retriever(Dog):
    def speak(self): return super().speak() + " (tail wagging)"

isinstance(Dog("Rex"), Animal)  # True
issubclass(Dog, Animal)         # True

14 What is the difference between a module and a package?

Modules A module is a single .py file. A package is a directory containing an __init__.py that groups related modules.

import os                            # module
from os import path                  # name from module
from os.path import join, exists     # specific names
import numpy as np                   # alias

# Relative imports (inside a package)
from . import utils                  # same package
from .models import User             # sub-module
from ..utils import helper           # parent package

# __all__ -- controls what `import *` exports
__all__ = ["public_func"]

# Only run when executed directly (not when imported)
if __name__ == "__main__":
    main()

15 What is exception handling in Python?

Errors

try:
    result = int("abc")             # raises ValueError
except ValueError as e:
    print(f"Value error: {e}")
except (FileNotFoundError, OSError):
    pass
except Exception:
    raise                           # re-raise unknown errors
else:
    print("No exception raised")    # runs only if try succeeded
finally:
    print("Always runs")            # cleanup code

# Custom exception
class InsufficientFundsError(ValueError):
    def __init__(self, balance, amount):
        super().__init__(f"Need {amount}, have {balance}")

# Exception chaining
try: connect_db()
except ConnectionError as e:
    raise RuntimeError("DB unavailable") from e

# Suppress specific exceptions silently
from contextlib import suppress
with suppress(FileNotFoundError):
    os.remove("temp.txt")

16 What are context managers and the with statement?

Core Context managers ensure resources are acquired and released cleanly โ€” even if an exception occurs. They call __enter__ on entry and __exit__ on exit.

with open("file.txt") as f:
    data = f.read()
# file closed here even on exception

# @contextmanager -- simplest custom context manager
from contextlib import contextmanager

@contextmanager
def db_transaction(conn):
    try:
        yield conn       # code in `with` block runs here
        conn.commit()
    except Exception:
        conn.rollback()
        raise

with db_transaction(connection) as conn:
    conn.execute("INSERT ...")

17 What are Python string formatting methods?

Strings

name = "Alice"; score = 98.765; count = 1_000_000

# f-strings (Python 3.6+) -- preferred, fastest
f"Hello, {name}!"              # Hello, Alice!
f"Score: {score:.2f}"          # Score: 98.77
f"Count: {count:,}"            # Count: 1,000,000
f"{name!r}"                    # 'Alice' (repr)
f"{2 ** 10 = }"                # 2 ** 10 = 1024 (debug, 3.8+)

# str.format() -- older but common
"Hello, {name}!".format(name=name)

# % formatting -- legacy, still seen in logging
"Hello, %s! Score: %.1f" % (name, score)

# Raw strings -- no backslash interpretation
path = r"C:\Users\Alice\Documents"

18 What is the difference between deepcopy and shallow copy?

Core Shallow copy creates a new container but shares references to nested objects. Deep copy recursively copies everything โ€” fully independent.

import copy
original = [[1,2,3],[4,5,6]]

shallow = copy.copy(original)    # or: original[:]
shallow[0].append(99)
print(original[0])  # [1,2,3,99] -- inner list shared!

deep = copy.deepcopy(original)
deep[0].append(99)
print(original[0])  # [1,2,3] -- completely independent

19 What are Python’s built-in higher-order functions?

Functions

from functools import reduce, partial

nums = [1,2,3,4,5,6,7,8,9,10]

list(map(lambda x: x**2, nums))       # [1,4,9,16,25,...]
list(filter(lambda x: x%2==0, nums))  # [2,4,6,8,10]
reduce(lambda a,x: a+x, nums)         # 55

# sorted with key
sorted(["banana","apple"], key=len)   # ['apple','banana']

# zip and enumerate
for i, name in enumerate(["Alice","Bob"], start=1):
    print(f"{i}. {name}")

# partial -- freeze some arguments
double = partial(lambda f,x: x*f, 2)
double(5)  # 10

20 What is the difference between @staticmethod, @classmethod, and instance methods?

OOP

class Temperature:
    _count = 0

    def __init__(self, c):
        self.celsius = c
        Temperature._count += 1

    # Instance method -- receives self (the instance)
    def to_fahrenheit(self): return self.celsius * 9/5 + 32

    # Class method -- receives cls (the class); common as alternative constructor
    @classmethod
    def from_fahrenheit(cls, f): return cls((f-32)*5/9)

    @classmethod
    def get_count(cls): return cls._count

    # Static method -- no self or cls; just a namespaced function
    @staticmethod
    def is_valid(value): return value >= -273.15

t = Temperature.from_fahrenheit(212)   # 100.0
Temperature.is_valid(-300)             # False

21 What is pip and how do virtual environments work?

Tooling

python -m venv .venv               # create virtual environment
source .venv/bin/activate          # activate (Linux/Mac)
.venv\Scripts\activate             # activate (Windows)
deactivate                         # deactivate

pip install requests               # install latest
pip install requests==2.31.0       # specific version
pip install -r requirements.txt    # from file
pip freeze > requirements.txt      # snapshot

# Why virtual environments?
# Each project gets its own isolated dependencies
# Prevents version conflicts between projects
# System Python remains clean

# Modern alternatives:
# uv     -- ultra-fast Rust-based package manager
# poetry -- dependency management + packaging

22 What is PEP 8 and why does it matter?

Style PEP 8 is Python’s official style guide. Consistent style makes code easier to read and maintain across teams.

  • Indentation โ€” 4 spaces (never tabs)
  • Naming โ€” snake_case for variables/functions, PascalCase for classes, UPPER_CASE for constants
  • Imports โ€” stdlib, then third-party, then local; one module per line
  • Line length โ€” 79 chars (88 for Black formatter)
# Enforcement tools:
# ruff   -- ultra-fast linter + formatter (recommended, replaces flake8+isort+pyupgrade)
# black  -- opinionated auto-formatter
# mypy   -- static type checker

# Type hints (encouraged for public APIs)
def greet(name: str, times: int = 1) -> str:
    return (name + " ") * times

📝 Knowledge Check

Test your understanding of Python fundamentals.

🧠 Quiz Question 1 of 5

Why is using a mutable object (like a list) as a default argument dangerous in Python?





🧠 Quiz Question 2 of 5

What is the key advantage of a generator expression over a list comprehension for large sequences?





🧠 Quiz Question 3 of 5

What does the GIL mean for Python threading with CPU-bound vs I/O-bound tasks?





🧠 Quiz Question 4 of 5

What is the difference between a shallow copy and a deep copy of a nested list?





🧠 Quiz Question 5 of 5

When should you use @classmethod instead of a regular instance method?