CPython is the reference Python implementation and the default target for many Python libraries. Its behavior matters when processing large datasets: dynamic features cost time and memory, and optimizing Python code alone may not make a critical loop fast enough. section 1.3 section 1.3.1
CPython uses operating-system threads, but its Global Interpreter Lock (GIL) permits only one thread to execute Python code at a time. Threads can still make progress on overlapping tasks, such as work that waits for I/O; they do not make CPU-bound Python loops run in parallel across cores. Lower-level library code can release the GIL, while multiprocessing provides another route to parallel work. section 1.3.1 section 3.3.3
Measure before changing implementations
Time a Python matrix-multiplication function under CPython, then run the same function under PyPy. The comparison isolates an implementation choice rather than treating Python performance as a fixed property. For a costly inner loop, Cython offers a different route: inspect its generated C code for interactions with the Python interpreter and reduce those interactions. section 1.3 section 5.8
Memory measurements need similar care. `sys.getsizeof` reports a container’s size, not the full footprint of everything it refers to. Summing the apparent sizes of a list and all its entries can also mislead because CPython’s allocation behavior makes the naive estimate unreliable. section 2.4.1
print(sys.getsizeof(all_files)) print(sys.getsizeof(all_files.values())) print(sys.getsizeof(list(all_files.values())))
Compare the dictionary, its values iterator, and a list made from those values. These calls measure each object itself—not all referenced data. section 2.4.1

