Python fundamentals are the foundation for every data professional. Master variables, data types, control flow, and functions to solve any coding problem. These concepts appear in every interview and real-world code.
Variables and Dynamic Typing
Assignment & Reference Semantics: Python uses reference semantics: x = y makes x point to same object as y, not copy. Mutable objects (lists, dicts) share references: both point to same memory. Immutable objects (int, str, tuple) create new objects on reassignment. Understanding this prevents bugs: modifying y after x = y modifies y only if mutable.
y = x
y = 10
print(x) # 5 (immutable int, x unchanged)
list1 = [1, 2, 3]
list2 = list1
list2.append(4)
print(list1) # [1, 2, 3, 4] (mutable list, both changed)
Mutable vs Immutable Objects: Immutable: int, float, str, tuple, frozenset. Once created, cannot change. Reassignment creates new object. Hashable: can be dict keys, set members. Mutable: list, dict, set. Modifiable after creation. Not hashable: cannot be dict keys. This distinction affects performance and correctness: immutable suitable for caching, mutable for collections.
immutable_str = “hello”
mutable_list = [1, 2, 3]
mutable_dict = {“a”: 1}
my_set = {immutable_tuple, immutable_str} # Works
my_dict = {immutable_tuple: “value”} # Works
my_set = {mutable_list} # TypeError: unhashable
Data Types Essentials: int (unlimited precision), float (64-bit), bool (True/False, subclass of int), str (immutable sequences), list (mutable sequences), tuple (immutable sequences), dict (key-value pairs), set (unique unordered). Type conversion: int(), str(), list(), etc. type() returns type, isinstance() checks type. Numbers: + – * / // % ** operations.
y = 3
print(x // y, x % y, x ** y)
str_val = “123”
int_val = int(str_val)
my_list = [1, 2, 3]
my_tuple = tuple(my_list)
my_set = {1, 2, 2} # {1, 2} auto-dedup
print(type(x), isinstance(x, int))
Control Flow: Conditional Execution: if/elif/else branches based conditions. Truthiness: 0, empty ([], “”, {}, set()), None are falsy. Python evaluates left-to-right. Short-circuit evaluation: “a and b” stops if a is falsy. “a or b” stops if a is truthy. Ternary: x if condition else y. Exception handling: try-except-finally manages errors gracefully.
if age >= 18:
print(“Adult”)
elif age >= 13:
print(“Teen”)
else:
print(“Child”)
status = “active” if age >= 18 else “inactive”
try:
x = int(“abc”)
except ValueError:
x = 0
finally:
print(“Done”)
Loops: Iteration Patterns: for loops over sequences (list, tuple, str, range, dict). range(n) generates 0 to n-1. enumerate() adds index. zip() pairs elements from sequences. while loops repeat until condition false. break exits loop, continue skips iteration. List comprehensions: [expr for item in seq if condition] create filtered lists efficiently. Generator expressions: (expr for item in seq) return iterators (lazy evaluation, memory-efficient).
print(i)
for idx, val in enumerate([“a”, “b”, “c”]):
print(idx, val)
for x, y in zip([1, 2], [3, 4]):
print(x, y)
i = 0
while i < 5: print(i) i += 1 squares = [x**2 for x in range(5)] even_gen = (x for x in range(100) if x % 2 == 0)
Functions & Advanced Concepts: def keyword defines function. return exits with value. *args: variable positional arguments (tuple). **kwargs: variable keyword arguments (dict). Unpacking: *seq unpacks sequences, **dict unpacks dictionaries. lambda: anonymous function. map(func, seq), filter(condition, seq) apply functions to sequences.
return f”{greeting}, {name}!”
def sum_all(*args):
return sum(args)
def print_config(**kwargs):
for key, val in kwargs.items():
print(f”{key}={val}”)
a, b, *rest = [1, 2, 3, 4]
print(greet(“Alice”))
print(sum_all(1, 2, 3))
print_config(host=”localhost”, port=8080)
result = list(map(lambda x: x**2, [1, 2, 3]))
5 Complete Solved Coding Problems
Question 1: Floyd’s Cycle Detection (O(1) Space)
Find duplicate in list of n integers (1 to n-1). Must solve in O(1) extra space, O(n) time. Treat list as linked list: each value points to index.
slow = fast = nums[0]
while True:
slow = nums[slow]
fast = nums[nums[fast]]
if slow == fast:
break
slow = nums[0]
while slow != fast:
slow = nums[slow]
fast = nums[fast]
return slow
print(find_duplicate([1, 3, 4, 2, 2])) # 2
print(find_duplicate([3, 1, 3, 4, 2])) # 3
Explanation: Two pointers: slow moves 1 step, fast 2 steps. When they meet, cycle exists (duplicate). Second pass finds entry point. Floyd’s algorithm: elegant cycle detection, constant space, no set/dict needed.
Google – Senior SWE
Question 2: Mutable Default Arguments Pitfall
Why does function with list default create persistent state? How to fix safely?
items.append(item)
return items
print(append_item_wrong(1)) # [1]
print(append_item_wrong(2)) # [1, 2] (same list!)
def append_item_right(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(append_item_right(1)) # [1]
print(append_item_right(2)) # [2] (new list)
Explanation: Default arguments evaluated once at function definition, not each call. Mutable defaults (list, dict) shared across calls. Bug: modifications persist. Fix: use None as default, create new list inside function. Safe pattern.
Meta – Software Engineer
Question 3: Generator vs List Memory (O(1) vs O(n))
Create 1M integers. Compare memory: list [x for x in range(1M)] vs generator (x for x in range(1M)). Explain difference.
list_comp = [x for x in range(1000000)]
print(f”List memory: {sys.getsizeof(list_comp) / 1024 / 1024:.2f} MB”) # ~42 MB
gen_exp = (x for x in range(1000000))
print(f”Generator memory: {sys.getsizeof(gen_exp) / 1024:.2f} KB”) # ~0.05 MB
for val in gen_exp:
if val > 5:
break
Explanation: List stores all 1M values in memory (42MB). Generator computes values lazily on-demand (constant memory). Use list when you need random access, generator for large data/streaming. Generator 800x more memory-efficient here.
Amazon – Senior SWE
Question 4: Unpacking with *args/**kwargs
Function accepts variable arguments. Unpack list and dict to function call. Return combined result.
return (a + b + c) * multiplier
args = [1, 2, 3]
kwargs = {“multiplier”: 2}
result = calculate(*args, **kwargs)
print(result) # (1+2+3)*2 = 12
def flexible(*args, **kwargs):
total = sum(args)
for key, val in kwargs.items():
print(f”{key}={val}”)
return total
print(flexible(1, 2, 3, name=”test”, value=10)) # 6
Explanation: *args unpacks list as positional arguments. **kwargs unpacks dict as keyword arguments. Enables flexible function signatures. Essential for decorators, wrappers, callbacks.
Apple – Software Engineer
Question 5: List/Dict Comprehension with Filtering
Count word frequencies in text. Filter words length > 3. Use dict comprehension. Result: {word: count}.
words = text.split()
from collections import Counter
freq = Counter(words)
filtered = {word: count for word, count in freq.items() if len(word) > 3}
print(filtered) # {‘quick’: 1, ‘brown’: 1, ‘jumps’: 1, …}
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares) # [0, 4, 16, 36, 64]
Explanation: Comprehensions combine filtering and transformation in one expression. More readable than loop. Faster execution (C-level). Dict comprehension creates filtered dicts. Essential for data manipulation.
Microsoft – Senior SWE
15 Practice Questions (Test Your Understanding)
Question 6: Calculate mean, median, mode using *args. Single function for any number of values.
Question 7: Flatten nested list [[1, 2], [3, [4, 5]]] recursively. Return single-level [1, 2, 3, 4, 5].
Question 8: Function accepting both *args and **kwargs. Parse each type separately, combine results.
Question 9: Validate password: min 8 chars, uppercase, lowercase, digit, special. Return detailed feedback (missing requirements).
Question 10: Fibonacci generator yielding infinite sequence. Use yield statement, test with next().
Question 11: Rotate list left by k positions. [1,2,3,4,5] rotated 2 → [3,4,5,1,2]. No built-in rotate().
Question 12: Create dict from two lists: keys from list1, values from list2. Use zip().
Question 13: Check Armstrong number (narcissistic): sum of digits^n equals number. n = digit count.
Question 14: Two-sum problem: find two numbers in list summing to target. Return indices. O(n) time.
Question 15: Decorator counting function calls. Store count, reset method. @counter applied to any function.
Question 16: Convert number to base k. Recursive approach. Result: string representation.
Question 17: Longest common substring of two strings. Return substring, not just length.
Question 18: Check if two strings are anagrams. Ignore case, spaces. Return True/False.
Question 19: Remove duplicates from list preserving order. Cannot use set (loses order).
Question 20: Prefix sum array: [1,2,3,4] → [1,3,6,10]. Efficient O(n) solution.
Master Python Fundamentals
Get our comprehensive Data Analytics: Ace All The Interview Concepts course.