CPython Bytecode
CPython compiles Python source into code objects containing bytecode and metadata, then executes that bytecode with its interpreter. Bytecode is useful for inspection, but it is version-sensitive and does not by itself predict performance.
CPython compiles Python source into code objects. A code object contains bytecode along with metadata such as constants, names, and source-location information. Function bodies are typically compiled when their containing module is compiled, while constructs such as `eval` can compile source dynamically.
The bytecode is a sequence of operations for CPython's interpreter, such as loading values, performing operations, and returning results. The interpreter processes those operations and coordinates Python-level behavior, including object handling and exceptions. Bytecode is not native machine instructions executed directly by the processor.
Bytecode is useful for understanding how CPython represents a program. The standard-library `dis` module can display a human-readable disassembly, but instruction names, layouts, and optimizations may differ across Python versions. Treat bytecode inspection as diagnostic, not as a portable interface.
Bytecode compilation alone does not make Python code fast. Runtime cost often comes from dynamic operations, object allocation, function calls, and library or I/O work. For performance work, measure representative workloads first, then optimize the dominant cost rather than guessing from the disassembly.
Key points
- CPython compiles source into code objects that include bytecode and metadata.
- The CPython interpreter executes bytecode; it is not native machine code.
- Use `dis` to inspect bytecode, while expecting details to vary by CPython version.
- Bytecode inspection does not replace profiling and benchmarking.
Interview questions
What is CPython bytecode, and how does CPython use it?
CPython compiles source into code objects that contain bytecode and metadata. The interpreter executes the bytecode, handling Python objects and language behavior as it goes. This differs from compiling directly to native machine code, which a processor can execute without a Python bytecode interpreter.
How can you inspect the bytecode for a Python function, and what should you be cautious about?
Use `dis.dis` to inspect a function or code object, for example by importing `dis` and passing it a function. The displayed instructions can help explain control flow or operations, but their exact names and arrangement depend on the CPython version. Use the disassembly as a debugging aid rather than assuming it is a stable contract.
Does simpler or shorter bytecode necessarily mean faster Python code?
Bytecode is a representation of the program, not a performance score. Disassembly can reveal operations or control flow, but it does not account for the full cost of dynamic dispatch, object allocation, data sizes, cache behavior, or external work. Profile a representative workload to locate its bottleneck, then benchmark a targeted change under comparable conditions.
How does a CPython function relate to its code object?
In CPython, function code is represented by a code object, and a function refers to that code object along with its global namespace and other runtime state. Code objects are not the same as function objects: the latter carry the context needed to call the code. Nested functions generally have their own code objects, while module-level execution also has a code object.