Chapter 7 — Python Fundamentals (60 Questions)

Topics (60 questions):

  1. 1. What is Python
  2. 2. What are the main features of Python
  3. 3. What is the difference between Python 2 and Python 3
  4. 4. What is PEP 8 and why does it matter
  5. 5. What is indentation in Python
  6. 6. What is the difference between a comment and a docstring
  7. 7. What does if __name__ == "__main__" mean in Python
  8. 8. What is dynamic typing in Python
  9. 9. What is duck typing in Python
  10. 10. What is the LEGB rule in Python
  11. 11. What is the difference between global and local variables in Python
  12. 12. What are Python’s built-in data types
  13. 13. What is the difference between a list, tuple, set, and dictionary in Python
  14. 14. What is the difference between mutable and immutable types in Python
  15. 15. Why are strings immutable in Python
  16. 16. How does list slicing work in Python
  17. 17. What is the difference between append() and extend()
  18. 18. What is the difference between remove(), pop(), and del
  19. 19. What is the difference between sort() and sorted()
  20. 20. How do you reverse a list or a string in Python
  21. 21. Can you use a list as a dictionary key in Python
  22. 22. What is a list comprehension in Python
  23. 23. What are collections.Counter and defaultdict used for
  24. 24. How do you merge two dictionaries in Python
  25. 25. What is *args and **kwargs in Python
  26. 26. What is a lambda function in Python
  27. 27. Why is using a mutable default argument a bug
  28. 28. What is the difference between pass, break, and continue
  29. 29. What are map(), filter(), and reduce() in Python
  30. 30. What is enumerate() used for in Python
  31. 31. What is zip() used for in Python
  32. 32. How do you swap two variables in Python
  33. 33. How do f-strings work in Python
  34. 34. What is a decorator in Python
  35. 35. What is unpacking in Python
  36. 36. What is the ternary operator in Python
  37. 37. What is a class and an object in Python
  38. 38. What is self in Python
  39. 39. What is the difference between a class variable and an instance variable
  40. 40. What is inheritance in Python
  41. 41. What is polymorphism in Python
  42. 42. What is encapsulation in Python
  43. 43. What is the difference between isinstance() and type()
  44. 44. What is super() used for in Python
  45. 45. What is an abstract base class in Python
  46. 46. What is multiple inheritance in Python
  47. 47. How does try/except/else/finally work in Python
  48. 48. What is the difference between raise and assert
  49. 49. How do you read and write a file in Python
  50. 50. Why should you use with when opening files
  51. 51. What is the difference between import and from import
  52. 52. How do you work with JSON in Python
  53. 53. What is pip and how do you install packages
  54. 54. Why do you use a virtual environment in Python
  55. 55. How do you write a simple unit test in Python
  56. 56. What is the difference between Django and Flask
  57. 57. How do you make an HTTP request in Python
  58. 58. What is the difference between a NumPy array and a Python list
  59. 59. What are Python’s advantages and disadvantages
  60. 60. How do you debug a Python program

1. What is Python?

Interview Answer

Python is a high-level, interpreted, dynamically typed language designed for readability. It uses indentation instead of braces, has a large standard library, and is used for scripting, web backends, data work, automation, and AI.

Interviewers usually want a one-sentence definition plus where you have used it (APIs, scripts, data pipelines), not a history of Guido van Rossum.


Important Point

Python is the language. CPython is the most common implementation (the one from python.org). Saying “Python is compiled to bytecode then interpreted” is accurate and shows you are not reciting a slogan.


Follow-up Question

Q: Is Python compiled or interpreted?

Answer

Both, in practice. Source is compiled to bytecode (.pyc), then a virtual machine executes that bytecode. You still run it like an interpreted language: no separate compile step in everyday use.


2. What are the main features of Python?

Interview Answer

A balanced answer also names trade-offs: slower CPU-bound loops than C#/Go, and CPython’s GIL limiting CPU threads. Do not pretend Python is the fastest language.


Important Point

Features that win interviews are the ones you can connect to work: “We used it for glue services and data jobs because the library support was better than rewriting in C#.”


Follow-up Question

Q: Why do companies still use Python if it is slower?

Answer

Developer speed, libraries, and I/O-bound services. Many production systems spend time waiting on the network or database, not multiplying integers in a tight loop.


3. What is the difference between Python 2 and Python 3?

Interview Answer

Python 2 reached end of life in 2020. New code should be Python 3. Common differences interviewers still ask:


Important Point

If a job description still says Python 2, treat it as a migration/legacy signal. Do not start a new service on 2.


Follow-up Question

Q: What Python 3 version should you target?

Answer

Whatever the team already runs in production, usually 3.11 or 3.12 today. Mention you check python --version and the Docker base image rather than assuming laptop Python.


4. What is PEP 8 and why does it matter?

Interview Answer

PEP 8 is the official style guide: 4-space indentation, naming (snake_case functions, CapWords classes, UPPER_CASE constants), import order, and line length.

Teams enforce it with ruff, flake8, or black. Interviews use it as a proxy for “will this person write code others can review?”


Important Point

PEP 8 is a default, not a religion. Match the repository’s formatter. Inconsistent style in a PR is a worse signal than a 99-character line.


Follow-up Question

Q: Is black the same as PEP 8?

Answer

Black is an autoformatter that is PEP 8-inspired but opinionated (for example its own wrapping rules). Many teams run Black or Ruff format so humans stop arguing about commas.


5. What is indentation in Python?

Interview Answer

Indentation is syntax, not decoration. Blocks are defined by consistent leading whitespace. Mixing tabs and spaces causes TabError. The convention is 4 spaces.

if ready:
    start()
else:
    wait()

Important Point

This is the first thing that trips people coming from C#. There are no braces to save a misaligned else.


Follow-up Question

Q: Can you put a block on one line?

Answer

Yes: if x: y(). It is fine for a single short statement. Nested one-liners become unreadable; prefer a real block.


6. What is the difference between a comment and a docstring?

Interview Answer

Comments (#) are for the next reader of the code. They are ignored at runtime.

Docstrings are string literals as the first statement in a module, class, or function. They become __doc__ and are used by help(), IDEs, and tools like Sphinx.

def area(width, height):
    """Return width * height."""
    return width * height

Important Point

Write docstrings for public APIs. Do not restate the function name. Prefer explaining arguments, return values, and exceptions that callers must know.


Follow-up Question

Q: Should every function have a docstring?

Answer

Public library functions yes. Tiny private helpers with an obvious name often do not. Noise docstrings are as bad as no docs.


7. What does if __name__ == "__main__" mean in Python?

Interview Answer

Every module has a __name__. When you run the file directly, it is "__main__". When you import it, it is the module’s name.

def main():
    print('job started')

if __name__ == '__main__':
    main()

This lets a file be both a library and a script. Tests and other modules can import functions without starting the CLI side effects.


Important Point

Put runnable work under this guard. Top-level code in a module runs on import, which surprises people and slows test collection.


Follow-up Question

Q: Why not just put the script code at the bottom with no guard?

Answer

The first time someone imports a helper from that file, the script body runs. That has caused production jobs to fire from a unit test import.


8. What is dynamic typing in Python?

Interview Answer

Types are attached to objects, not to variable names. The same name can hold an int, then a string:

x = 1
x = 'ok'  # legal

This is different from C#, where a variable’s type is declared (or inferred) and then mostly fixed. Python checks types when operations run, which is why 'a' + 1 fails at runtime, not at compile time (unless a type checker is in CI).


Important Point

Modern Python still uses type hints. They help readers and tools; they are not required by the interpreter unless you add a runtime validator.


Follow-up Question

Q: Is Python strongly typed or weakly typed?

Answer

Strongly and dynamically typed. It will not silently turn "1" + 2 into "12" the way some weakly typed languages do. You must convert explicitly.


9. What is duck typing in Python?

Interview Answer

“If it walks like a duck and quacks like a duck, it is a duck.” Code cares about behaviour (methods/protocol), not the exact class.

def save(stream):
    stream.write('data')  # file, BytesIO, socket-like object

You do not need a shared base class. This is why so many APIs accept “file-like objects.”


Important Point

Duck typing is powerful and easy to misuse. In larger codebases, document the expected methods or use a Protocol / ABC so callers know the contract. Deep typing and Protocols belong in Advanced Python; here the interview answer is the duck sentence plus an example.


Follow-up Question

Q: How is duck typing different from interfaces in C#?

Answer

C# usually requires an explicit interface. Python often does not. You can still use ABCs if you want a named contract and isinstance checks.


10. What is the LEGB rule in Python?

Interview Answer

Name lookup walks four scopes, inner to outer:

  1. Local — the current function
  2. Enclosing — outer functions (nested defs)
  3. Global — the module
  4. Built-in — names like len, range

Assignment in a function makes a name local unless you declare global or nonlocal.


Important Point

Shadowing builtins (list = []) is a common bug. LEGB is why that breaks later calls to list().


Follow-up Question

Q: When do you need the global keyword?

Answer

Only when a function must assign to a module-level name. Reading a global does not need global. Prefer passing arguments and returning values over mutating globals.


11. What is the difference between global and local variables in Python?

Interview Answer

Locals live in a function and disappear when the function returns. Globals live on the module and last for the life of that module.

count = 0  # global

def bump():
    count = 1  # local, does not change the module count

def bump_global():
    global count
    count += 1

Important Point

Avoid globals for business state. They make tests and concurrency harder. Configuration constants at module level are fine.


Follow-up Question

Q: What about nonlocal?

Answer

nonlocal assigns to a variable in an enclosing function, not the module. It is for nested functions. Closures in more depth are covered in Advanced Python.


12. What are Python’s built-in data types?

Interview Answer

A practical interview list:

type(obj) and isinstance(obj, cls) are how you inspect them. Prefer isinstance when subclasses should count.


Important Point

There is no separate ‘character’ type. A one-character string is still a str.


Follow-up Question

Q: Is bool really an int?

Answer

Yes: True == 1 and True + True == 2. Do not use that trick in real code; it confuses readers.


13. What is the difference between a list, tuple, set, and dictionary in Python?

Interview Answer

Pick the type that matches the need: sequence vs unique bag vs key/value. Using a list to search for membership in a large collection is a common beginner mistake; use a set or dict.


Important Point

Tuples are not “constant lists.” They are a different type with different methods and hashing rules. Nested mutables inside a tuple can still change.


Follow-up Question

Q: When would you use a tuple instead of a list?

Answer

Fixed-size records, unpacking (x, y = point), and as dict keys. Also as a signal: “this sequence is not meant to grow.”


14. What is the difference between mutable and immutable types in Python?

Interview Answer

Immutable objects cannot change in place: int, float, str, tuple, frozenset, bytes. Operations return new objects.

Mutable objects can change in place: list, dict, set, bytearray, most custom classes.

s = 'ab'
s += 'c'   # new string
nums = [1]
nums.append(2)  # same list object

Important Point

Mutability explains default-argument bugs, surprising function side effects, and why lists cannot be dict keys. Always ask: “does this call change my object or return a new one?”


Follow-up Question

Q: Does x += 1 mutate an int?

Answer

No. It rebinds the name x to a new int. The old int is unchanged (and may be interned).


15. Why are strings immutable in Python?

Interview Answer

Immutability lets strings be hashable (usable as dict keys), shared safely, and interned in some cases. Concatenating in a loop with + builds many temporary strings; use ''.join(parts) instead.

name = 'Ann'
# name[0] = 'B'  # TypeError

Important Point

You “change” a string by creating another one. Methods like replace and upper return new strings.


Follow-up Question

Q: How do you build a large string efficiently?

Answer

Collect pieces in a list, then join. Or use an io.StringIO for a file-like builder. Avoid s = s + chunk in a tight loop.


16. How does list slicing work in Python?

Interview Answer

Slice syntax is seq[start:stop:step]. stop is exclusive. Omitted values use defaults: start 0, stop end, step 1.

a = [0, 1, 2, 3, 4]
a[1:4]     # [1, 2, 3]
a[:3]      # [0, 1, 2]
a[::2]     # [0, 2, 4]
a[::-1]    # [4, 3, 2, 1, 0]

Slicing a list returns a new list (shallow copy of that range). Negative indices count from the end (-1 is last).


Important Point

Assignment to a slice can change length: a[1:3] = [9, 9, 9]. That is a mutable-sequence feature, not available on strings or tuples.


Follow-up Question

Q: Does a[:] copy nested lists?

Answer

No. It is a shallow copy: the outer list is new, inner objects are shared. Nested copies are a different topic (deepcopy) in Advanced Python.


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

Interview Answer

append(x) adds one element (even if x is a list). extend(iterable) adds each item from the iterable.

a = [1]
a.append([2, 3])  # [1, [2, 3]]
b = [1]
b.extend([2, 3])  # [1, 2, 3]

+= on a list is like extend. a + b builds a new list and does not change a.


Important Point

This is one of the most common junior mix-ups. If the interviewer draws a nested list, they are testing append vs extend.


Follow-up Question

Q: What about insert()?

Answer

insert(i, x) puts x at index i and shifts the rest. It is O(n). Prefer append unless you truly need the middle.


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

Interview Answer


Important Point

Use pop when you need the value. Use del/remove when you only need it gone. Catching ValueError from remove is often clumsier than testing membership first.


Follow-up Question

Q: How do you delete while iterating a list?

Answer

Do not delete from the list you are iterating. Build a new list, or iterate a copy: for x in a[:]:. Deleting in-place shifts indices and skips items.


19. What is the difference between sort() and sorted()?

Interview Answer

list.sort() sorts in place and returns None. sorted(iterable) returns a new list and works on any iterable.

nums = [3, 1, 2]
sorted(nums)   # [1, 2, 3]; nums unchanged
nums.sort()    # nums is [1, 2, 3]

Both accept key= and reverse=True. Timsort is stable.


Important Point

A classic bug is return nums.sort(), which returns None. Interviewers love that trap.


Follow-up Question

Q: How do you sort by a field?

Answer

Use key: sorted(users, key=lambda u: u['name']) or operator.itemgetter('name'). For multiple fields, return a tuple from the key function.


20. How do you reverse a list or a string in Python?

Interview Answer

Strings have no reverse() method because they are immutable. Use slicing or ''.join(reversed(s)).


Important Point

For interviews, [::-1] is the expected one-liner. Mention reversed if they ask about memory on large sequences.


Follow-up Question

Q: Is [::-1] the fastest reverse?

Answer

It is simple and fast enough. For huge lists that you can mutate, reverse() avoids allocating a second list.


21. Can you use a list as a dictionary key in Python?

Interview Answer

No. Dict keys must be hashable. Lists are mutable, so they are unhashable. Tuples of hashable items can be keys.

d = {}
# d[[1, 2]] = 'no'   # TypeError: unhashable type: 'list'
d[(1, 2)] = 'yes'

Important Point

If you mutate an object after using it as a key, the hash table breaks. That is why mutability and hashing are tied together.


Follow-up Question

Q: Can a tuple containing a list be a key?

Answer

No. The tuple’s hash depends on its items. A nested list makes the whole tuple unhashable.


22. What is a list comprehension in Python?

Interview Answer

A compact way to build a list from an iterable, with an optional filter:

squares = [n * n for n in range(10) if n % 2 == 0]

There are also set and dict comprehensions: {x for x in items}, {k: v for k, v in pairs}.

Keep them readable. Nested comprehensions that look like a puzzle should be loops. Generator expressions (lazy) are a separate Advanced Python topic.


Important Point

Comprehensions are eager: the whole list exists when the line finishes. That is usually what you want for small results.


Follow-up Question

Q: Can a comprehension have more than one for?

Answer

Yes: [x + y for x in a for y in b]. Order matches nested loops. Prefer named loops if it is not obvious.


23. What are collections.Counter and defaultdict used for?

Interview Answer

Counter counts hashable items. defaultdict supplies a default factory for missing keys so you do not write if key not in d.

from collections import Counter, defaultdict

Counter('abracadabra')
# Counter({'a': 5, 'b': 2, 'r': 2, ...})

groups = defaultdict(list)
for user in users:
    groups[user.role].append(user)

These two types show up constantly in coding interviews (anagrams, grouping, histograms).


Important Point

A plain dict with d.get(k, 0) + 1 also works. defaultdict is cleaner when the value is a list or set you keep updating.


Follow-up Question

Q: What is the default for a missing Counter key?

Answer

Accessing a missing key returns 0, not KeyError. That is convenient and can hide typos if you are not careful.


24. How do you merge two dictionaries in Python?

Interview Answer

Modern Python (3.9+):

merged = a | b          # new dict; b wins on key clashes
a |= b                 # update a in place

Older style: {**a, **b} or c = a.copy(); c.update(b).

Later keys win. Nested dicts are not merged recursively unless you write that yourself.


Important Point

Do not use dict(a.items() + b.items()) — that is Python 2 thinking and fails in 3.


Follow-up Question

Q: How do you merge more than two?

Answer

a | b | c, or start with {} and update in a loop. For many dicts, a loop is clearer.


25. What is *args and **kwargs in Python?

Interview Answer

*args collects extra positional arguments into a tuple. **kwargs collects extra named arguments into a dict. The names args and kwargs are convention.

def log(level, *messages, **fields):
    print(level, messages, fields)

log('info', 'up', 'ok', user='ann')

When calling, *list unpacks positionals and **dict unpacks keywords.


Important Point

Use them for wrappers, decorators, and forward-compatible APIs. Do not use them to avoid designing a real function signature.


Follow-up Question

Q: What is the difference between *args and a list parameter?

Answer

A list parameter is one argument that happens to be a list. *args is zero or more arguments. Callers write f(1, 2, 3) vs f([1, 2, 3]).


26. What is a lambda function in Python?

Interview Answer

A lambda is a small anonymous function with a single expression:

pairs.sort(key=lambda p: p[1])

It cannot contain statements (if blocks, for, assignments except the walrus operator). For anything non-trivial, use def with a name.


Important Point

Lambdas are common as key= for sort/min/max and as short callbacks. Named functions are easier to test and debug.


Follow-up Question

Q: Can a lambda have multiple arguments?

Answer

Yes: lambda x, y: x + y. Default arguments work too. It is still one expression.


27. Why is using a mutable default argument a bug?

Interview Answer

Default values are evaluated once, when the function is defined, not on each call. A default list or dict is shared across calls.

def add_item(item, bucket=[]):
    bucket.append(item)
    return bucket

add_item(1)  # [1]
add_item(2)  # [1, 2]  surprise

The correct pattern:

def add_item(item, bucket=None):
    if bucket is None:
        bucket = []
    bucket.append(item)
    return bucket

Important Point

This is one of the most frequently asked Python questions. If you only remember one “gotcha,” remember this one.


Follow-up Question

Q: Does a default None have the same problem?

Answer

No. None is immutable (a singleton). The bug is sharing one mutable object, not the idea of defaults.


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

Interview Answer

Empty function bodies cannot be truly empty; use pass or ... (Ellipsis) as a stub.


Important Point

break only exits one loop level. For nested loops, people use a flag, a function return, or (rarely) a custom exception.


Follow-up Question

Q: Is pass the same as None?

Answer

No. pass is a statement. None is a value. A function with only pass returns None because all Python functions return None by default.


29. What are map(), filter(), and reduce() in Python?

Interview Answer

List comprehensions and generator expressions are usually more readable than map/filter. Know map/filter because interviewers still ask.

list(map(str.upper, ['a', 'b']))
[s.upper() for s in ['a', 'b']]  # preferred

Important Point

Remember to wrap with list() if you need a list. A map object is lazy.


Follow-up Question

Q: When is reduce appropriate?

Answer

Rarely. sum, any, all, and a simple loop are clearer. reduce is reasonable for a well-known fold that has no builtin.


30. What is enumerate() used for in Python?

Interview Answer

enumerate(iterable, start=0) yields (index, item) pairs so you do not maintain a manual counter.

for i, name in enumerate(names, start=1):
    print(i, name)

This is cleaner than for i in range(len(names)), which is a C-style habit and fails on iterators that have no length.


Important Point

If you only need items, do not use enumerate. If you need index and item, prefer it over range(len).


Follow-up Question

Q: Does enumerate copy the sequence?

Answer

No. It is a lazy iterator over the original iterable.


31. What is zip() used for in Python?

Interview Answer

zip(a, b) walks two (or more) iterables in parallel and stops at the shortest. In Python 3 it returns a lazy zip object.

for name, score in zip(names, scores):
    print(name, score)

To keep going to the longest, use itertools.zip_longest. To unzip: names, scores = zip(*pairs).


Important Point

A frequent bug is assuming zip pads the shorter list. It does not.


Follow-up Question

Q: How do you zip into a dict?

Answer

dict(zip(keys, values)). Keys must be unique or later values overwrite earlier ones.


32. How do you swap two variables in Python?

Interview Answer

Tuple unpacking, no temp variable:

a, b = b, a

The right-hand side is evaluated first (as a tuple), then unpacked. It also works for more names: a, b, c = c, a, b.


Important Point

This is a classic “Python is different from C” question. Do not overthink it.


Follow-up Question

Q: Does this copy lists?

Answer

It rebinds names. If a and b are lists, you swapped which name points where; you did not clone the lists.


33. How do f-strings work in Python?

Interview Answer

f-strings (3.6+) embed expressions inside a string prefixed with f:

name = 'Ann'
f'Hello {name.upper()}'
f'{value:.2f}'

Older styles still appear: '%s' % name and '{}'.format(name). Prefer f-strings for new code.

Expressions inside braces are real Python. Keep them small. Do not put side effects in an f-string.


Important Point

Debug helper in 3.8+: f'{name=}' prints the name and value.


Follow-up Question

Q: Are f-strings faster than format?

Answer

Usually a bit, and they are easier to read. That is not why you choose them; readability is.


34. What is a decorator in Python?

Interview Answer

A decorator is a function (or callable) that takes a function and returns a function. The @ syntax is applied at definition time.

import time

def timed(fn):
    def wrapper(*args, **kwargs):
        t0 = time.perf_counter()
        result = fn(*args, **kwargs)
        print(time.perf_counter() - t0)
        return result
    return wrapper

@timed
def work():
    ...

@timed is the same as work = timed(work). Use functools.wraps so the wrapper keeps the original name and docstring.


Important Point

This FAQ covers the simple wrapper. Decorators with arguments, class decorators, and how @property is implemented are Advanced Python.


Follow-up Question

Q: Does a decorator run every time you call the function?

Answer

The outer decorator runs once, at import/definition. The inner wrapper runs on every call.


35. What is unpacking in Python?

Interview Answer

Unpacking assigns parts of a sequence (or other iterable) to names:

first, *rest, last = [1, 2, 3, 4]
# first=1, rest=[2, 3], last=4

x, y = point

In calls, f(*args, **kwargs) unpacks into the function. In 3.5+ you can unpack in lists and dicts: [*a, *b], {**d1, **d2}.


Important Point

A mismatch in the number of items raises ValueError. That is better than silently ignoring extra values.


Follow-up Question

Q: What does * do on the left of an assignment?

Answer

It captures the leftover items into a list (starred assignment). There can be only one starred target in that assignment.


36. What is the ternary operator in Python?

Interview Answer

Python’s conditional expression is:

value = 'yes' if ok else 'no'

It is an expression, so it can sit on the right-hand side of an assignment or inside a comprehension. It is not cond ? a : b like C#/Java.

Do not nest several of them. A normal if/else is clearer.


Important Point

People coming from C# look for ?. The interview answer is the x if cond else y form.


Follow-up Question

Q: Can you use elif in a ternary?

Answer

Not as a keyword. You can nest: a if c1 else b if c2 else d. That gets ugly fast.


37. What is a class and an object in Python?

Interview Answer

A class is a blueprint. An object (instance) is a concrete value created from that class.

class User:
    def __init__(self, name):
        self.name = name

u = User('Ann')  # u is an instance

In Python everything is an object, including classes and functions. type(u) is User; type(User) is type.


Important Point

You do not need a class for every problem. A function or a dataclass is often enough. Use classes when you have state plus behaviour that belong together.


Follow-up Question

Q: What does __init__ do?

Answer

It initializes a new instance after it is created. It is not a constructor in the C# sense. Object creation vs initialization in more depth is Advanced Python (__new__).


38. What is self in Python?

Interview Answer

self is the instance the method was called on. The name is convention; the first parameter of an instance method is that instance.

class Counter:
    def __init__(self):
        self.n = 0
    def inc(self):
        self.n += 1

c = Counter()
c.inc()      # Counter.inc(c)

You must declare it in the method signature. Forgetting self is a daily beginner error: the first real argument gets eaten and you see confusing TypeErrors.


Important Point

Class methods use cls by convention. Static methods have no automatic first argument.


Follow-up Question

Q: Is self a keyword?

Answer

No. You could name it this, and reviewers would reject the PR. Always use self / cls.


39. What is the difference between a class variable and an instance variable?

Interview Answer

Instance variables are set on self (usually in __init__) and unique per object. Class variables are defined on the class body and shared by all instances unless an instance overrides the name.

class Box:
    kind = 'generic'      # class variable
    def __init__(self, n):
        self.n = n        # instance variable

A classic bug is a mutable class variable (items = [] on the class) shared by every instance.


Important Point

Reading self.kind can still see the class variable if the instance has no kind. Assigning self.kind = ... creates an instance attribute and shadows the class one.


Follow-up Question

Q: How do you change a class variable for everyone?

Answer

Assign on the class: Box.kind = 'other'. Assigning on an instance only affects that instance.


40. What is inheritance in Python?

Interview Answer

A subclass reuses and extends a base class:

class Animal:
    def speak(self):
        raise NotImplementedError

class Dog(Animal):
    def speak(self):
        return 'woof'

The subclass can override methods and call the parent with super(). Python supports multiple inheritance: class C(A, B).


Important Point

Prefer composition when you only need a helper object. Inheritance is for a real “is-a” relationship. Method resolution order (MRO) is the Advanced Python follow-up if they go deeper.


Follow-up Question

Q: Does Python have private inheritance like C++?

Answer

No. Inheritance is public in that sense. “Private” attributes are a naming convention (_name, name mangling with __name), not access control.


41. What is polymorphism in Python?

Interview Answer

The same operation works on different types. In Python this is usually duck typing plus method overrides.

def notify(channel):
    channel.send('hi')  # Email, Sms, FakeChannel

You do not need a shared interface type. If it has send, it can be notified. Subclass overrides (Dog.speak vs Cat.speak) are the OOP textbook form.


Important Point

Interviewers comparing to C# want to hear: Python is polymorphic without requiring an interface, and you can still use ABCs when you want a named contract.


Follow-up Question

Q: Is operator overloading polymorphism?

Answer

Yes, in a broad sense: + works on ints, strings, and lists because each type implements the operation differently.


42. What is encapsulation in Python?

Interview Answer

Encapsulation means bundling data with the methods that work on it and hiding internals. Python does this by convention, not compiler enforcement.

Properties (@property) let you expose a field-like API while controlling get/set. How properties work internally is Advanced Python.


Important Point

There are no true private members. “We are all consenting adults” is the culture. In a large codebase, still treat _ as a boundary.


Follow-up Question

Q: Should you use __dunder name mangling for secrets?

Answer

No. It will not hide data. Use real access control, encryption, and not storing secrets on the object in the first place.


43. What is the difference between isinstance() and type()?

Interview Answer

type(obj) is SomeClass is an exact match. isinstance(obj, SomeClass) is true for subclasses too, and can take a tuple of types.

isinstance(True, int)   # True, bool subclasses int
type(True) is int      # False

Prefer isinstance unless you truly need an exact type. Prefer duck typing over either when you only need a method to exist.


Important Point

Checking types everywhere is not idiomatic Python. Check at boundaries (user input, JSON) and trust internals.


Follow-up Question

Q: How do you check several types?

Answer

isinstance(x, (int, float)). In 3.10+ you may also see int | float in type hints; that is not the same as the runtime isinstance tuple unless you use extra tools.


44. What is super() used for in Python?

Interview Answer

super() lets you call a method on the next class in the method resolution order — usually the parent.

class Employee(Person):
    def __init__(self, name, role):
        super().__init__(name)
        self.role = role

Always call super().__init__ in subclasses that override __init__ if the parent sets required state.


Important Point

In single inheritance this feels like “call the parent.” With multiple inheritance it follows MRO, which is why Advanced Python covers that separately. For this FAQ, “call the parent implementation” is the expected answer.


Follow-up Question

Q: Can you write super(Employee, self).__init__()?

Answer

That is the Python 2 / explicit form. In Python 3 inside a method, zero-argument super() is preferred.


45. What is an abstract base class in Python?

Interview Answer

The abc module lets you define a class that cannot be instantiated until subclasses implement abstract methods.

from abc import ABC, abstractmethod

class Channel(ABC):
    @abstractmethod
    def send(self, message): ...

class Email(Channel):
    def send(self, message):
        ...

Trying Channel() raises TypeError. This is the closest everyday equivalent to a C# abstract class / interface hybrid.


Important Point

Use ABCs when you own the hierarchy and want a named contract. Structural typing with Protocol is the Advanced Python alternative for third-party types.


Follow-up Question

Q: Do you have to inherit from ABC?

Answer

For the usual pattern, yes (or use the ABCMeta metaclass). Registering virtual subclasses with register() exists but is uncommon in interviews.


46. What is multiple inheritance in Python?

Interview Answer

A class can list several bases: class MixinUser(Logged, Audited, User). Mixins (small classes that only add behaviour) are the usual justification.

If two bases define the same method, Python uses the MRO to pick one. Diamond inheritance is allowed. If you cannot explain MRO yet, say you prefer a single base plus composition, and that MRO is the follow-up chapter.

C# does not have multiple class inheritance; it has multiple interfaces. Do not copy C# habits blindly, and do not build a diamond unless you need it.


Important Point

Keep mixins stateless when you can. Cooperative super() in every mixin is required if they all override the same method.


Follow-up Question

Q: When is multiple inheritance a bad idea?

Answer

When bases are heavy frameworks that were not designed to stack, or when you are using inheritance only to reuse a utility function — that should be a module-level function instead.


47. How does try/except/else/finally work in Python?

Interview Answer

try:
    data = load()
except FileNotFoundError:
    data = []
except Exception:
    log.exception('load failed')
    raise
else:
    validate(data)  # runs if no exception
finally:
    cleanup()       # always runs

Catch specific exceptions. Bare except: also catches KeyboardInterrupt and SystemExit — almost never what you want. Use except Exception: if you must catch broadly, then re-raise.

else is for the success path without putting it in try (so new bugs there are not swallowed). finally is for cleanup.


Important Point

In Python 3 use except Error as e. Chaining: raise NewError() from e keeps the cause.


Follow-up Question

Q: Should you catch Exception everywhere?

Answer

No. Catch what you can handle. At the top of a worker you may catch Exception to log and continue the next job, then re-raise or dead-letter.


48. What is the difference between raise and assert?

Interview Answer

raise throws an exception on purpose as part of the API: invalid input, not found, conflict.

assert condition, 'msg' is for internal invariants. It raises AssertionError if the condition is false. Assertions can be stripped with python -O, so never use assert to validate user input or security checks.

if n < 0:
    raise ValueError('n must be >= 0')
assert items, 'caller must pass a non-empty list'  # debug only

Important Point

Production validation = raise ValueError / custom exceptions. assert = “this should be impossible if my code is correct.”


Follow-up Question

Q: Can you raise without an argument?

Answer

raise alone re-raises the current exception inside an except block. That is the right way to add logging then propagate.


49. How do you read and write a file in Python?

Interview Answer

with open('data.txt', 'r', encoding='utf-8') as f:
    text = f.read()

with open('out.txt', 'w', encoding='utf-8') as f:
    f.write('hello')

Modes: r read, w write (truncates), a append, x create-only, b binary. Always set encoding for text mode so behaviour is the same on Windows and Linux.

For large files, iterate: for line in f: instead of read().


Important Point

pathlib.Path('data.txt').read_text(encoding='utf-8') is the modern one-liner.


Follow-up Question

Q: What is the difference between read() and readlines()?

Answer

read() returns one string. readlines() returns a list of lines (including newlines) and can use a lot of memory. Prefer iterating the file object.


50. Why should you use with when opening files?

Interview Answer

with is a context manager. For files it guarantees close() runs even if an exception occurs.

f = open('a.txt')
try:
    data = f.read()
finally:
    f.close()
# same idea, less code:
with open('a.txt') as f:
    data = f.read()

Leaked file handles cause “too many open files” in long-running services. This is the everyday reason for with. Writing your own context manager is Advanced Python.


Important Point

The same pattern applies to locks, DB sessions, and HTTP clients that support the context manager protocol.


Follow-up Question

Q: What happens if you return inside with?

Answer

The file still closes. __exit__ runs when the block is left for any reason, including return.


51. What is the difference between import and from import?

Interview Answer

import math
math.sqrt(4)

from math import sqrt
sqrt(4)

from math import sqrt as square_root

import module binds one name and keeps the namespace clear. from module import name binds the name into your module. from module import * pollutes the namespace and is frowned on outside a REPL.

Both load the module once (cached in sys.modules). How that cache and circular imports work in depth is Advanced Python.


Important Point

Style: stdlib, third party, then local imports, each group separated by a blank line (PEP 8 / isort / Ruff).


Follow-up Question

Q: Is from x import y faster at call time?

Answer

It avoids an attribute lookup on the module, which is a tiny win. Choose based on readability, not micro-benchmarks.


52. How do you work with JSON in Python?

Interview Answer

import json

obj = json.loads('{"n": 1}')   # str -> Python
text = json.dumps(obj)         # Python -> str

json.dump(obj, file)
obj = json.load(file)

JSON objects become dicts, arrays become lists, numbers become int/float, null becomes None, true/false become True/False.

Only dicts with string keys, lists, numbers, bools, and None serialize by default. Datetime and custom classes need a default hook or a library like Pydantic.


Important Point

Never use eval on JSON. Never unpickle untrusted data; JSON is the safe interchange format for APIs. Pickle vs JSON as a design choice is Advanced Python.


Follow-up Question

Q: Why is dumps not pretty by default?

Answer

Compact JSON is smaller. Use indent=2 for humans. sort_keys=True helps stable diffs in tests.


53. What is pip and how do you install packages?

Interview Answer

pip is the standard installer for packages from PyPI (and other indexes).

python -m pip install requests
python -m pip install -r requirements.txt
python -m pip freeze > requirements.txt

Always run it as python -m pip so you install into the same interpreter you run. Pin versions in requirements for applications.

Wheels vs sdists, lock files, and Poetry/uv are the next level (Advanced Python packaging). Here the expected answer is pip + requirements.txt + a venv.


Important Point

Do not pip install into the system Python on Linux if you can avoid it. Use a virtual environment.


Follow-up Question

Q: What is the difference between pip install and conda install?

Answer

pip installs Python packages (usually from PyPI). conda installs packages from the conda ecosystem, including non-Python binaries. Many web teams only need pip.


54. Why do you use a virtual environment in Python?

Interview Answer

A venv is an isolated set of packages for one project so Project A’s Django 4 does not break Project B’s Django 5.

python -m venv .venv
# Windows:
.venv\Scripts\activate
python -m pip install -r requirements.txt

Each venv has its own site-packages. You commit requirements or a lockfile, not the venv folder.


Important Point

This FAQ is “why isolate dependencies.” Comparing venv vs Poetry vs uv vs Conda is Advanced Python.


Follow-up Question

Q: Can two projects share a venv?

Answer

They can, and then they fight over versions. One venv per project (or per service image) is the rule.


55. How do you write a simple unit test in Python?

Interview Answer

stdlib unittest:

import unittest
from app import add

class AddTests(unittest.TestCase):
    def test_adds_two_numbers(self):
        self.assertEqual(add(2, 3), 5)

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

Many teams use pytest instead: a plain assert add(2, 3) == 5 in a test_*.py file. Designing pytest fixtures and testable architecture is Advanced Python; here you only need “a test function, an assert, a test runner.”


Important Point

Name tests after behaviour, not after methods. Keep tests deterministic (no real network).


Follow-up Question

Q: Where do tests live?

Answer

A top-level tests/ folder mirroring the package, or tests next to the code. Match the repo. CI should run them on every PR.


56. What is the difference between Django and Flask?

Interview Answer

There is no universal winner. Say what you have shipped and why it fit the problem.


Important Point

Senior interviews care that you know the trade-off: Django speed of features vs Flask/FastAPI flexibility. Not that you memorized every setting name.


Follow-up Question

Q: Is Flask dead because of FastAPI?

Answer

No. Plenty of production Flask exists. New HTTP APIs often pick FastAPI for typing and OpenAPI. Choose based on the team and the problem.


57. How do you make an HTTP request in Python?

Interview Answer

The common library is requests (sync):

import requests

r = requests.get('https://api.example.com/users', timeout=5)
r.raise_for_status()
data = r.json()

Always set a timeout. Always check status. For async code, httpx or aiohttp is the usual choice (asyncio details are Advanced Python).

stdlib urllib works but is more verbose; interviews expect requests or httpx.


Important Point

Do not skip TLS verification to “make it work.” Fix certificates or use a proper CA bundle.


Follow-up Question

Q: How do you send JSON in a POST?

Answer

requests.post(url, json={'n': 1}, timeout=5) sets the body and Content-Type. Use data= for form bodies.


58. What is the difference between a NumPy array and a Python list?

Interview Answer

Use lists for general collections. Use NumPy/pandas for numeric data, tables, and anything you would otherwise loop in Python.

import numpy as np
np.array([1, 2, 3]) * 2  # array([2, 4, 6])

Important Point

You do not need to be a data scientist for this question. You need to know why “a million floats in a list” is the wrong default.


Follow-up Question

Q: Is pandas the same as NumPy?

Answer

pandas is built on NumPy and adds labeled tables (DataFrame), alignment, and time series helpers. NumPy is the array layer.


59. What are Python’s advantages and disadvantages?

Interview Answer

Advantages: fast to write, readable, huge libraries, great for automation, APIs, data, and ML, easy to glue systems together.

Disadvantages: slower CPU-bound code than compiled languages, packaging can confuse newcomers, dynamic types need discipline (hints + tests), CPython GIL limits CPU threads, mobile/desktop GUI is not its strongest story.

A strong answer pairs each con with a mitigation you have used: type checkers, process workers, NumPy, rewriting a hot path.


Important Point

Do not bash Python or worship it. Interviewers hire people who pick tools on purpose.


Follow-up Question

Q: When would you not choose Python?

Answer

Ultra-low-latency trading loops, constrained embedded devices, or a codebase that is already C#/.NET end-to-end with no Python skills on the team. Also when you need a single binary with a simple deploy story and the team already has Go.


60. How do you debug a Python program?

Interview Answer

For running services, add request IDs, metrics, and traces. Local pdb is not how you debug Kubernetes.


Important Point

If you only name print, you sound junior. If you only name distributed tracing, you sound like you have never opened pdb. Mention both scales.


Follow-up Question

Q: What is pdb’s most useful command?

Answer

n next, s step in, c continue, p expr print, l list, q quit. pp pretty-prints. You do not need to memorize all of them.