Structure your answer by first comparing lists and dictionaries across the four dimensions (time complexity, iteration order, mutability, memory), then address map() vs list comprehensions with trade-offs and use cases. Emphasize practical implications and performance considerations, tying back to real-world scenarios.
Pro tip: Mention that in Python 3.7+, dictionaries maintain insertion order, but this is an implementation detail that became a language guarantee—showing you stay updated with Python's evolution. Also, note that list comprehensions are generally faster than map() with lambda, but map() can be more memory-efficient with lazy evaluation in Python 3.
Explain that list indexing is O(1) but search is O(n), while dictionary key lookup is O(1) average case. Mention that dictionaries use hash tables, so worst-case is O(n) but rare.
State that lists are ordered by insertion, and dictionaries (Python 3.7+) also maintain insertion order, but before 3.7 order was arbitrary. Clarify that order preservation in dicts is now guaranteed.
Both are mutable, but lists allow duplicate elements and are index-based, while dicts require hashable keys and unique keys. Memory-wise, dicts have higher overhead due to hash table structure, while lists are more compact for sequential data.
map() applies a function to each item and returns an iterator (lazy), while list comprehensions build a list eagerly. Comprehensions are often more readable and faster for simple transformations; map() can be better for memory with large data or when using an existing function.
Choose list comprehensions for readability, speed, and when you need a list immediately. Choose map() for lazy evaluation, memory efficiency, or when applying a pre-defined function without lambda.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
This one tripped me up more than I expected.
Start by defining the GIL and its impact on CPU-bound vs I/O-bound multithreaded programs. Then, compare threading and multiprocessing, giving clear criteria for when to use each. Finally, discuss safe state sharing with synchronization primitives, highlighting race conditions and deadlock avoidance.
Pro tip: Mention that the GIL is an implementation detail of CPython, not a language feature, and that other Python implementations like Jython or PyPy (with STM) don't have it. Also, note that the GIL can be released during I/O and C extensions, which is why threading still helps for I/O-bound tasks.
Explain that the GIL is a mutex that protects access to Python objects, preventing multiple threads from executing Python bytecode simultaneously. It exists to simplify memory management and ensure thread safety in CPython.
Describe how the GIL limits parallelism for CPU-bound tasks, as only one thread runs at a time. For I/O-bound tasks, threads can still be effective because the GIL is released during I/O operations.
Choose threading for I/O-bound tasks due to lower overhead and shared memory. Use multiprocessing for CPU-bound tasks to bypass the GIL and achieve true parallelism, at the cost of higher overhead and inter-process communication.
For threads, use locks, queues, or other synchronization primitives to protect shared mutable state. For processes, use multiprocessing primitives like Queue, Pipe, or shared memory with locks. Always minimize shared state and prefer message passing.
Use locks consistently and in a fixed order to prevent deadlocks. Employ higher-level abstractions like queues or concurrent.futures to reduce manual synchronization. Consider timeouts and deadlock detection mechanisms.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.