CPython, Bytecode ve Python Virtual Machine (PVM) A developer explains the internal architecture of CPython, the standard Python interpreter, detailing how Python source code is tokenized, parsed, compiled into bytecode, and executed by the Python Virtual Machine (PVM). The post clarifies that Python is a language specification and CPython is its most common implementation, written in C. It also describes how bytecode is stored in .pyc files for faster subsequent execution. Python öğrenen çoğu geliştirici şu aşamaya kadar gelir: x = 10 y = 20 print x + y Kod çalışır, sonuç gelir ve konu kapanır. Fakat profesyonel seviyede Python geliştiren, performans optimizasyonu yapan veya Python'un iç mimarisini anlamak isteyen bir yazılımcının aşağıdaki üç kavramı çok iyi bilmesi gerekir: Bu üçü Python'un çalışma motorunu oluşturur. Bir Python programı çalıştırıldığında süreç şu şekildedir: Python Source Code ↓ Tokenizer ↓ Parser ↓ AST ↓ Compiler ↓ Bytecode ↓ Python Virtual Machine ↓ CPython Runtime ↓ Operating System ↓ CPU Buradaki en kritik bölüm: Bytecode ↓ PVM çiftidir. Öncelikle çok önemli bir yanlış anlaşılmayı düzeltelim. Birçok kişi Python'u bir dil zanneder. Aslında: Python = Dil Spesifikasyonu CPython ise: Python'un en yaygın implementasyonu dur. Nasıl ki: C Java JavaScript bir dil tanımıysa, Python da bir dil tanımıdır. Bu tanımı çalıştıran farklı motorlar vardır. Örneğin: | Implementasyon | Dil | |---|---| | CPython | Python | | PyPy | Python | | Jython | Python | | IronPython | Python | | MicroPython | Python | Hepsi Python kodu çalıştırır. Ama iç yapıları farklıdır. CPython: C dili ile yazılmış Python yorumlayıcısıdır. Python.org üzerinden indirdiğiniz standart Python budur. Örneğin: python app.py komutu verdiğinizde çalışan şey CPython'dur. CPython'ın kaynak kodu yaklaşık: 500.000+ satır C içerir. Kaynak kodu: CPython: x = 10 y = 20 print x + y kodunu alır. Sonra: Token ↓ AST ↓ Bytecode ↓ PVM Execution işlemlerini gerçekleştirir. Basitleştirilmiş görünüm: CPython │ ├── Lexer ├── Parser ├── Compiler ├── Bytecode Generator ├── PVM ├── Garbage Collector ├── Memory Manager └── C API Python doğrudan makine koduna çevrilmez. Java'da olduğu gibi ara bir temsil oluşturulur. Buna: Bytecode denir. Kodumuz: x = 10 y = 20 z = x + y olsun. Python bunu aşağıdakine benzer bytecode'a dönüştürür: LOAD CONST 10 STORE NAME x LOAD CONST 20 STORE NAME y LOAD NAME x LOAD NAME y BINARY ADD STORE NAME z Python'un şu kodu: x + y işlemciye doğrudan gönderilemez. Çünkü CPU şunu anlamaz: x + y Bu nedenle Python önce bunu standart komutlara dönüştürür. Python'da: python import dis def test : x = 10 y = 20 return x + y dis.dis test çıktı: LOAD CONST 10 STORE FAST x LOAD CONST 20 STORE FAST y LOAD FAST x LOAD FAST y BINARY OP + RETURN VALUE şeklinde olur. Python bytecode'u diske de yazabilir. Örneğin: main.py çalıştırıldığında: pycache oluşur. İçinde: main.cpython-313.pyc benzeri dosyalar vardır. Bu dosya: Bytecode saklar. Tekrar derleme maliyetini azaltmak için. İkinci çalıştırmada: .py yerine .pyc kullanılabilir. Bu da açılış süresini azaltır. Hayır. En çok karıştırılan konu budur. Bytecode: LOAD FAST STORE FAST BINARY ADD gibi komutlardan oluşur. CPU bunları anlayamaz. Bu komutları çalıştıracak başka bir sistem gerekir. İşte burada PVM devreye girer. PVM: Python Bytecode'unu çalıştıran sanal işlemcidir. Gerçek CPU: MOV ADD SUB MUL gibi komutlar çalıştırır. PVM ise: LOAD FAST STORE FAST CALL FUNCTION BINARY ADD çalıştırır. Bytecode: LOAD FAST x LOAD FAST y BINARY ADD üretmiştir. PVM bunu okur. LOAD FAST x çalışır. Stack: 10 LOAD FAST y çalışır. Stack: 20 10 BINARY ADD çalışır. Stack: 30 Bu nedenle PVM'e bazen: Stack Based Virtual Machine denir. Örnek: 3 + 5 Bytecode: LOAD CONST 3 LOAD CONST 5 BINARY ADD PVM: PUSH 3 PUSH 5 ADD mantığıyla çalışır. Aslında PVM ayrı bir program değildir. CPython'ın içindedir. Şöyle düşünün: CPython │ ├── Compiler ├── Bytecode └── PVM Yani: PVM ⊂ CPython Örneğin: x = 10 gördüğümüzde: "10" doğrudan RAM'e yazılmaz. CPython şu yapıyı oluşturur: PyLongObject Basitleştirilmiş hali: struct { reference count; type; value; } Yani: 10 aslında bir nesnedir. PVM: BINARY ADD gördüğünde: CPython içindeki C fonksiyonlarını çağırır. Örneğin: PyNumber Add Bu fonksiyon: 10 + 20 mi? "abc" + "def" mi? list1 + list2 mi? kontrol eder. Sonra uygun işlemi gerçekleştirir. CPython içinde. Örneğin: x = 1,2,3 oluşturuldu. Daha sonra: x = None oldu. Referans kalmadığında: Reference Counting ve Garbage Collector mekanizmaları belleği temizler. Python'un C veya Rust'tan yavaş olmasının temel nedeni budur. Çünkü: x + y ifadesi: gibi birçok katmandan geçer. C dilinde ise: int z = x + y; doğrudan makine koduna çevrilir. Bütün sistemi tek diyagramda özetlersek: Python Source Code │ ▼ CPython Parser │ ▼ AST │ ▼ Compiler │ ▼ Bytecode │ ▼ Python Virtual Machine │ ▼ CPython Runtime C │ ▼ Operating System │ ▼ CPU │ ▼ Machine Code Özetle: Bu üç yapıyı anladığınızda Python artık yalnızca bir programlama dili değil; derleyici teorisi, sanal makineler, işletim sistemleri ve bilgisayar mimarisinin kesişim noktasında çalışan çok katmanlı bir yürütme platformu olarak görünmeye başlar.