my_list = [1, 2, 3]
my_list.append(4) # OK: lists are mutable
my_tuple = (1, 2, 3)
# my_tuple.append(4) # Error: tuples have no append method, they're immutable
Topics
20
Async Generators & Async Context Managers
Command-Line Interfaces (argparse)
Comprehensions & Generators
Concurrency (asyncio/threading/multiprocessing)
Data Types & Structures
Dataclasses & NamedTuples
Debugging & Profiling
Decorators
Descriptors & Properties
Exception Handling
File I/O & Context Managers
Functions & Scope
functools & Functional Programming Tools
Iterators & the Iterator Protocol
Logging
Magic Methods & Operator Overloading
Memory Management & Garbage Collection
Metaclasses & Class Customization
Modules & Packaging
Multiple Inheritance & MRO
Data Types & Structures
15 questions found
A list is MUTABLE (elements can be added, removed, or changed after creation), created with square brackets; a tuple is IMMUTABLE (fixed once created), created with parentheses — tuples are also slightly faster and can be used as dictionary keys or set elements, unlike lists.
Real-world example
Using a tuple for a fixed (x, y) coordinate pair that should never change, versus a list for a growing shopping cart.
Functions & Scope
A set stores only UNIQUE elements (duplicates are automatically discarded) and doesn't preserve insertion order in the general case; a list preserves both order AND duplicates — sets also provide much faster O(1) average membership testing (`in`) compared to a list's O(n) linear scan.
unique = {1, 2, 2, 3, 3, 3}
print(unique) # {1, 2, 3} -- duplicates automatically removed
ordered = [1, 2, 2, 3]
print(ordered) # [1, 2, 2, 3] -- duplicates and order preserved
Real-world example
Deduplicating a large list of user IDs while getting fast O(1) membership checks instead of a slow list scan.
Data Types & Structures
Access a value with dict[key] (raises KeyError if missing) or dict.get(key, default) (returns None or a default instead); add/update with dict[key] = value; remove with del dict[key] or dict.pop(key), the latter also returning the removed value.
person = {"name": "Sam", "age": 30}
print(person["name"]) # 'Sam'
person["city"] = "NYC" # add a new key
age = person.pop("age") # remove and return 30
print(person.get("email", "N/A")) # 'N/A' -- safe default lookup
Real-world example
Storing and updating a user's profile information as key-value pairs, safely handling missing fields.
Exception Handling
What's the difference between shallow copy and deep copy for a nested data structure, and which functions perform each?
IntermediateA shallow copy (via list.copy(), dict.copy(), or copy.copy()) creates a NEW outer container but still references the SAME nested/inner objects — mutating a nested list inside a shallow copy affects the original too. A deep copy (via copy.deepcopy()) recursively copies EVERY nested object, producing a fully independent structure.
import copy
original = [[1, 2], [3, 4]]
shallow = original.copy()
shallow[0].append(99)
print(original) # [[1, 2, 99], [3, 4]] -- original affected too!
deep = copy.deepcopy(original)
deep[0].append(100)
print(original) # unaffected by the deep copy's mutation
Real-world example
Safely duplicating a nested configuration dictionary before modifying it, without accidentally corrupting the original.
Memory Management & Garbage Collection
list[start:stop:step] extracts a sub-list from index 'start' (inclusive) up to 'stop' (exclusive), advancing by 'step' — negative indices count from the END of the list, and a negative step reverses direction, letting you write concise expressions like list[::-1] to reverse a list entirely.
numbers = [0, 1, 2, 3, 4, 5]
print(numbers[1:4]) # [1, 2, 3]
print(numbers[-2:]) # [4, 5] -- last two elements
print(numbers[::2]) # [0, 2, 4] -- every other element
print(numbers[::-1]) # [5, 4, 3, 2, 1, 0] -- reversed
Real-world example
Extracting a specific range of records or reversing an ordered list of results without a manual loop.
Iterators & the Iterator Protocol
Why must dictionary keys (and set elements) be hashable, and what makes an object hashable in Python?
IntermediateDictionaries and sets use HASHING internally for O(1) average lookup, requiring keys to have a stable, consistent hash value (via __hash__) that doesn't change during the object's lifetime — this is why mutable types like list and dict CAN'T be dictionary keys (their contents, and thus their hash, could change), while immutable types like str, int, and tuple (of hashable elements) can.
valid_dict = {("a", 1): "value"} # OK: tuple of hashable elements is hashable
# invalid_dict = {["a", 1]: "value"} # TypeError: unhashable type: 'list'
Real-world example
Understanding why you must convert a list to a tuple before using it as a dictionary key or adding it to a set.
Magic Methods & Operator Overloading
What is the difference between collections.deque and a regular list for queue-like operations?
Intermediatedeque (double-ended queue) provides O(1) appends and pops from BOTH ends, while a regular list's insert(0, ...) and pop(0) are O(n) because every remaining element must shift — making deque the correct choice for a FIFO queue or any structure needing efficient operations at the front.
from collections import deque
queue = deque()
queue.append(1) # O(1): add to the right end
queue.appendleft(0) # O(1): add to the left end
queue.popleft() # O(1): remove from the left end
# A regular list's list.pop(0) is O(n) -- much slower for this use case
Real-world example
Implementing an efficient task queue or a sliding-window buffer that needs fast operations at both ends.
Standard Library Essentials (collections
itertools)
How does frozenset differ from a regular set, and what specific use case does its immutability enable?
Advancedfrozenset is an IMMUTABLE version of set — once created, it can't be modified, which (like tuples) makes it HASHABLE and therefore usable as a dictionary key or as an element of another set, something a regular mutable set cannot do.
regular_set = {1, 2, 3}
# hash(regular_set) # TypeError: unhashable type: 'set'
frozen = frozenset([1, 2, 3])
print(hash(frozen)) # works fine
cache = {frozen: "cached result"} # usable as a dict key
Real-world example
Using a frozenset of tags or permissions as a dictionary key to cache results based on that exact combination.
Magic Methods & Operator Overloading
How does Python's small integer caching (interning) affect identity comparisons ('is') versus equality comparisons ('==') for integers?
AdvancedCPython pre-caches and reuses integer objects in the range -5 to 256 for performance, so 'is' comparisons between small integers in this range often return True even for separately-created values — but this is an IMPLEMENTATION DETAIL, not a language guarantee, so 'is' should never be relied upon for integer equality; always use '==' for value comparison.
a = 100
b = 100
print(a is b) # True -- likely, due to small int caching (implementation detail!)
c = 1000
d = 1000
print(c is d) # often False -- outside the cached range, separate objects
# Always use '==' for value equality, regardless of caching behavior
print(c == d) # True, reliably
Real-world example
Debugging a subtle bug caused by incorrectly using 'is' instead of '==' to compare integer values.
Memory Management & Garbage Collection
How would you efficiently merge two dictionaries in modern Python, and how do the different approaches handle overlapping keys?
AdvancedPython 3.9+ supports the merge operator (`|`) and update operator (`|=`), and dict unpacking (`{**a, **b}`) also works in earlier versions — in ALL cases, when both dictionaries share a key, the value from the SECOND (right-hand or later) dictionary wins, overwriting the first.
defaults = {"theme": "light", "retries": 3}
overrides = {"theme": "dark"}
merged = defaults | overrides # Python 3.9+: {'theme': 'dark', 'retries': 3}
merged2 = {**defaults, **overrides} # equivalent, works in older Python too
Real-world example
Merging a user's custom settings with a set of application defaults, letting the user's values take precedence.
functools & Functional Programming Tools
Showing 1–10 of 15