Learning Python - Core Concepts & Main Architecture
Episode 2 of 23

Learning Python - Core Concepts & Main Architecture

This episode breaks down how Python works behind the scenes: CPython, bytecode, the interpreter loop, and the GIL. You'll also get to know alternative implementations such as PyPy, Jython, IronPython, and GraalPython, as well as Python project structure: modules, packages, the import system, and sys.path.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Most people learn Python without ever knowing what happens behind python3 main.py. Episode 2 pulls back that curtain: how the CPython interpreter works, what bytecode is, why the GIL is so talked about, and how your code is organized into modules and packages.

Understanding this architecture isn't just theory. It explains why Python is slow in certain cases, why threading doesn't always speed things up, and when to use alternative implementations. Let's dissect it layer by layer.

CPython and the Execution Flow

What Is CPython

CPython is the reference implementation of Python, written in the C language. This is the interpreter you installed via pyenv in episode 0. CPython sets the standard because it's the most complete and the most widely used in the world.

A Two-Stage Execution Flow

CPython runs code in two stages: compilation to bytecode, then execution by the interpreter loop. The flow is simple:

Alur eksekusi CPython
sumber.py → bytecode → interpreter loop → hasil

Bytecode is an intermediate representation similar to machine code, but at a higher level. You can see the bytecode of a function using the dis module:

PythonMelihat bytecode
import dis
 
def tambah(a, b):
    return a + b
 
dis.dis(tambah)

The output of dis.dis(tambah) shows instructions like LOAD_FAST, BINARY_OP, and RETURN_VALUE. This proves that Python does compile code before executing it — it just caches the result in memory.

The Interpreter Loop and the GIL

The Nature of Bytecode and the GIL

A key fact you should know: since version 3.13, CPython supports a free-threaded build that optionally removes the GIL. However, on the standard build, the GIL (Global Interpreter Lock) still exists and locks bytecode execution to only one thread at a time.

PythonThread tetap butuh GIL
import threading
 
def kerja():
    total = 0
    for _ in range(1_000_000):
        total += 1
 
t = threading.Thread(target=kerja)
t.start()
t.join()
print("selesai")

On the standard CPython build, running kerja() on many threads does not speed up CPU computation because of the GIL. threading.Thread(target=kerja) creates a new thread, but the GIL ensures only one thread executes bytecode at a time.

Consequences of the GIL

The GIL makes CPython simple and safe for memory management, but it limits CPU parallelism. The common solutions are multiprocessing for heavy computation (episode 15) or using PyPy and free-threaded CPython. So don't be surprised if Python doesn't feel as fast as C for intensive calculations.

Alternative Implementations

PyPy and GraalPython

CPython isn't the only way to run Python. Several alternative implementations offer different advantages:

  • PyPy: an interpreter with a JIT compiler that is very fast for CPU-bound code.
  • GraalPython: an implementation on top of GraalVM for integration with the JVM ecosystem.
  • Jython: Python running on the JVM, suitable for Java integration.
  • IronPython: Python for the .NET platform.

When Alternative Implementations Matter

PyPy is attractive for intensive computation because of its JIT. Jython and IronPython are rarely used, but important when you need to interact with the Java or .NET ecosystems. For this series, we stick with CPython because it's the most compatible with modern libraries.

Modules and Packages

The Difference Between Modules and Packages

In Python, a module is a single .py file, while a package is a directory containing modules with an __init__.py file. This package marker is what distinguishes it from a regular directory. A simple structure looks like this:

Struktur package
project/
    main.py
    utils/
        __init__.py
        helper.py

The __init__.py file can be empty or contain initialization. With this structure, you can import via from utils.helper import fungsi. The __init__.py marker makes the utils directory recognized as a package.

The Import System and sys.path

When you write import modul, Python looks for the module in directories registered in sys.path. The search order includes the script directory, environment variables, and site-packages. You can see the list:

PythonMelihat sys.path
import sys
 
for path in sys.path:
    print(path)

The loop for path in sys.path prints the import search locations. If a local file name collides with a library, the local file wins. That's why you should avoid naming files after standard modules like random.py or json.py.

Practicing Project Structure

Building Your First Project

Let's practice the concepts above in a small project:

Membuat struktur project
mkdir belajar-struktur
cd belajar-struktur
touch __init__.py main.py

The command mkdir belajar-struktur creates a directory, then touch main.py creates the main file. With this structure, you already have an empty package ready to be filled in episodes 3 and 7.

Why Structure Matters

A tidy structure determines how easy maintenance is. Small, focused modules are easier to test, import, and reuse. This habit will continue into episode 7 when we discuss packaging and pyproject.toml.

Closing

Key takeaways:

  • CPython is the reference implementation written in the C language.
  • Python is compiled to bytecode, then executed by the interpreter loop.
  • The GIL limits bytecode execution to one thread at a time.
  • PyPy, GraalPython, Jython, and IronPython are alternative implementations.
  • A module is a .py file; a package is a directory with init.py.
  • sys.path determines the search locations at import time.

In the next episode, episode 3, we'll cover syntax and program structure — built-in data types, operators, control flow, functions, and comprehensions, complete with naming and docstring best practices. This is the time to start writing lots of Python code!