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:
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.