Python
Python syntax, built-ins, data structures
Python is a high-level, dynamically-typed programming language known for its readable syntax and versatility. In technical assessments, Python questions test understanding of core data types (lists, dictionaries, sets, tuples), comprehensions, lambda functions, decorators, memory management, and common gotchas like mutable default arguments and variable scoping. Mastery of Python's built-in functions and standard library is essential for solving coding problems efficiently.
Complexity / Key Facts
Key Concepts
Mutable vs Immutable Types
Mutable objects (lists, dictionaries, sets) can be modified after creation. Immutable objects (int, float, string, tuple, frozenset) cannot be changed once created. Operations on immutables create new objects. This distinction is crucial for understanding how Python handles assignment, function arguments, and default values. For example, `a = [1, 2]; b = a; b.append(3)` modifies both a and b, but `a = (1, 2); b = a; b += (3,)` creates a new tuple for b while a remains unchanged.
List Operations and Methods
Lists support indexing, slicing (negative step reverses), concatenation (+), repetition (*), and membership testing (in). Key methods: append(x) adds one element, extend(iterable) adds multiple, insert(i, x) inserts at index, remove(x) removes first occurrence of value, pop([i]) removes and returns element at index (default last), sort() sorts in place (key parameter for custom sorting), reverse() reverses in place. Slicing creates shallow copies: `lst[:]` or `lst.copy()` duplicates the list.
Dictionary Operations
Dictionaries store key-value pairs with O(1) average lookup. Keys must be hashable (immutable). Access: d[key] raises KeyError if missing; use d.get(key, default) for safe access. Methods: keys(), values(), items() return view objects. setdefault(key, default) sets value if key missing. pop(key, default) removes and returns. update() merges dictionaries. dict comprehension: {k: v for k, v in pairs}. Since Python 3.7+, dicts maintain insertion order.
Set Operations
Sets are unordered collections of unique, hashable elements. Creation: {1, 2, 3} or set(iterable). Methods: add(x), remove(x) (raises KeyError if missing), discard(x) (no error), pop() removes arbitrary element. Set algebra: union (| or union()), intersection (& or intersection()), difference (- or difference()), symmetric difference (^ or symmetric_difference()). Subset/superset tests: issubset(), issuperset(), isdisjoint(). Sets are ideal for membership testing and removing duplicates.
Comprehensions
Comprehensions provide concise syntax for creating collections. List: [x*2 for x in range(5)] produces [0, 2, 4, 6, 8]. Dict: {x: x*x for x in range(5)}. Set: {x for x in iterable if x > 0}. Nested comprehensions: [[i*j for j in range(3)] for i in range(3)]. Generator expression: (x*2 for x in range(5)) - lazy evaluation, memory efficient. Comprehensions are generally faster than equivalent for-loops.
Lambda Functions and Higher-Order Functions
Lambda creates anonymous functions: lambda args: expression. Limited to single expression, no statements. Common use with map(), filter(), sorted(): `sorted(lst, key=lambda x: x[1])` sorts by second element. Map applies function: `map(lambda x: x*2, [1, 2, 3])` returns iterator yielding [2, 4, 6]. Filter selects elements: `filter(lambda x: x > 0, [-1, 1, -2, 2])` yields [1, 2]. Reduce (from functools) aggregates: `reduce(lambda a, b: a+b, [1, 2, 3, 4])` returns 10.
Variable Scope and Closures
Python has LEGB scope resolution: Local, Enclosing, Global, Built-in. `global` keyword declares global variable; `nonlocal` refers to nearest enclosing non-global scope. Default arguments are evaluated once at definition time - mutable defaults cause bugs: `def f(lst=[]): lst.append(1)` accumulates across calls. Closures remember enclosing scope: functions returning functions capture variables from outer scope. Late binding closures capture variable names, not values - use default arguments to bind values immediately.
Common Python Gotchas
1) `is` vs `==`: `is` checks identity (same object), `==` checks equality. `None is None` is correct, never use `== None`. 2) Chained comparisons: `1 < x < 10` is valid Python, equivalent to `1 < x and x < 10`. 3) Boolean values: 0, '', [], {}, None are falsy; everything else is truthy. 4) Integer caching: -5 to 256 are pre-allocated; `is` may work for equality accidentally. 5) Shallow vs deep copy: `copy.copy()` shallow copies, `copy.deepcopy()` recursively copies nested structures. 6) Modifying iterable while iterating causes unexpected behavior - iterate over a copy instead.
Tips
- Remember that `is` checks object identity while `==` checks value equality. Use `is` only for None checks and singleton comparisons.
- Avoid mutable default arguments in function definitions. Use `None` as default and initialize inside the function.
- Use list/dict comprehensions over map/filter with lambdas for better readability and often better performance.
- When iterating over a dictionary, use `.items()` to get both key and value: `for k, v in d.items()`.
- For efficient membership testing (checking if item exists), use sets instead of lists -- O(1) vs O(n).
- Remember that strings and tuples are immutable -- methods that appear to modify them actually return new objects.
- Use `enumerate()` when you need both index and value while looping: `for i, val in enumerate(lst)`.
Practice with questions
Real placement-style technical questions.