CPython Walkthrough
.knuth baba
'Şu sektörde olmamızın bir getirisi olaraktan yaşamımızın bir parçası haline gelen yazılımların oluşturulma, geliştirilme sürecinde yatan ayrıntılar, uygulanan method ve uygulama içerisinde izlerine rastlanan tekniklerin keşfi; kişinin bu vazgeçilmesi anlamasında önemli bir yere sahiptir.
Kullandığımız bu yazılımlar, yazarı tarafından birtakım işlemleri gerçekleştirmek maksadıyla oluşturulmuşlardır.
Bu işlemlerin aslen ne olduğunu anlamaksızın üstünkörü kullanmak, içerisinde fizik yasalarına aykırı gizemli olaylar döndüğüne kanaat getirmek kişiye bir şey katmaz, sadece başta da belirttiğim üzere "yaşamının bir parçası" olur.
Onsuz yapamaz, onsuz hareket edemez ve ona bağlanır. Makul olan, arka planında dönen dolapları gün yüzüne çıkarıp kendini bu bağımlılıktan alıkoymaktır.
Ne yazık ki sadece kullanıyor ve onları vazgeçilmez kılmaya diretiyoruz, kavramaya çalışmıyoruz, anlamak için çaba sarf etmiyoruz.'
En son sürümünü git ile temin etmek için;
Kod:
git clone https://github.com/python/cpython
CPython compiler'i saf C kodlarından oluşmaktadır. Standart kütüphaneler ise saf Python veyahut Python-C kombinasyonu ile yazılmıştır. Peki niye Python ile değil de C ile yazılmıştır? Bunun için compiler'in çalışma mantığına bakmamız lazım. Self-hosted compilers - derledikleri dilde yazılmış derleyicilerdir (z.B Go Compiler). Source-to-Source compilers halihazırda derleyicileri bulunan başka dillerde yazılmış derleyicilerdir. Eğer yeni bir dil yazıyorsanız, dilin compiler'ini derlemek için executable bi' app'e ihtiyacınız vardır. Executable bi' app için de compiler'a, bu compiler için eski ve yerleşik diller tercih edilir.
Misal olarak Go compiler'i ilk olarak C ile yazılıp, derlendikten sonra Go ile tekrardan yazılmıştır.
CPython'un ssl modülü veya sockets modülü gibi standart kütüphane modüllerinin çoğu, düşük seviyeli işletim sistemi API'larına erişmek için C dilinde yazılmıştır. Network sockets oluşturmak, dosya sistemiyle çalışmak veya ekranla etkileşimde bulunmak için Windows ve Linux çekirdeklerindeki API'lerin tümü C ile yazılmıştır. Python'un genişletilebilirlik katmanının C diline odaklanması mantıklıdır. Bu makalenin ilerleyen bölümlerinde Python Standart Kütüphanesi ve C modüllerini ele alacağız. Python'da PyPy adında bir Python derleyicisi yazılmıştır. PyPynin logosu, derleyicinin self-hosting doğasını temsil eden bir Ouroboros'tur.
Python'un cross-compiler'ina başka bir örnek vermek daha gerekirse Jython olabilir. Jython Java ile yazılmış ve Python kodlarını Java Byte Code'una derler. CPython C kütüphanelerini Python'a import etmeyi ve kullanmayı kolaylaştırırdığı gibi Jython'da modüllerin ve classların referans alımını ve import edilmesini kolaylaştırır.
CPython kaynak kodu içinde Python dilinin tanımı yer almaktadır. Bu, tüm Python interpreterleri tarafından kullanılan referans spesifikasyonudur. Spesifikasyon hem human-readable hem de machine-readable formattadır. Belgelerin içinde, Python dilinin, izin verilenlerin ve her ifadenin nasıl davranması gerektiği hakkında ayrıntılı bir açıklama bulunmaktadır.
-docs
Doc/reference dizininin içinde, Python dilindeki özelliklerin her birinin reStructuredText açıklamaları bulunur. = Docs Python
doc/reference ;
Dili, structureları, keywordler;
Kod:
compound_stmts.rst
datamodel.rst
executionmodel.rst
expressions.rst
grammar.rst
import.rst
index.rst
introduction.rst
lexical_analysis.rst
simple_stmts.rst
toplevel_components.rst
ör: compound_stmts.rst
Dokümantasyon compound statements ve with tanımlanması. En basit hali;
Kod:
with x():
as ile sonucu bir değişkene atayabiliriz;
Kod:
with x [COLOR="White"]as [/COLOR]y:
birden fazla;
Kod:
with x() as y[COLOR="white"],[/COLOR] z() as jk:
-grammar
Dokümantasyon dilin human-readable özelliklerini içerir ve machine-readable özellikleri Grammar/Grammar adlı tek bir dosyada bulunur. Grammar dosyası Backus-Naur Form(BNF) adı verilen bir context-notation ile yazılmıştır. BNF Python'a özgü değildir ve çoğu zaman diğer dillerde dilbilgisi gösterimi olarak kullanılır. Bir programlama dilinde grammatical structure kavramı Noam Chomsky'nin 1950'lerde Syntactic Structures üzerine yaptığı çalışmalardan esinlenmiştir! Pythonun grammar dosyası, regular-expression sözdizimiyle Extended-BNF (EBNF) belirtimini kullanır. Yani, grammardosyasında şunları kullanabilirsiniz;
Kod:
[COLOR="White"]*[/COLOR] tekrar için
[COLOR="white"]+[/COLOR] en az bir kere tekrar için
[COLOR="white"][][/COLOR] optional partlar için
[COLOR="white"]|[/COLOR] alternatifler için
[COLOR="white"]()[/COLOR] gruplama için
Dosya içinde with statement ile ilgili bir şeyler aradığınızda, 80'nci satırlarda with statement'i için tanımlamaları göreceksiniz:
Kod:
with_stmt: 'with' with_item (',' with_item)* ':' suite
with_item: test ['as' expr]
Quote içindeki literal stringler keyword olarak tanımlanmıştır. with_stmt şöyle belirtilmiştir.
with_stmt:
- with ile başlayacak
- Ardından with_item, örnekte 'test'
- İsteğe bağlı olarak as kullanılabilir
- expression
- Fazla itemlerde virgül ile ayırma
- ' : ' ile bitiyor ve ardından suite.
test: değerlendirilen basit bir ifadeyi ifade eder
suite: bir veya daha fazla ifadeye sahip bir kod bloğunu belirtir
expr: expression'u ifade eder
Grammar'ın nasıl kullanıldığına dair PEP 572'de 'colon equals', ':' operatörü, grammar dosyasına buradan ulaşabilirsiniz.
-pgen
Grammar dosyasının kendisi asla Python derleyicisi tarafından kullanılmaz. Bunun yerine, pgen adlı bir araç tarafından oluşturulan bir parser tablosu kullanılır. pgen grammar dosyasını okur ve bir parser tablosuna dönüştürür. Grammar dosyasında değişiklik yaparsanız, parser tablosunu yeniden oluşturmanız ve Python'u recompile etmeniz gerekir.
Kod:
[COLOR="white"]Not:[/COLOR] Pgen uygulaması Python 3.8'de C'den pure Python ile yeniden yazılmıştır. [URL="https://github.com/python/cpython/blob/master/Parser/pgen/pgen.py"][COLOR="white"]link[/COLOR][/URL]
Kod:
[COLOR="White"]Not2:[/COLOR] Not: pgen, EBNF statementlerini [URL="https://en.wikipedia.org/wiki/Nondeterministic_finite_automaton"][COLOR="White"]Non-deterministic Finite Automaton (NFA)[/COLOR][/URL] dönüştürerek çalışır ve daha sonra bir[URL="https://en.wikipedia.org/wiki/Deterministic_finite_automaton"] [COLOR="white"]Deterministic Finite Automaton (DFA) [/COLOR][/URL]dönüştürülür.
DFA'lar parser tarafından tabloları parselemek için CPython'a özgü özel bir teknik kullanılır. Bu teknik [URL="http://infolab.stanford.edu/~ullman/dragon/slides1.pdf"][COLOR="white"]Stanford Üniversitesi'nde[/COLOR][/URL] oluşturuldu ve 1980'lerde Python'un ortaya çıkmasından hemen önce geliştirildi.
-tokens
Tokens dosyası Python 3.8' ile gelmiştir. Örnek olarak sol parantez için LPAR virgül için SEMI kullanılıyor. Tokens;
Kod:
LPAR '('
RPAR ')'
LSQB '['
RSQB ']'
COLON ':'
COMMA ','
SEMI ';'
Tokens dosyasında her hangi bir değişiklik yaparsanız pgen'i yeniden çalıştırmanız gerekir. tokens'i çalışırken görmek için CPython'da tokenize modülünü kullanabilirsiniz. test_tokens.py adlı basit bir Python scripti oluşturun:
Kod:
# Hello Valentina!
def my_function():
proceed
Kod:
python.exe -m tokenize -e test_tokens.py
Çıktıda, ilk column çizgi/column koordinatlarının aralığı, ikinci column tokenin adı ve son column tokenin değeridir. Çıktımızda tokenize modülümüz bize dosyamızda olmayan bazı şeyleri gösteriyor. ENCODING tokeni - utf-8, function declaration'unu kapatmak için DEDENT , dosya sonu için ENDMARKER.
Python kaynak dosyalarınızın sonunda boş bir satır olması en iyi yöntemdir. Bunu atlarsanız, CPython sizin için küçük bir performans cezasıyla birlikte ekler.
Tokenize modülü saf Python ile yazılmıştır ve CPython kaynak kodu içinde Lib/tokenize.py içinde bulunmaktadır.
Kod:
[COLOR="Red"]Önemli Hatırlatma:[/COLOR]CPython kaynak kodunda iki tokenizer vardır: biri Python'da yazılmış, ve şu an anlattığım ve diğeri C'de yazılan.
Python'da yazılmış tokenizer bir utility'dir ve C'de yazılmış olan Python compiler'i tarafından kullanılır.
Aynı output ve davranışları vardır. C ile yazılan [COLOR="white"]performans[/COLOR] için ve Python'daki [COLOR="White"]debugging [/COLOR]için tasarlanmıştır.
C tokenizerinin ayrıntılı görmek için, daha önce oluşturduğumuz test_tokens.py scriptimizi -d flagiyle Python'da çalıştıralım.
Kod:
python.exe -d test_tokens.py
Çıktıda, vurgulandığı gibi proceed keyword olarak ilerlediğini görebilirsiniz.
-bellek yönetimi
PyArena, CPythonun memory management structurelarından biridir. Kod bloğu Python/pyarena.c içerisindedir ve Cnin memory allo-cation ve deallo-cation işlevleri için bir paket içerir. Geleneksel olarak yazılmış bir C programında, geliştirici bu verilerle işlem yapmadan önce data structure için bellek ayırmalıdır 'allo-cation'. Bu ayırma işlemi, belleği işletim sistemindeki memory'e ait olarak işaretler. Bir process, örneğin bir function veya loop içinde bir değişken için bellek ayırırsa, bu function tamamlandığında bellek otomatik olarak C'deki işletim sistemine geri verilmez. Bu nedenle, açıkça C koduna yerleştirilmemişse, memory leak'e neden olur. Python bu sorumluluğu programcıdan uzaklaştırır ve iki algoritma kullanır: reference counter ve garbage collector.
Bir interpreter başlatıldığında, bir PyArena oluşturulur ve interpreterda ki alanlardan birini iliştirir. Bir CPython interpreterinin yaşam döngüsü boyunca birçok arena allocate edilebilir. linked list ile bağlıdır. Arena, Python objectlerine bir pointer listesini PyListObject olarak saklar. Yeni bir Python objecti her oluşturulduğunda, PyArena_AddPyObject() kullanılarak bir pointer eklenir. Bu function call, arenada a_objects listesinde pointer olarak saklar.
Python da Pointerlar olmamasına rağmen, Pointerların davranışlarını simüle etmek için bazı teknikler vardır.
PyArena, raw memory bloklarının bir listesini allocate etmek ve referans vermek için kullanılan second function'a hizmet eder. Örneğin, binlerce ek değer eklediyseniz bir PyList'in ek memory'eihtiyacı olacaktır. PyList nesnesinin C kodu memory'i doğrudan ayırmaz. Object, gerekli memory boyutuyla PyObject'ten PyArena_Malloc() öğesini çağırarak PyArena'dan raw memory blokları alır. Bu görev, Objects/obmalloc.c dosyasındaki başka bir abstraction ile tamamlanır. Object allo-cation modülünde, bir Python objecti için memory allocate edilebilir, freed ve reallocated edilebilir.
Allocate edilmiş blokların linked list'i arenada saklanır, böylece bir interpreter durdurulduğunda, yönetilen tüm memory blokları PyArena_Free() kullanılarak tek seferde deallocated edilebilir PyListObject örneğini bakalım. Bir objecti Python listesine .append() ile ekleyecekseniz, önceden varolan listede kullanılan memoryi reallocate gerekmez. .Append() methodu, listeler için memory allo-cation işlemine list_resize() methodunu çağırır. Her liste nesnesi, allocated edilen bellek miktarının bir listesini tutar. Eklediğiniz öğe mevcut boş memory'e sığacaksa, basitçe eklenir. Listenin daha fazla memory alanına ihtiyacı varsa, genişletilir. Listelerin uzunluğu [0, 4, 8, 16, 25, 35, 46, 58, 72, 88] olarak genişletilir.
Listeye ayrılan memory'i genişletmek için PyMem_Realloc() yöntemi çağrılır. PyMem_Realloc() pymalloc_realloc() için bir API wrapper'dır.
Python ayrıca buffer overflow hatalarını önlemeye yardımcı olmak için memory allo-cation'in maksimum boyutunu ayarlayan C çağrısı malloc() için özel bir wrapper'a sahiptir (Bkz. PyMem_RawMalloc()).
Özetle;
Raw memory bloklarının allo-cation işlemi PyMem_RawAlloc() ile yapılır.
Python objectlerinde pointerlar PyArena içinde saklanır.
PyArena ayrıca allocated memory bloklarının linked-list'ini de saklar.
API hakkında daha fazla bilgi CPython belgelerinde ayrıntılı olarak verilmiştir.
-reference counting
Python'da bir değişken oluşturmak için, uniquely olarak adlandırılmış bir değişkene bir değer atamanız gerekir:
Kod:
my_variable = 1903
Python'daki bir değişkene her değer atandığında, değişkenin adının zaten var olup olmadığını görmek için local ve global kapsamında kontrol edilir. my_variable zaten locals() veya globals() dict'iiçinde olmadığından, bu yeni object oluşturulur ve değer 180392 numeric constant olarak atanır. Artık my_variable'a bir referans var, bu nedenle my_variable için referans counting 1 artırıldı.
CPython için C kaynak kodu boyunca Py_INCREF() ve Py_DECREF() function çağrılarını göreceksiniz. Bu functionlar, o objecte yapılan referansların sayısını arttırırve azaltır. Bir değişkene declare edildiği kapsamın dışına çıktığında, bir objecte yapılan referanslar azaltılır. Python'daki kapsam bir function veya methoda, comprehension veya lambda functionuna atıfta bulunabilir. Bunlar daha literal kapsamların bazılarıdır, ancak değişkenleri bir function call'a geçirmek gibi başka implicit kapsamlar da vardır.
Dile dayalı artan ve azalan referansların işlenmesi, CPython compiler'ına ve bu makalenin ilerleyen kısımlarında ayrıntılı olarak ele alacağımız execution loop 'ceval.c' içine yerleştirilmiştir. Py_DECREF() her çağrıldığında ve counter 0 olduğunda, PyObject_Free() function'u çağrılır. Bu object için allo-cated edilen tüm memoryiçin PyArena_Free() çağrılır.
-garbage collection
CPython C ile aynı prensibe sahiptir ve Garbage Collection algoritmasını kullanır. CPythonun garbage collector'ü default olarak enabled'dir, arka planda gerçekleşir ve artık kullanılmayan objectler için kullanılan memory'i deallo-cated etmeye çalışır.
Garbage collection algoritması referans counting algoritmasından çok daha karmaşık olduğundan her zaman gerçekleşmez, aksi takdirde çok miktarda CPU tüketirdi. Belirli sayıda işlemden sonra periyodik olarak çalışır. CPythonun standart kütüphanesi, arena ve garbage collector, gc modülü ile interface oluşturmak için bir Python modülü ile birlikte gelir. Gc modülünü debug mode'da nasıl kullanacağız?:
Kod:
import gc
gc.set_debug(gc.DEBUG_STATS)
Gc çalıştırıldığında ki istatistiği yazdırır.
get_threshold() yöntemini çağırarak garbage collector'ün çalıştırılma threshold'unu(eşiğini) alabilirsiniz:
Kod:
gc.get_threshold()
(700, 10, 10)
Mevcut threshold sayıları:
Kod:
gc.get_count()
(688, 1, 1)
Son olarak kendimiz de manual olarak gc'yi başlatabiliriz:
Kod:
gc.collect()
Bu, garbage collector algoritmasının implementation'u içeren Modules/gcmodule.c dosyasının içindeki collect() öğesini çağıracaktır.
Part part CPython'u işleyeceğiz, will be continued..
Son düzenleme:



