diff --git a/EmbeddedLinux-OzetNotlar-Ornekler.txt b/EmbeddedLinux-OzetNotlar-Ornekler.txt index f015835..8fc120a 100644 --- a/EmbeddedLinux-OzetNotlar-Ornekler.txt +++ b/EmbeddedLinux-OzetNotlar-Ornekler.txt @@ -13,7 +13,7 @@ (Notları sabit genişlikli font kullanan programlama editörleri ile açınız.) (Editörünüzün "Line Wrapping" özelliğini pasif hale getiriniz.) - Son Güncelleme: 07/01/2025 - Salı + Son Güncelleme: 25/02/2025 - Salı -----------------------------------------------------------------------------------------------------------------------------*/ @@ -12165,10 +12165,19 @@ void exit_sys(const char *msg) $ sudo ip addr add 192.168.7.2/24 dev usb0 $ sudo ip link set usb0 up - 3) İnternete çıkış yapabilmesi için varsayılan gateway olarak host makine aşağıdaki givi ayarlanabilir: + 3) İnternete çıkış yapabilmesi için varsayılan gateway olarak host makine aşağıdaki gibi ayarlanabilir: $ sudo ip route add default via 192.168.7.1 + Bu işlemden sonra "ip route show" komutunu uyguladığınızda aşağıdaki gibi bir satır görmeniz gerekir: + + default via 192.168.7.1 dev usb0 + + Eğer default gateway doğru atanmamışsa ve "ip route add default" komutundan "ip: RTNETLINK answers: File exists" + biçiminde bir hata mesajı alırsanız yanlış gateway adresini aşağıdaki gibi düzeltebilirsiniz: + + $ sudo ip route replace default via 192.168.7.1 dev usb0 + Burada artık BBB'de host makinenın IP adresi 192.168.7.1 biçiminde BBB'deki USB arayüzünün IP adresi ise 192.168.7.2 biçiminde ayarlanmıştır. Yukarıdaki iki maddenin otomatik yapılması için "/etc/network/interfaces" dosyası aşağıdaki gibi de düzenlenebilir: @@ -12249,9 +12258,9 @@ void exit_sys(const char *msg) -----------------------------------------------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------------------------------------------- - Şimdi de yukarıda açıkladığımız host ve BBB'de yapılanlar otomatize edilebilir. BBB'deki sistemde "systemd" kullanılıyorsa - (örneğin biz "debootstrap" ile Debian sistemi kurmuşsak) otomatize etmek için bir "servis birim (service unit)" dosyası - oluşturabiliriz. Bu dosya şöyle olabilir: + Şimdi de BBB'de yapılması gerekenleri optomatize edelim. BBB'deki sistemde "systemd" kullanılıyorsa (örneğin biz "debootstrap" + ile Debian sistemi kurmuşsak) otomatize etmek için bir "servis birim (service unit)" dosyası oluşturabiliriz. Bu dosya şöyle + olabilir: [Unit] Description=USB connection @@ -12273,7 +12282,7 @@ void exit_sys(const char *msg) -----------------------------------------------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------------------------------------------- - Şimdi de Internet'i BusyBox ile oluşturduğumuz minimak kök dosya sistemindeki Linux'a bağlamaya çalışalım. Ancak öncelikle + Şimdi de Internet'i BusyBox ile oluşturduğumuz minimal kök dosya sistemindeki Linux'a bağlamaya çalışalım. Ancak öncelikle BusyBox'ta oluşturduğunuz kök dosya sisteminin ağ komutlarını içerip içermediğinden emin olmalısınız. Eğer bu kök dosya sisteminde bu komutlar yoksa kök dosya sistemi bu komutları içerecek biçimde yeniden derlenmelidir. Bu seçenekler BusyBox'ın menuconfig menüsünde "Network Utilities" girişinde bulunmaktadır. @@ -12331,49 +12340,314 @@ void exit_sys(const char *msg) Bu giriş eklendiğinde artık açılış sırasında rcS script'inin yanı sıra "network" script'i de çalıştırılacaktır. Bu dosyaların "x" hakkına sahip olması gerektiğini anımsayınız. Yukarıdaki scrip içeriğini "network" isimli sctipt dosyasına yazabiliriz: +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + 82. Ders 20/02/2025 - Perşembe +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Bu bölümde geçici olarak Gömülü Linux sistemlerinin organize edilmesine yönelik konulardan uzaklaşıp kernel mod aygıt + sürücülerin yazılması ve kullanılmaıs üzerinde duracağız. Sonra yeniden Linux sistemlerinin organize edilmesine ilişkin + "buildroot" ve "yocto" gibi araçları inceleyeceğiz. +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Giriş derslerinden de anımsayacağınız gibi modern Linux sistemleri işlemcilerin "korumalı mod" özelliklerini kullanıyordu. + Bu sistemlerde çalışan programların (yani proseslerin) "kullanıcı modu (user mode)" ya da "çekirdek modu (kernel mode)" biçiminde + çalışma modları vardır. Normak bütün programlar kullanıcı modunda (user mode'da) çalışmaktadır. Ancak çekirdek kodları + "çekirdek modunda (kernel mode)" çalışır. Kullanıcı modunda koruma mekanizması aktiftir. Dolayısıyla kullanıcı modunda çalışan + programlar bellekte her yere erişemezler ve her maikne komutunu kullanamazlar. Halbuki çekirdek kodları koruma mekanizmasına + takılmamaktadır. Yani çekirdek kodları bellekte her yere erişebilmeke ve her makine komutunu kullanabilmektedir. Daha önceden + de gördüğümüz gibi kullanıcı modundaki sıradan programlar işletim sisteminin içerisindeki ismine "sistem fonksiyonları ya da + sistem çağrıları" denilen özel fonksiyonları çağırabilmektedir. Kullanıcı modundaki programlar bir sistem fonksiyonunu çağırdığında + programın akışı sistem fonksiyonu çağrıldığı anda çekirdek moduna geçer, sistem fonksiyonu çekirdek modunda çalıştırılır, + sonra sistem fonksiyonu bittiğinde programın akışı yenidne kullanıcı modune döner. Bunu şekilsel olarak şöyle gösterebilriz: + + kullanıcı modunda çalışan sıradan program -----> sistem fonksiyonunu çağırıyor -----> programın akışı kullanıcı modunundan + çekirdek moduna geçiliyor -----> ilgili sistem fonksiyonun kodları çalıştırılıyor -----> sistem fonksiyonun çalışması bitiyor + -----> programın akışı çekirdek modundan yeniden kullanıcı moduna geçiyor. + + Kullanıcı modunda çalışan sıradan programlar sistem fonksiyonlarını genellikle doğrudan değil POSIX kütüphanesindeki POSIX + fonskiyonları yoluyla dolaylı bir biçimde çağırmaktadır. Yani programcı bir POSIX fonksiyonunu çağırdığında o POSIX fonksiyonu + da bir sistem fonksiyonunu çağırabilmektedir. Tabii her POSIX fonksiyonun bir sistem fonksiyonunu çağırdığını söylemiyoruz. + Önceki konularda çekirdeğe yeni sistem fonksiyonu eklerken de gördüğümüz gibi programcı isterse syscall isimli "libc" kütüphane + fonksiyonu ile numarasını belirterek de sistem fonksiyonlarını doğrudan çağırabilir. Tabii Linux'ta sistem fonksiyonlarının + çağrılmasının belli bir yöntemi vardır. Bunun için makine dilinde bazı komutların kullanılması gerekmektedir. syscall fonksiyonu + bunu bizim için yapmaktadır. + + Linux çekirdeğinin içerisinde sistem fonksiyonlarının dışında binlerce fonksiyon vardır. Kullanıcı modunda çalışan normal + programlar yalnızca sistem fonksiyonlarını çağırabilmektedir. + + Biz bir programı etkin kullanıcı id'si 0 olacak biçimde "sudo" ile çalıştırdığımızda program yine kullanıcı modunda çalışmaktadır. + Bu "sudo" mekanizmasının bu konuyla bir ilgisi yoktur. Linux sistemlerinde kullanıcı id'leri bir kulalnıcının bir programının + başka bir kulalnıcının bir dosyasına erişip erişememesi konusunda etki gstermektedir. +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + İşte çekirdeğin bir parçası biçiminde işlev gören çekirdek modunda çalışan özel programlara "çekirdek modülleri (kernel modules) + ve "aygıt sürücüler (device drivers)" denilmektedir. Çekirdek modülleri ya da aygıt sürücülerin en önemli özelliği özelliği + bunların bellek ve komut koruma mekanizmasına takılmamalarıdır. Böylece biz kullanıcı modunda yapamayacağımız birtakım işlemleri + çekirdek modülleri ve aygıt sürücüler oluşturarak yapabilmekteyiz. Aslında aygıt sürücülerin çekirdek modu yerine kullanıcı + modunda çalıştığı işletim sistemleri de vardır. Bu tür çekirdek mimarilerine genel olarak "mikro çekirdek (micro kernel)" + mimarileri denilmektedir. Ancak Linux ve Windows sistemleri büyük ölçüde "monolitik" bir yapıdadır. Fakat bu işletim sistemlerine + de yavaş yavaş "kullanıcı modunda çalışan aygıt sürücüler" sokulmumaktadır. + + Linux sistemlerinde "çekirdek modülü (kernel module)" ve "aygıt sürücü (device driver)" kavramları arasında bir fark vardır. + Çekirdeğe yüklenen bütün kodlara "çekirdek modülü" denilmektedir. Eğer bir çekirdek modülü kulalnıcı modu ile ilişki kuruyorsa + ya da birtakım kesme gibi olaylara yanıt verecek biçimde yazılmışsa bunlara aygıt sürücü denilmektedir. Yani her aygıt sürücü + bir çekirdek modülüdür ancak her çekirdek modülü bir aygıt sürücü değildir. +/*----------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------------------------- - ------------------------------------------------------------------------------------------------------------------------------*/ + Çekirdek modüllerinde ve aygıt sürücülerde her türlü fonksiyon kullanılamaz. Bunları yazabilmek için özel başlık dosyalarına + ve ve amaç dosyalara gereksinim duyulmaktadır. Bu nedenle ilk yapılacak şey bu başlık dosyalarının ve kütüphanelerin ilgili + sisteme yüklenmesidir. + Genellikle bir Linux sistemini yüklediğimizde zaten çekirdek modüllerini ve aygıt sürücüleri oluşturabilmek için gereken + başlık dosyaları ve diğer gerekli öğeler zaten "/usr/src" dizini içerisindeki "linux-headers-$(uname -r)" dizininde yüklü + biçimde bulunmaktadır. Ancak bunlar yüklü değilse Debian tabanlı sistemlerde bunları şöyle yükleyebilirsiniz: + + $ sudo apt install linux-headers-$(uname -r) + + Tabii programcı o anda çalışılan çekirdeğin kodlarının hepsini de kendi makinesine indirmek isteyebilir. Bunun için aşağıdaki + komut kullanılabilir: + + $ sudo apt-get install linux-source + + Bu indirmeler "/usr/src" dizinine yapılmaktadır. + + Ayrıca "/lib/modules/$(uname -r)" isimli dizindeki "build" isimli dizin de çekirdek kaynak kodlarının bulunduğu dizine ya + da aygıt sürücülerin derlenmesi için gereken öğelerin bulunduğu dizine (tipik olarak "linux-headers-$(uname -r)" dizinine) + sembolik link yapılmış durumdadır. + + Biz daha önce çekirdeği derlerken "make modules_install" yaptığımızda çekirdek modülelrinin install edildiği yerde "build" + isimli biri dizinin de oluşturulduğunu görmüştük. Bu "build" dizini aslında çekirdek kaynak kodlarına bir sembolik link + oluşturmaktaydı. Yani "make modules_install" yapıldığında aslında aynı zamanda çekirdek modüllerinin ve aygıt sürücülerin + derlenmesi ve link edilmesi için gerekli olan dosyalar da hedefe çekilmiş olmaktadır. -----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Bir çekirdek modülünde biz user mod için yazılmış kodları kullanamayız. Çünkü orası ayrı bir dünyadır. Ayrıca biz çekirdek + modüllerinde çekirdek içerisindeki her fonksiyonu da kullanamayız. Yalnızca bazı fonksiyonları kullanabiliriz. Bunlara + "çekirdek tarafından export edilmiş fonksiyonlar" denilmektedir. "Çekirdek tarafından export edilmiş fonksiyon" kavramıyla + "sistem fonksiyonu" kavramının bir ilgisi yoktur. Sistem fonksiyonları kullanıcı modundan (user mode) çağrılmak üzere tasarlanmış + ayrı bir grup fonksiyondur. Oysa çekirdek tarafından export edilmiş fonksiyonlar kullanıcı modundan çağrılamazlar. Yalnızca + çekirdek modüllerinden çağrılabilirler. Buradan çıkan sonuç şudur: Bir çekirdek modülü yazılırken ancak çekirdeğim export + ettiği fonksiyonlar ve nesneler kullanılabilmektedir. Tabii çekirdeğin kaynak kodları çok büyüktür ancak buradaki kısıtlı + sayıda fonksiyon export edilmiştir. Benzer biçimde programcının oluşturduğu bir çekirdek modül içerisindeki belli fonksiyonları + da programcı export edebilir. Bu durumda bu fonksiyonlar da başka çekirdek modüllerinden kullanılabilirler. O halde özetle: + + 1) Çekirdek modülleri yalnızca çekirdek içerisindeki export edilmiş fonksiyonları kullanabilir. + 2) Kendi çekirdek modülümüzde biz de istediğimiz fonksiyonu export edebiliriz. Bu durumda bizim çekirdek modülümüz çekirdeğin + bir parçası haline geldiğine göre başka çekirdek modülleri de bizim export ettiğimiz bu fonksiyonları kullanabilir. +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Mademki çekirdek modülleri işletim sisteminin çekirdek kodlarındaki fonksiyonları ve nesneleri kullanabiliyor o zaman çekirdek + modülleri o anda çalışılan çekirdeğin yapısına da bağlı durumdadır. Bu nedenle işletim sistemlerinde "çekirdek modülü yazmak" + ya da "aygıt sürücü yazmak" biçiminde genel bir konu yoktur. Her işletim sisteminin çekirdek modül ve aygıt sürücü mimarisi + diğerlerinden farklıdır. Dolayısıyla çekirdek modüllerinin ve aygıt sürücülerinin yazılması spesifik bir işletim sistemi için + geçerli olabilecek platform oldukça bağımlı bir konudur. Hatta işletim sistemlerinde bazı versiyonlarda genel aygıt sürücü + mimarisi bile değiştirilebilmektedir. Dolayısıyla bu tür durumlarda eski aygıt sürücüler yeni versiyonlarda, yenileri de eski + versiyonlarda çalışamamaktadır. Örneğin Linux'ta çekirdek versiyonları arasında çekirdekteki export edilmiş bazı fonksiyonlar + isim ya da parametrik yapı olarak değiştirilmiş durumdadır. Bu nedenle Linux çekirdeğinin belli bir versiyonu için yazılmış + olan aygıt sürücüler başka bir versiyonunda geçersiz hale gelebilmektedir. +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Çekirdek modüllerinin ve aygıt sürücülerin yazımı için programcının çekirdek yapısını ana hatlarıyla bilmesi gerekmektedir. + Çünkü bunları yazarken çekirdeğin içerisindeki çeşitli veri yapıları ve export edilmiş fonksiyonlar kullanılmaktadır. + + Linux çekirdek modülleri ve aygıt sürücüleri hakkında yazılmış birkaç kitap vardır. Bunların en klasik olanı "Linux Device + Drivers (3. Edition)" kitabıdır. Ancak bu kitaptaki bazı içerikler güncel çekirdeklerle uyumsuz hale gelmiştir. Bu konudaki + resmi dokümanlar ise "kernel.org" sitesindeki "documentation" kısmında bulunmaktadır. +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Bir çekirdek modülünü derlemek ve link etmek maalesef sanıldığından daha zordur. Her ne kadar çekirdek modülleri ELF object + dosyaları biçimindeyse de bunlarda özel bazı "bölümler (sections)" bulunmaktadır. Dolayısıyla bu modüllerin derlenmesinde + özel gcc seçenekleri devreye sokulmaktadır. Çekirdek modüllerinin link edilmeleri de bazı kütüphane dosyalarının devreye + sokulmasıyla yapılmaktadır. Dolayısıyla bir çekirdek modülünün manuel biçimde "build edilmesi" için bazı ayrıntılı bilgilere + gereksinim duyulmaktadır. İşte çekirdek modüllerinin build edilmesinde çekirdeğin KBuild sistemi devreye sokulmaktadır. Bu + nedenle çekirdek modüllerinin build edilmesi için çekirdek kaynak kodlarındaki birtakım başlık dosyalarının ve Make dosyalarının + build işleminin yapılacağı makinede bulunması gerekir. Biz yukarıda bu dosyalara "/lib/modules/$(uname -r)/build" dizini + yoluyla erişilebileceğini belirtmiştik. Bu dizin aslında Linux kaynak kod ağacının bulunduğu dizini belirtmektedir. Ancak + yukarıda da belirttiğimiz gibi çekidek modüllerinin ve aygıt sürücülerin derlenmesi için Linux'ın tüm kaynak kodlarına + gerek yoktur. Yalnızca başlık dosyaları ve make dosyalarının bulunması yeterlidir. +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Çekirdek modülleri o anda çalışılan host sistem için derlenebileceği gibi gömülü bir sistem için de derlenebilir. Eğer derleme + gömülü sistem için yapılacaksa süphesiz çapraz derleyicilerin de ilgili sistemde kurulu olması gerekir. Yukarıda da belirttiğimiz + gibi çekirdek modüllerinin derlenmesi için ilgili çekirdeğe yönelik başlık dosyaları ve çeşitli make dosyaları gibi + bazı öğelerin de bulunuyor olması gerekir. Eğer derleme bir gömülü sistem için yapılacaksa o gömülü sistemdeki çekirdeğe + ilişkin bu dosyalar da host makinede bulunuyor olmalıdır. Örneğin biz masaüstü bilgisayardaki Mint dağıtımında çalışıyor + olalım. Bu sistemin kendisi için çekirdek modülü derleyeceksek zaten tüm gerkeli öğeler hazır bulunuyor olacaktır. Ancak biz + bu akinede BBB için çekirdek modülü ve aygıt sürücü derlemesi yapacaksak çapraz derleyicimizin ve BBB'deki çekirdeğe yönelik + temel başlık dosyalarının ve Make dosyalarının bulunuyor olamsı gerekecektir. Tabii BBB'deki çekirdek sürümüne kaynak kodları + bu makineye çekerek bu gereksinimi karşılayabiliriz. +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Masaüstü bir sistem için çekirdek modül derlemeinde kullanılabilecek minimal bir "Makefile" dosyası aşağıdaki gibi olabilir: + + obj-m += generic.o + + all: + make -C /lib/modules/$(shell uname -r)/build M=${PWD} modules + clean: + make -C /lib/modules/$(shell uname -r)/build M=${PWD} clean + + Burada önce "/lib/modules/$(uname -r)/build" dizinindeki "Makefile" dosyası çalıştırılmış ondan sonra çalışma bu yazdığımız + make dosyasından devam ettirilmiştir. Özetle bu make dosyası aslında çekirdeğin buil sistemini kullanarak "generic.c" isimli + dosyanın derlenmesini ve çekirdek modülü biçiminde link edilmesini sağlamaktadır. Tabii bu make dossyasını şöyle de düzenleyebiliriz: + + obj-m += generic.o + + KDIR := /lib/modules/$(shell uname -r)/build + PWD := $(shell pwd) + + all: + make -C ${KDIR} M=${PWD} modules + clean: + make -C ${KDIR} M=${PWD} clean + + Çekirdek modülü birden fazla kaynak dosya kullanılarak oluşturulabilir. Bu durumda ilk satır şöyle oluşturulabilir: + + obj-m += a.o b.o c.o... + + Eğer bu dosyaları birden fazla satırda ayrı ayrı belirtirsek bu durumda birden fazla modül dosyası oluşturulur: + + obj-m += a.o + obj-m += b.o + obj-m += c.o + ... + + Bizim oluşturduğumuz Makefile dosyasındaki "all" hedefine dikkat ediniz: + + $ make -C /lib/modules/$(shell uname -r)/build M=${PWD} modules + + make programının -C seçeneği Makefile dosyasını aramadan önce bu seçeneğin argümanında belirtilen dizine geçiş yapmaktadır. + Dolayısıyla aslında yukarıdaki satırla "/lib/modules/$(shell uname -r)/build" dizinindeki Makefile dosyası çalıştırılacaktır. + Buradaki M=${PWD} derlenecek kaynak dosyaların o anda çalışılan dizinde aranacağını belirtmektedir. Böyşece çekirdeğin KBuild + sistemi yalnızca bizim dosyalarımızı derleyecektir. +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + 83. Ders 25/02/2025 - Salı +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Çekirdek modüllerini va aygıt sürücüleri derlerken iki noktaya dikkat etmelisiniz: + + 1) Kullandığınız çekirdek kodları hedef mekinenin çekirdek sürümüne uygun olmalıdır. Eğer bu koşul sağlanmazsa çekirdekler + arasında farklılıklar söz konusu olabileceği için derlenmiş olan çekirdek modül dosyası hedef sisteme başarılı bir biçimde + yüklenemeyebilir. Tabii Linux çekirdeğindeki değişiklikler daha önce yazılmış olan her çekirdek modülünü ve aygıt sürücüyü + geçersiz hale getirmemektedir. Örneğin minör numara değişikliklerinde genellikle bir sorun oluşmamaktadır. Ancak ne olursa + olsun derleme yapılırken hedef sistemdeki çekirdeğe uygun kaynak dosyaların kullanılması şiddetle tavsiye edilmektedir. + Örneğin biz 6.9.2 çekirdeğinde çalışan makine için aygıt sürücüsü yazacaksak derleme yaptığımız makinede kullanacağımız + çekirdek kaynak kodlarının da bu 6.9.2 çekirdeğine ilişkin olması gerekir. Biz eski bir çekirdeğin kaynak kodlarıyla yeni + bir çekirdek için aygıt süsürücü derlemeye çalışmamalıyız. Tabii eski bir veriyon kullanılarak derleme yapılırsa çoğu + durumda bir sorun ortaya çıkmayabilecektir. Ancak sorunun ortaya çıkma olsılığı da vardır. + + 2) Kullanılan araç zincirinin de (yani derleyici, linker gibi programların da) çekirdeğin derlenmiş olduğu sistemle uyumlu + olmasına dikkat ediniz. Eğer bu temel araçların versiyonlarında geçmişe doğru uyumu bpzabilecek değişiklikler söz konusuysa + yine derleme işlemi başarısız olabilir ya da çekirdek modülü yüklenirken sorun oluşabilir. Aslında çekirdeğin KBuild sistemi + çekirdek konfigürasyon dosyası yoluyla bu kontrolü yapabilmektedir. Ancak bu kontrol bypass da edilebilmektedir. +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Pekiyi bir çekirdek modülünü ya da aygıt sürücüyü gömülü bir Linux sistemi için derlerken yukarıdaki iki madde dışında nelere + dikkat etmemiz gerekir? Host makine genellikle Intel tabanlı hedef makine de ARM tabanlı olacaktır. O halde yine bir çapraz + derleme işleminin yapılması gerekir. Çapraz derleme yaparken make işleminde yine CROSS_COMPILE çevre değişkeni set edilmelidir. + Derlemenin yapıldığı kabulta yine çağran derleyicinin "bin" dizini için PATH çevre değişkeninin ayarlanmış olması gerekir. + Ayrıca make işleminde ARCH çevre değişkeninin "arm" biçiminde ya da "arm64" biçiminde belirtilmiş olması gerekir. O halde + çapraz derleme yapacak tipik bir Makefile dosyası şöyle olabilir: + + CROSS_COMPILE = arm-none-linux-gnueabihf- + ARCH = arm + + KDIR := /home/kaan/Study/EmbeddedLinux/KernelBuild/linux-6.6.32-ti-arm32-r4 + PWD := $(shell pwd) + + obj-m += $(file).o + + all: + make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules + clean: + make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) clean +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Şimdi en basit bir kernel modülü oluşturup bunu bir başlangıç noktası olarak kullanalım. Bu modülümüze "helloworld" ismini + verelim: + + /* helloworld.c */ + + #include + #include + + MODULE_LICENSE("GPL"); + + int init_module(void) + { + printk(KERN_INFO "Hello World...\n"); + + return 0; + } + + void cleanup_module(void) + { + printk(KERN_INFO "Goodbye World...\n"); + } + + Bu kernel modül aşağıdaki gibi build edilebilir: + + $ make file=helloworld" + + Build işlemi bittiğinde kernel modül "helloworld.ko" dosyası biçiminde oluşturulacaktır. Burada "ko" uzantısı "kernel + object" sözcüklerinden kısaltılmıştır. +-----------------------------------------------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------------------------------------------- + Modüller "modprobe" isimli programla da yüklenebilir. Ancak modprobe programı yüklenecek modülleri "/lib/modules/$(uname -r)" + dizininde aramaktadır. Dolayısıyla biz kendi derlediğimiz modülleri bu dizine yerleştirmemişsek yüklemeyi modprobe ile + yapamayız. Ancak çekirdek derlenirken oluşturulmuş olan modüller bu dizinde olduğu için bunları modprobe ile yükleyebiliriz. + modprobe programı yüklenecek aygıt sürücünün yalnızca ismini almaktadır. Çünkü zaten arama işlemini kendisi yapmaktadır. + Örneğin: + + $ modprobe g_ether + + insmod programının yüklenecek aygıt sürücü dosyasının tüm yol ifadesini aldığına dikkat ediniz. modprobe programında dosyanın + ".ko" uzantısı da belirtilmemektedir. Halbuki insmod programında bu uazantının da belirtilmesi gerekmektedir. modprobe aslında + "modules.dep" isimi bir dosyaya başvurmaktadır. Bu dosya çekirdek kaynak kodlarının kök dizininde çekirdek modülleri derlenirken + oluşturulmaktadır. Bu dosya içerisinde bağımlılık bilgileri vardır. Bir çekirdek modülü yazılırken başka bir çekirdek modülünün + içerisindeki fonksiyonlar kullanılmış olabilir. Bu durumda kullanan modülün yüklenmesi için önce onun kullandığı modülün + yüklenmesi gerekir. İşte bu biçimde durum karmaşık bir hal alabilmektedir. "modules.dep" dosyası içerisinde bir modülün + yüklenebilmesi için hangi modüllerin de yüklenmesi gerektiği bilgileri bulunmaktadır. Eğer biz kendi çekirdek modülümüzün de + modprobe ile yüklenmesini istiyorsak önce onu "/lib/modules/$(uname -r)/kernel" dizininin içerisindeki dizinlerden birine yerleştirip + sonra bu "modules.dep" dosyasının güncllenmesini sağlamamız gerekir. Bu işlem "depmod" programıyla "-a" seçeneği kullanılarak + yapılmaktadır: + + $ sudo depmod -a + + Kendi çekirdek modülünüzü ya da aygıt sürücünüzü örneğin "/lib/modules/$(uname -r)/kernel/drivers/misc" dizinine yerleştirebilirsiniz. + Tabii aygıt sürücü geliştirirken ikide bir modülü buraya yerleştirmenin bir anlamı yoktur. Bu nedenle geliştirme aşamasında + genellikle "insmod" programı kullanılmaktadır. + + modprobe ile yüklenen aygıt sürücü "modeprobe -r" ile boşaltılabilir. Örneğin: + + $ modprobe -r g_ether + + Tabii boşaltım sırasında yine eğer aygıt sürünün bağımlı olduğu çekirdek modülleri başka modüller tarafından kullanılmıyorsa + onlar da çekirdekten çıkartılacaktır. +-----------------------------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------*/ -/*----------------------------------------------------------------------------------------------------------------------------- - ------------------------------------------------------------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------------------------------------------------------- - ------------------------------------------------------------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------------------------------------------------------- - ------------------------------------------------------------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------------------------------------------------------- - ------------------------------------------------------------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------------------------------------------------------- - ------------------------------------------------------------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------------------------------------------------------- - ------------------------------------------------------------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------------------------------------------------------- - ------------------------------------------------------------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------------------------------------------------------- - -----------------------------------------------------------------------------------------------------------------------------*/ diff --git a/Python-App-OzetNotlar-Ornekler.txt b/Python-App-OzetNotlar-Ornekler.txt index 55600c2..37660ce 100644 --- a/Python-App-OzetNotlar-Ornekler.txt +++ b/Python-App-OzetNotlar-Ornekler.txt @@ -8,10 +8,13 @@ Bu notlar Kaan ASLAN tarafından oluşturulmuştur. Kaynak belirtmek koşulu ile her türlü alıntı yapılabilir. - (Notları okurken editörünüzün "Line Wrapping" özelliğini pasif hale getiriniz) + (Notları sabit genişlikli font kullanan programlama editörleri ile açınız.) + (Editörünüzün "Line Wrapping" özelliğini pasif hale getiriniz.) + + Son Güncelleme: 27/10/2023 - Cuma #------------------------------------------------------------------------------------------------------------------------------------ - 1.Ders 07/12/2022 - Çarsamba + 1. Ders 23/02/2025 - Pazar #------------------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------------------ @@ -21,9 +24,9 @@ #------------------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------------------ - time modülündeki time fonksiyonu epoch'tan (01/01/1970'ten) geçen saniye sayısına geri döner. (Aslında fonksiyonda epoch orijininin - 01/01/1970 olduğu belirtilmemiştir. Ancak sistemlerin hemen hepsinde epoch bu biçimdedir.) time fonksiyonu float bir değere geri - dönmektedir. Yani elde edilen saniye sayısı noktalı bir değer olabilmektedir. + time modülündeki time fonksiyonu epoch'tan (01/01/1970'ten) geçen saniye sayısına geri döner. Epoch olarak 01/01/1970 00:00 alınmaktadır. + (Bu epoch C Programalama Dilinin bir çeşit doğum tarihi gibi düşünülmüştür.) time fonksiyonu float bir değere geri dönmektedir. Yani + elde edilen saniye sayısı noktalı bir değer olabilmektedir. #------------------------------------------------------------------------------------------------------------------------------------ import time @@ -49,8 +52,21 @@ result = t2 - t1 print(result) #------------------------------------------------------------------------------------------------------------------------------------ - time modülündeki ctime fonksiyonu epoch'tan (01/01/1970'ten) geçen saniye sayısını parametre olarak alır, onu yazısal biçime - dönüştürerek bir yazı biçiminde bize verir + time modülündeki ctime fonksiyonu epoch'tan (01/01/1970'ten) geçen saniye sayısını parametre olarak alır, onu yazısal biçime + dönüştürerek bir yazı biçiminde bize verir. Örneğin: + + import time + + t = time.time() + s = time.ctime(t) + print(s) + + ctime fonksyonun verdiği yazı belli bir formattadır. Biz o formatı değiştiremeyiz. Format şöyledir: + + Sun Feb 23 19:39:40 2025 + + ctime fonksiyonu bilgisayarın saatine bakmaz. Yalnızca verilen saniye sayısı üzerinde ayrıştırmalar yaparak onu parçalara ayırır. + ctime her zaman yerel saati (local time) vermektedir. #------------------------------------------------------------------------------------------------------------------------------------ import time @@ -60,8 +76,8 @@ s = time.ctime(t) print(s) #------------------------------------------------------------------------------------------------------------------------------------ - Eğer ctime fonksiyonuna parametre girilmezse o andaki zaman dikkate alınmaktadır. Yani bu durumda fonksiyon önce time.time fonksiyonu ile - o andaki zamanı elde edip onu kullanmaktadır. + Eğer ctime fonksiyonuna argüman girilmezse o andaki zaman dikkate alınmaktadır. Yani bu durumda fonksiyon önce time.time fonksiyonu + ile o andaki zamanı elde edip onu kullanmaktadır. #------------------------------------------------------------------------------------------------------------------------------------ import time @@ -70,14 +86,16 @@ s = time.ctime() print(s) #------------------------------------------------------------------------------------------------------------------------------------ - Şu andaki tarih zamanı tek bir ifade ile aşağıdaki gibi yazdırabiliriz. + Şu andaki tarih zamanı tek bir ifade ile aşağıdaki gibi yazdırabiliriz: + + print(time.ctime()) #------------------------------------------------------------------------------------------------------------------------------------ print(time.ctime()) #------------------------------------------------------------------------------------------------------------------------------------ - time modülündeki localtime fonksiyonu 01/01/1970'ten geçen saniye sayısını parametre olarak alır ve struct_time türünden - bir sınıf nesnesine geri döner. Bu sınıf nesnesinin tm_xxx biçiminde isimlendirilmiş olan elemanlarından ilgili zaman bilgisini + time modülündeki localtime fonksiyonu 01/01/1970'ten geçen saniye sayısını parametre olarak alır ve struct_time türünden bir sınıf + nesnesine geri döner. Bu sınıf nesnesinin tm_xxx biçiminde isimlendirilmiş olan elemanlarından ilgili zaman bilgilerini alabiliriz. struct_time sınıfının önemli elemanları şunlardır: tm_gmtoff @@ -92,7 +110,8 @@ print(time.ctime()) tm_year tm_zone - Bu fonksiyon da parametresiz kullanımda o andaki zamana ilişkin bilgiyi vermeketedir. + Bu fonksiyon da parametresiz kullanımda o andaki zamana ilişkin bilgiyi vermeketedir. localtime fonksiyonun da bilgisayarın saatine + bakmadığına yalnızca ayrıştırma işlemini yaptığına dikkat ediniz. #------------------------------------------------------------------------------------------------------------------------------------ import time @@ -102,27 +121,24 @@ st = time.localtime() print(f'{st.tm_mday:02d}/{st.tm_mon:02d}/{st.tm_year:04d}-{st.tm_hour:02d}:{st.tm_min:02d}:{st.tm_sec:02d}') #------------------------------------------------------------------------------------------------------------------------------------ - time modülündeki perf_counter fonksiyonu daha duyarlıklı zaman ölçmek için bulundurulmuştur. Bu fonksiyon bize float - türünden bir saniye sayısı verir. Ancak bu sayının nereden itibaren bir saniye sayısı belirttiği belli değildir. (Örneğin bu değer - 01/01/1970'ten geçen saniye sayısı değildir.) Dolayısıyla bu fonksiyon ancak iki kere çağrılıp aradaki farkı hesaplamak için anlamlıdır. + time modülündeki perf_counter fonksiyonu daha duyarlıklı zaman ölçmek için bulundurulmuştur. Bu fonksiyon bize float türünden bir + saniye sayısı verir. Ancak bu sayının nereden itibaren bir saniye sayısı belirttiği belli değildir. (Örneğin bu değer 01/01/1970'ten + geçen saniye sayısı değildir.) Dolayısıyla bu fonksiyon ancak iki kere çağrılıp aradaki farkı hesaplamak için anlamlıdır. #------------------------------------------------------------------------------------------------------------------------------------ import time t1 = time.perf_counter() - for _ in range(100_000_000): pass - t2 = time.perf_counter() result = t2 - t1 - print(result) #------------------------------------------------------------------------------------------------------------------------------------ - time modülündeki perf_counter_ns bize geçen zaman bilgisini nano saniye (saniyenin milyarda biri) cinsinden verir. Dolayısıyla bu fonksiyon - noktadan sonraki yuvarlama hataları ile ilgili sorun oluşturmaz. + time modülündeki perf_counter_ns bize geçen zaman bilgisini nano saniye (saniyenin milyarda biri) cinsinden verir. Dolayısıyla bu + fonksiyon noktadan sonraki yuvarlama hataları ile ilgili sorun oluşturmaz. #------------------------------------------------------------------------------------------------------------------------------------ import time @@ -170,9 +186,9 @@ for i in range(10): bad_sleep(0.5) #------------------------------------------------------------------------------------------------------------------------------------ - datetime isimli standart modül faydalı birkaç sınıf barındırmaktadır. datetime modülündeki date isimli sınıf bir tar,h bilgisini (zaman değil) - tutarak bazı temel işlemleri yapmamıza olanak sağlamaktadır. date sınıfının __init__ metodu bizden yıl, ay, gün bilgisini alarak - sınıfın year, month, day örnek özniteliklerinde saklar. + datetime isimli standart modül faydalı birkaç sınıf barındırmaktadır. datetime modülündeki date isimli sınıf bir tarih bilgisini + (zaman değil) tutarak bazı temel işlemleri yapmamıza olanak sağlamaktadır. date sınıfının __init__ metodu bizden yıl, ay, gün bilgisini + alarak nesnenin year, month, day özniteliklerinde saklar. datetime.date(year, month, day) @@ -185,10 +201,6 @@ d = datetime.date(2022, 10, 21) print(d) print(f'{d.day:02d}/{d.month:02d}/{d.year:04d}') -#------------------------------------------------------------------------------------------------------------------------------------ - 2. Ders 12/12/2022 - Pazartesi -#------------------------------------------------------------------------------------------------------------------------------------ - #------------------------------------------------------------------------------------------------------------------------------------ datetime sınıfının static today isimli metodu o andaki tarih bilgisini date nesnesi olarak verir. #------------------------------------------------------------------------------------------------------------------------------------ @@ -200,7 +212,17 @@ print(d) print(f'{d.day}/{d.month}/{d.year}') #------------------------------------------------------------------------------------------------------------------------------------ - O anki tarihi Python'da yazdırmanın en pratik yoluş aşağıdaki gibidir. + O anki tarihi Python'da yazdırmanın pratik bir yolu aşağıdaki gibidir. + + import datetime + + print(datetime.date.today()) + + Tabii bunu yukarıda da belrttiğimiz gibi time.ctime fonksiyonuyla da yapabiliriz: + + import time + + print(time.ctime()) #------------------------------------------------------------------------------------------------------------------------------------ import datetime @@ -208,17 +230,21 @@ import datetime print(datetime.date.today()) #------------------------------------------------------------------------------------------------------------------------------------ - date sınıfının weekday isimli metodu bize tarihin haftanın kaçıncı günü olduğu bilgisini verir. Burada 0 = Pazartesidir. + date sınıfının weekday isimli metodu bize tarihin haftanın kaçıncı günü olduğu bilgisini verir. Burada 0 = Pazartesidir. Örneğin + weekday metodu bize 4 verdiyse bu ilgili günün Cuma olduğu anlamına gelecektir. Biz burada elde ettiğimiz değeri günlerin isimlerinin + bulunduğu bir listeye indeks yaparak gün ismini elde edebiliriz. Örneğin: + + d = datetime.date.today() + daytext = ('Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi', 'Pazar') + print(daytext[d.weekday()]) #------------------------------------------------------------------------------------------------------------------------------------ import datetime d = datetime.date.today() - daytext = ('Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi', 'Pazar') - print(daytext[d.weekday()]) - + #------------------------------------------------------------------------------------------------------------------------------------ date sınıfının isoweekday isimli metodu yine bize nesnenin tuttuğu tarihin haftanın kaçıncı günü olduğu bilgisini verir. Ancak haftanın başlangıç günü Pazar'dır. Yani burada 0 = Pazar'dır. @@ -1587,53 +1613,52 @@ python -m timeit -n 100000 "'-'.join(str(i) for i in range(100))" UPDATE student SET student_name = 'Mehmet Kömcü' WHERE student_name = 'Timur Kömcü'; Burada "Timur Kömcü" olan öğrencinin ismi "Mehmet Kömcü" biçiminde değiştirilmiştir. UPDATE komutu dikkatle uygulanmalıdır. - Komutun WHERE kısmı unutulursa tüm kayıtlarda değişiklik yapılmaktadırç. - + Komutun WHERE kısmı unutulursa tüm kayıtlarda değişiklik yapılmaktadır. #------------------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------------------ Koşulu sağlayan kayıtların elde edilmesi SELECT komutuyla yapılmaktadır. SELECT komutunun genel biçimi ayrıntılıdır. Çünkü komuta çeşitli cümlecikler monte edilebilmektedir. Komutun temel biçimi şöyledir: - SELECT FROM [WHERE FROM [WHERE 600; Burada öğrenci numarası 600'den büyük olan öğrencilerin tüm sütun bilgileri elde edilmiştir. - Eğer SELECT sedilen kayıtlar belli bir sütuna göre elde edilmek istenirse ORDER BY cümleciği komuta eklenir. Örneğin: + Eğer SELECT edilen kayıtlar belli bir sütuna göre sıralı biçimde elde edilmek istenirse ORDER BY cümleciği komuta eklenir. Örneğin: SELECT * FROM student WHERE student_id > 600 ORDER BY stdent_name; - ORDER BY default olarak küçükteb büyüğe (ASC) vermektedir. Ancak DESC ile büyükten küçüğe de sıralama yapılabilir. Örneğin: + ORDER BY default olarak kayıtları küçükten büyüğe (ASC) vermektedir. Ancak DESC ile büyükten küçüğe de sıralama yapılabilir. Örneğin: SELECT * FROM student WHERE student_no > 600 ORDER BY student_name DESC; - ORDER BY cümleciğinde birden falz sütun belirtilebilir. Bu durumda ilk sütun değerleri aynıysa diğer sütunlar dikkate alınır. Örneğin: + ORDER BY cümleciğinde birden fazla sütun belirtilebilir. Bu durumda ilk sütun değerleri aynıysa diğer sütunlar dikkate alınır. Örneğin: SELECT * FROM student WHERE student_no > 600 ORDER BY student_name DESC, student_no ASC; Burada ismi aynı olanlar numaralarına göre küçükten büyüğe elde edilecektir. - LIMIT cümleceği de SELECT cümlesiyle kullanılabilir. LIMIT anahtar sözcüğnün yanında bir sayı bulunur. Koşulu sağlayan kayıtların belli sayıda - miktarını elde etmek için kullanılır. Örneğin: + LIMIT cümleceği de SELECT cümlesiyle kullanılabilir. LIMIT anahtar sözcüğnün yanında bir sayı bulunur. Koşulu sağlayan kayıtların + belli sayıda miktarını elde etmek için kullanılır. Örneğin: SELECT * FROM student WHERE student_no > 600 ORDER BY student_name DESC, student_no ASC LIMIT 10; WHERE cümleciğinde built-in fonksiyonlar kullanılabilir. Örneğin: - SELECT * FROM city WHERE length(city) == 6; + SELECT * FROM city WHERE char_length(city) = 6; #------------------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------------------ - İlişkisel veri tabanlarında tablolarda veri tekrarı istenmez. Örneğin bir öğrenci veritabanı oluşturacak olalım. Bir öğrencinin çeşitli bilgilerinin - yanı sıra onun okulu hakkında da bilgileri tutmak isteyelim. Aşağıdaki gibi bir tablo tasarımı uygun değildir: + İlişkisel veritabanlarında tablolarda veri tekrarı istenmez. Örneğin bir öğrenci veritabanı oluşturacak olalım. Bir öğrencinin + çeşitli bilgilerinin yanı sıra onun okulu hakkında da bilgileri tutmak isteyelim. Aşağıdaki gibi bir tablo tasarımı uygun değildir: Adı Soyadı No Okul Adı Okulun Bulunduğu Şehir Okulun Türü - + -------------------------------------------------------------------------------------- Ali Serçe 123 Tarsus Amerikan Lisesi Mersin Devlet Lisesi Kaan Aslan 745 Eskişehir Atatürk Lisesi Eskişehir Devlet Lisesi Hasan Bulur 734 Tarsus Amerikan Lisesi Mersin Devlet Lisesi @@ -1644,6 +1669,7 @@ python -m timeit -n 100000 "'-'.join(str(i) for i in range(100))" Öğrenci Tablosu Adı Soyadı No Okul ID'si + ---------------------------------- Ali Serçe 123 100 Kaan Aslan 745 235 Hasan Bulur 734 100 @@ -1652,18 +1678,20 @@ python -m timeit -n 100000 "'-'.join(str(i) for i in range(100))" Okul Tablosu Okul Id'si Okul Adı Okulun Bulunduğu Şehir Okulun Türü + ------------------------------------------------------------------------------------ + ... ... ... ... 100 Tarsus Amerikan Lisesi Mersin Devlet Lisesi 150 Eskişehir Atatürk Lisesi Eskişehir Devlet Lisesi ... ... ... ... - Burada veri tekrarı minimale indirilmiştir. Tabii bu tablolarda da Okul ID'si ortak bir sütundur. Bu ortak sütun - tablolar arasında ilişki kurmak için gerekmiştir. Bu tür sütunlara "foreign key" denilmektedir. Ancak yukarıdaki gibi tekrar engellendiğinde - gerekli bilgiler artık tek bir tablodan değil çeşitli tabolardan çekilip alınacaktır. İşe çeşitli tabolardan bilgilerin çekilip alınması işlemine - "JOIN" işlemi denilmektedir. JOIN işleminin birkaç biçimi vardır (INNER JOIN, OUTER JOIN, LEFT JOIN, RIGHT JOIN gibi). Ancak en fazla kullanılan - JOIN işlemi "INNER JOIN" denilen işlemdir. JOIN denildiğinde zaten default olarak INNER JOIN anlaşılır. INNER JOIN işleminde eğer iki tablo söz konusu - ise önce iki tablonun kartezyen çarpımları elde edilir. Her kaztezyen çarpım iki tablonun birleştirilmesi biçiminde (join oradan geliyor) - elde edilmektedir. Sonra kartezyen çarpımlarda yalnızca belli koşulu sağlayan satırlar elde edilir. Böylece tablolar "ilişkisel (relational)" - biçimde birleştirilmiş olur. + Burada veri tekrarı oratadan kaldırılmıştır. Tabii bu tablolarda da Okul ID'si ortak bir sütundur. Bu ortak sütun tablolar arasında + ilişki kurmak için gerekmektedir. Bu tür sütunlara "foreign key" de denilmektedir. Ancak yukarıdaki gibi tekrarlar engellendiğinde + gerekli bilgiler artık tek bir tablodan değil çeşitli tabolardan çekilip alınacaktır. İşe çeşitli tabolardan bilgilerin çekilip alınması + işlemine "JOIN" işlemi denilmektedir. JOIN işleminin birkaç biçimi vardır (INNER JOIN, OUTER JOIN, LEFT JOIN, RIGHT JOIN gibi). Ancak + en fazla kullanılan JOIN işlemi "INNER JOIN" denilen işlemdir. JOIN denildiğinde zaten default olarak INNER JOIN anlaşılır. INNER JOIN + işleminde eğer iki tablo söz konusu ise önce iki tablonun kartezyen çarpımları elde edilir. Her kaztezyen çarpım iki tablonun birleştirilmesi + biçiminde ("join" ismi oradan geliyor) elde edilmektedir. Sonra kartezyen çarpımlarda yalnızca belli koşulu sağlayan satırlar elde + edilir. Böylece tablolar "ilişkisel (relational)" biçimde birleştirilmiş olur. INNER JOIN sentaksı iki biçimde oluşturulabilmektedir. Birinci sentaks klasik eski tip sentakstır. İkinci sentaks daha modern biçimdir. Klasik eski tip sentaks şöyledir: @@ -1672,18 +1700,22 @@ python -m timeit -n 100000 "'-'.join(str(i) for i in range(100))" Örneğin: - SELECT student.student_name, student.student_no, school.school_name FROM student INNER JOIN school ON student.school_id = school.school_id WHERE stduent.student_no > 600; + SELECT student.student_name, student.student_no, school.school_name FROM student INNER JOIN school ON + student.school_id = school.school_id WHERE stduent.student_no > 600; - Sütun isimleri belirtilirken eğer çakışma yoksa yalnızca isimler yazılabilir. Ancak çakışma varsa tablo ismi nokta operatörü ile sütunun hangi tabloya ilişkin olduğu - belirtilmelidir. Bazı uygulamacılar çakışma olsa da olmasada niteliklendirme yaparlar. Bazı uygulamacılar yalnızca çakışan sütunşarda - niteliklendirme yaparlar. Yukarıdaki örnekte tüm sütunlar niteliklendirilerek belirtilmiştir. Bu örnek şöyle de yapılabilirdi: + Sütun isimleri belirtilirken eğer çakışma yoksa yalnızca isimler yazılabilir. Ancak çakışma varsa tablo ismi ve nokta operatörü ile + sütunun hangi tabloya ilişkin olduğu belirtilmelidir. Bazı uygulamacılar çakışma olsa da olmasa da niteliklendirme yaparlar. Bazı + uygulamacılar yalnızca çakışan sütunlarda niteliklendirme yaparlar. Yukarıdaki örnekte tüm sütunlar niteliklendirilerek belirtilmiştir. + Bu örnek şöyle de yapılabilirdi: SELECT student_name, student_no, school_name FROM student INNER JOIN school ON student.school_id = school.school_id WHERE student_no > 600; - Modern INNER JOIN sentaksında SELECT komutunun FROM kısmında birden fazla tablo ismi belirtilir. Koşul da yine WHERE cümleciğine taşınır. Örneğin: + Modern INNER JOIN sentaksında SELECT komutunun FROM kısmında birden fazla tablo ismi belirtilir. Koşul da yine WHERE cümleciğine taşınır. + Örneğin: SELECT student_name, student_no, school_name FROM student, school WHERE student.school_id = school.school_id AND student_no > 600; + Daha çok bu modern biçim tercih edilmektedir. #------------------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------------------ @@ -21807,9 +21839,9 @@ print(c) #------------------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------------------ - NumPy kütüphanesinde bir NumPy dizisinin her elemanı üzerinde işlem yapan axis parametresine sahip olmayan pek çok klasik matematiksel fonksiyon - ve ndarray sınıfının metodu metot vardır. Bunlardan bazılarrı şunlardır: sqrt, square, power, log, log10, log2, round, exp, sin, cos, tan, arcsin, arccos, arctan, - lcm (okek), gcd (obeb). Örneğin: + NumPy kütüphanesinde bir NumPy dizisinin her elemanı üzerinde işlem yapan axis parametresine sahip olmayan pek çok klasik matematiksel f + onksiyon ve ndarray sınıfının metodu metot vardır. Bunlardan bazılarrı şunlardır: sqrt, square, power, log, log10, log2, round, exp, + sin, cos, tan, arcsin, arccos, arctan, lcm (okek), gcd (obeb). Örneğin: >>> a = np.random.random((5, 5)) >>> a @@ -21927,17 +21959,17 @@ print(c) Bu nedenle genellikle bu tür işlemler dizinin kendi üzerinde yapılmaz, işlem sonucunda bu işlemlerin yapılmış olduğu yeni bir dizi verilir. NumPy'da ekleme ve silme gibi işlemlerde axis paraetresi bu işlemin sonucunda değişikliğin oluştuğu ekseni belirtmektedir. Örneğin biz - iki boyutlu bir numpy dizisine satır eklemek istersek burada satır bu dizinin ilk boyutu üzerinde değişiklik yaratır. Bu nedenle satır ekleme - işleminde axis=0 girilmelidir. axis=1 sütun ekleneceği anlamına gelektedir. + iki boyutlu bir numpy dizisine satır eklemek istersek burada satır bu dizinin ilk boyutu üzerinde değişiklik yaratır. Bu nedenle + satır ekleme işleminde axis=0 girilmelidir. axis=1 sütun ekleneceği anlamına gelektedir. #------------------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------------------ - Bir NumPy dizisine eleman (satır, sütun) eklemek için append fonksiyonu kullanılır. append her zaman NumPy dizisinin yeni bir kopyasını oluşturur. - Yani in-place işlem yapmaz. append fonksiyonunun birinci parametresi eklemenin yapılacağı NumPy dizisini belirtir. İkinci parametre ise - eklenecek değerlerin bulunduğu NumPy dizisini belirtmektedir. append fonksiyonunda biz her zaman ana diziyle aynı boyutta bir dizi eklemeliyiz. - Örneğin iki bir boyutlu bir diziye tek bir satır ekleyeceksek - bu satırın sanki tek satırlık bir matris gibi olması gerekir. Benzer biçimde iki boyutlu bir diziye tek bir sütun ekleyeceksek bu sütunun - sanki tek sütunlu bir matris biçiminde olması gerekir. Örneğin: + Bir NumPy dizisine eleman (satır, sütun) eklemek için append fonksiyonu kullanılır. append her zaman NumPy dizisinin yeni bir + kopyasını oluşturur. Yani in-place işlem yapmaz. append fonksiyonunun birinci parametresi eklemenin yapılacağı NumPy dizisini belirtir. + İkinci parametre ise eklenecek değerlerin bulunduğu NumPy dizisini belirtmektedir. append fonksiyonunda biz her zaman ana diziyle + aynı satır ya da sütun sayısına sahip bir dizi eklemeliyiz. Örneğin iki bir boyutlu bir diziye tek bir satır ekleyeceksek bu satırın + sanki tek satırlık bir matris gibi olması gerekir. Benzer biçimde iki boyutlu bir diziye tek bir sütun ekleyeceksek bu sütunun sanki + tek sütunlu bir matris biçiminde olması gerekir. Örneğin: >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> a @@ -21973,7 +22005,8 @@ print(c) File "C:\Users\aslan\anaconda3\lib\site-packages\numpy\lib\function_base.py", line 5444, in append return concatenate((arr, values), axis=axis) File "<__array_function__ internals>", line 180, in concatenate - ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 2 dimension(s) and the array at index 1 has 1 dimension(s) + ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 2 dimension(s) and the array at + index 1 has 1 dimension(s) Satır eklemesi de yine aynı boyutta bir dizi ile yapılabilmektedir. Örneğin: @@ -22035,9 +22068,9 @@ print(c) #------------------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------------------ - append işleminde eksen belirtilmezse (axis parametresinin default değeri None biçimindedir). Dizi önce flatten işlemiyle tek boyuta indirgenir. - Sonra işlem yapılır. Bu işleme bazen seyrek de olsa gereksinim duyulmaktadır. Bu durumda append yapılzcak değerler herhangi bir - boyutta girilebilir. Zaten bu değerler de flatten işlemine sokulmaktadır. Örneğin: + append işleminde eksen belirtilmezse (axis parametresinin default değeri None biçimindedir). Eklemenin yapılacağı dizi önce flatten + işlemiyle tek boyuta indirgenir. Sonra işlem yapılır. Bu işleme bazen seyrek de olsa gereksinim duyulmaktadır. Bu durumda append + yapılzcak değerler herhangi bir boyutta girilebilir. Zaten bu değerler de flatten işlemine sokulmaktadır. Örneğin: >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> a @@ -22061,66 +22094,67 @@ print(c) insert isimli fonksiyon bizden bir NumPy dizisini, insert edilecek pozisyonu ve insert edilecek değerleri almaktadır. Insert işlemi insert edilmek istenen değerler insert pozisyonunda olacak biçimde diğer değerlerin kaydırılması yoluyla yapılmaktadır. Ancak insert fonksiyonunda insert edilecek dizi append fonksiyonunda olduğu gibi oluşturulmamaktadır. insert fonksiyonunda insert edilecek - dizi tek boyutlu olarak verilebilir. Örneğin: + dizi dolaşıldığında satırlara ya da sütunlara insert edilecek değerler elde edilmelidir. Insert edilecek dizi tek boyutlu olarak + verilebilir. axis parametresi 0 girilirse satır, 1 girilirse sütun insert edilmektedir. Örneğin: + >>> import numpy as np >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - >>> a - array([[1, 2, 3], - [4, 5, 6], - [7, 8, 9]]) + >>> print(a) + [[1 2 3] + [4 5 6] + [7 8 9]] >>> b = np.array([10, 20, 30]) - >>> b - array([10, 20, 30]) + >>> print(b) + [10 20 30] >>> result = np.insert(a, 1, b, axis=0) - >>> result - array([[ 1, 2, 3], - [10, 20, 30], - [ 4, 5, 6], - [ 7, 8, 9]]) + >>> print(result) + [[ 1 2 3] + [10 20 30] + [ 4 5 6] + [ 7 8 9]] >>> result = np.insert(a, 1, b, axis=1) - >>> result - array([[ 1, 10, 2, 3], - [ 4, 20, 5, 6], - [ 7, 30, 8, 9]]) + >>> print(result) + [[ 1 10 2 3] + [ 4 20 5 6] + [ 7 30 8 9]] - Eğer birden fazla satır ya da sütun insert edilecekse bu satır ya da sütun bilgileri her zaman iki boyutlu bir dizi biçininde verilmektedir. - Örneğin: + Eğer birden fazla satır ya da sütun insert edilecekse bu satır ya da sütun bilgileri her zaman iki boyutlu bir nesne biçininde verilmelidir. + insert fonksiyonu girilen bu nesneyi dolaşarak insert edilecek satır ya da sütunları elde eder. Örneğin: >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - >>> a - array([[1, 2, 3], - [4, 5, 6], - [7, 8, 9]]) + >>> print(a) + [[1 2 3] + [4 5 6] + [7 8 9]] >>> b = np.array([[10, 20, 30], [40, 50, 60]]) - >>> b - array([[10, 20, 30], - [40, 50, 60]]) + >>> print(b) + [[10 20 30] + [40 50 60]] >>> result = np.insert(a, 1, b, axis=1) - >>> result - array([[ 1, 10, 40, 2, 3], - [ 4, 20, 50, 5, 6], - [ 7, 30, 60, 8, 9]]) + >>> print(result) + [[ 1 10 40 2 3] + [ 4 20 50 5 6] + [ 7 30 60 8 9]] >>> result = np.insert(a, 1, b, axis=0) - >>> result - array([[ 1, 2, 3], - [10, 20, 30], - [40, 50, 60], - [ 4, 5, 6], - [ 7, 8, 9]]) + >>> print(result) + [[ 1 2 3] + [10 20 30] + [40 50 60] + [ 4 5 6] + [ 7 8 9]] - Görüldüğü gibi insert işleminde biz insert edilecek diziyi orijinal dizi ile aynı boyuta getirmek zorunda değiliz. Fonksiyonun kendisi - zaten bunu yapmaktadır. Fonksiyon aslında dolaşılabilir herhangi bir nesneyi de alabilmektedir. Örneğin: + Fonksiyon aslında dolaşılabilir herhangi bir nesneyi de alabilmektedir. Örneğin: - >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - >>> a - array([[1, 2, 3], - [4, 5, 6], - [7, 8, 9]]) + >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + >>> print(a) + [[1 2 3] + [4 5 6] + [7 8 9]] >>> result = np.insert(a, 1, [[10, 20, 30], [40, 50, 60]], axis=1) - >>> result - array([[ 1, 10, 40, 2, 3], - [ 4, 20, 50, 5, 6], - [ 7, 30, 60, 8, 9]]) + >>> print(result) + [[ 1 10 40 2 3] + [ 4 20 50 5 6] + [ 7 30 60 8 9]] insert işleminde insert edilecek değer bir skaler de olabilir. Bu durumda ilgili satır ya da sütun o değerle doldurulur. Örneğin: @@ -22141,19 +22175,41 @@ print(c) [ 4, 5, 6], [ 7, 8, 9]]) - insert işleminde de axis belirtilmezse önce flatten yapılıp insert o noktaya yapılmaktadır. Örneğin: + Buradan hareketle NumPy dizisine bir sütun vektörü insert edilmeye çalışılırsa bu sütun vektörünün her elemanı sütun olarak insert + edilmektedir. Örneğin: + + >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + >>> print(a) + [[1 2 3] + [4 5 6] + [7 8 9]] + >>> b = np.array([[10], [20], [30]]) + >>> print(b) + [[10] + [20] + [30]] + >>> result = np.insert(a, 1, b, axis=1) + >>> print(result) + [[ 1 10 20 30 2 3] + [ 4 10 20 30 5 6] + [ 7 10 20 30 8 9]] + + Insert işleminde de axis belirtilmezse önce insert işleminin uygulanacağı dizi flatten yapılıp insert o noktaya yapılmaktadır. Örneğin: >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - >>> a - array([[1, 2, 3], - [4, 5, 6], - [7, 8, 9]]) + >>> print(a) + [[1 2 3] + [4 5 6] + [7 8 9]] >>> b = np.array([10, 20, 30]) - >>> b - array([10, 20, 30]) + >>> print(b) + [10 20 30] >>> result = np.insert(a, 1, b) - >>> result - array([ 1, 10, 20, 30, 2, 3, 4, 5, 6, 7, 8, 9]) + >>> print(result) + [ 1 10 20 30 2 3 4 5 6 7 8 9] + + + #------------------------------------------------------------------------------------------------------------------------------------ @@ -22183,7 +22239,6 @@ print(c) >>> result = np.delete(a, 1) >>> result array([1, 3, 4, 5, 6, 7, 8, 9]) - #------------------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------------------ @@ -22263,15 +22318,15 @@ print(c) Aşağıdaki gibi "points.csv"isminde bir CSV dosyamız olsun: -x,y -10,23 -5,9 -6.1,8.7 -12,81 -4.2,9.3 -11,27 -32,72 -12.3,7.8 + x,y + 10,23 + 5,9 + 6.1,8.7 + 12,81 + 4.2,9.3 + 11,27 + 32,72 + 12.3,7.8 Dosyayı şöyle okuyabiliriz: @@ -25434,8 +25489,6 @@ print(df) Bu biçimde bir NumPy dizisini de bir satır olarak ekleyebiliriz. - - DataFrame nesnesine append metodu ile satır ve sütun eklenebiliyordu. Ancak bu metot artık "deprecated" yapılmış durumdadır. #------------------------------------------------------------------------------------------------------------------------------------ diff --git a/Python-OzetNotlar-Ornekler.txt b/Python-OzetNotlar-Ornekler.txt index db5b02e..c7ca407 100644 --- a/Python-OzetNotlar-Ornekler.txt +++ b/Python-OzetNotlar-Ornekler.txt @@ -26290,6 +26290,54 @@ class Sample: s = Sample() s.test() +#------------------------------------------------------------------------------------------------------------------------ + Burada ince fakat bazen önemli olabilecek bir ayrıntı üzerinde durmak istiyoruz. Aşağıdaki gibi bir dekorasyon söz konusu + olsun: + + @foo + def bar(): + pass + + Biz (ve bizim gibi pek çok kaynak) kolay bir anlatım sağlayabilmek için bu dekorasyonun eşdeğerinin aşağıdaki gibi + olduğunu söylemiştik: + + def bar(): + pass + bar = foo(bar) + + Aslında dekorasyınun tam eşdeğeri yukarıdaki gibi aşağıdaki gibidir: + + def _bar_xxx(): + pass + bar = foo(_bar_xxx) + + Burada _bar_xxx bar fonksiyonunun atandığı geçici değişkeni belirtiyor. Yani aslında yorumlayıcı önce bar değişkenine + fonksiyon nesnesini atayıp sonra dekore etmemektedir. Bir geçici değişken yoluyla fonksiyon nesnesini dekoratöre + vermektedir. Benzer biçimde: + + @foo + class bar: + pass + + Bu dekorasyonun eşdeğeri de aslında tam olarak aşağıdaki gibi değildir: + + class bar: + pass + bar = foo(bar) + + Aşağıdaki gibidir: + + class _bar_xxx: + pass + bar = foo(_bar_xxx) + + Burada _bar_xxx sınıf için oluturulan type nesnesinin atandaığı geçici değişkeni belirtiyor. Yani aslında yorumlayıcı + önce bar değişkenini oluşturup onu foo dekoratörüne vermemektedir. Yorumlayıcı sınıfın type nesnesini oluşturup + onu geçici değişken yoluyla foo dekoratörüne vermektedir. İşte kolay anlaşılsın diye verilen eşdeğerlikle gerçek + eşdeğer durum arasındaki fark bazı durumlarda önemli olabilmektedir. İleride property'lerin anlatıldığı bölümde + bu konuda açıklamalar yapacağız. +#------------------------------------------------------------------------------------------------------------------------ + #------------------------------------------------------------------------------------------------------------------------ 52. Ders 18/01/2025 - Cumartes #------------------------------------------------------------------------------------------------------------------------ @@ -31517,8 +31565,9 @@ print(d.day, d.month, d.year) a = a.setter(a) Bu eşdeğerlilikten hareketle a metodunu gören yorumlayıcı a değişkenine değer atadığında a değişkeninin içinin bozulacağını - düşünebilirsiniz. İşte dekoratör sentaksında aslında dekoratöre konu olan değişken boşuna hiç oluşturulmamaktadır. Yani burada a - ismi aslında bozulmamaktadır. Başka bir deyişle aslında yukarıdaki dekoratörün eşdeğeri şöyledir: + düşünebilirsiniz. İşte dekoratörler konusunda da belirttiğimiz gibi dekoratör sentaksında aslında dekoratöre konu olan + değişken boşuna hiç oluşturulmamaktadır. Yani burada a ismi aslında bozulmamaktadır. Başka bir deyişle aslında yukarıdaki + dekoratörün eşdeğeri şöyledir: def some_temporary_name(self, val): self._a = val @@ -31531,9 +31580,9 @@ print(d.day, d.month, d.year) def bar(): pass - Burada aslında bar değişkenine iki kere değer atanmamaktadır. Yani yorumlayıcı önce fonksiyon nesnesinin adresini bar değişkenine - atayıp sonra onu dekatöre vermemektedir. Fonksiyon nesnesini yaratıp henüz bar değişkenine atamdan dekoratöre vermektedir. Dolayısıyla - aslında bar değişkenine yalnızca dekoratörün döndürdüğü değer atanmaktadır. + Burada aslında bar değişkenine iki kere değer atanmamaktadır. Yani yorumlayıcı önce fonksiyon nesnesinin adresini bar + değişkenine atayıp sonra onu dekatöre vermemektedir. Fonksiyon nesnesini yaratıp henüz bar değişkenine atamdan dekoratöre + vermektedir. Dolayısıyla aslında bar değişkenine yalnızca dekoratörün döndürdüğü değer atanmaktadır. deleter metodu da yine property nesnesinin deleter isimli metoduyla oluşturulmaktadır. Örneğin: @@ -31566,11 +31615,11 @@ print(d.day, d.month, d.year) Bu dekoratörün oluşturulma biçimi de tamamen setter property'sinde olduğu gibidir. Örneğin: class myproperty: - def __init__(self, fget = None, fset = None, fdel = None, fdoc = None): + def __init__(self, fget = None, fset = None, fdel = None, doc = None): self._fget = fget self._fset = fset self._fdel = fdel - self._fdoc = fdoc + self._doc = doc def setter(self, fset): self._fset = fset @@ -31579,6 +31628,7 @@ print(d.day, d.month, d.year) def deleter(self, fdel): self._del = fdel return self +#------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ Sınıfların diğer bir özel metodu ise __new__ isimli metottur. Biz bir sınıf türünden nesne yarattığımızda aslında bu @@ -31777,13 +31827,23 @@ print(k, id(k)) m = Sample() print(m, id(m)) - + #------------------------------------------------------------------------------------------------------------------------ - Betimleyiciler (descriptors) ya da betimleyici sınıflar (descriptor classes) Python'a sonradan eklenmiştir. property - sınıfı gibi birtakım sınıfların yazılabilmesi için böyle bir kavrama ihtiyaç duyulmuştur. Bir sınıfın betimleyici - sınıf olması için o sınıfta __get__, __set__ ya da __delete__ metotlarının en bir tanesinin bulunuyor olması gerekir. - Genellikle betimleyici sınıflarda __get__ ve __set__ metotları birlikte bulunurkar. Ancak bazı sınıflarda yalnızca - __get__ metodu bulunuyor olabilir. + 61. Ders 16/02/2025 - Pazar +#------------------------------------------------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------------------------------------------------ + Daha öce görmüş olduğumuz property isimli dekaratör bir sınıf olarak mevcut bilgiler kullanılarak yazılamaz. Bu tür + sınıfların yazılabilmesi için dile "betimleyici (descriptor)" denilen bir özellik eklenmiştir. Betimleyicilere çok + seyrek gereksinim duyulmaktadır. Bu nedenle bu konu genellikle Python programcıları tarafından bilinen bir konu + değildir +#------------------------------------------------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------------------------------------------------ + Bir sınıfın betimleyici sınıf olması için o sınıfta __get__, __set__ ya da __delete__ metotlarının en az bir tanesinin + bulunuyor olması gerekir. Genellikle betimleyici sınıflarda __get__ ve __set__ metotları birlikte bulunurlar. Ancak bazı + sınıflarda yalnızca __get__ metodu bulunuyor olabilir. Betimleyici sınıflarda __delete__ bulunduurlmasına nadiren gereksinim + duyulmaktadır. __get__ metodunun self dışında iki parametresi olmalıdır. Bu parametrelee genellikle instance ve owner biçiminde isimlendirilmektedir. Örneğin: @@ -32067,16 +32127,15 @@ del s.a # delete s_a pip programı Internet'te üçüncü parti kütüphanelerin bulunduğu server'lara başvurur oradan kütüphanenin içeriğini yerel makineye indirir ve kütüphaneyi oluşturan Python dosyalarını bir dizine yerleştirir. Yerel makinedeki bu dizin Python - için birden fazla Python dosyasından oluşan bir pakettir. + bakış açısıyla birden fazla Python dosyasından oluşan bir pakettir. #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ - - Python'da bir dizin'in bir paket olarak ele alınması için tek gereken şey o dizin'in içinde "__init__.py" isimli bir - dosyanın bulunyor olmasıdır. Paketler de tamamen modüller gibi import edilmektedir. - - Örneğin bulunduğumuz dizin'in altında mypackage isimli bir dizin yaratıp onun içerisine "__init__.py" dosyasını yerleştirelim. - Dosyanın içi boş olabilir. Biz bir paketi nasıl bir dosyayı import ediyorsak dosya gibi import edebiliriz. Örneğin: + Python'da bir dizin'in bir paket olarak ele alınması için tek gereken şey o dizin'in içinde "__init__.py" isimli özel + bir dosyanın bulunuyor olmasıdır. Python 3.3'ten itibaren bu zorunluluk da ortadan kaldırılmıştır. Python 3.3'ten + itibaren her dizin bir paket olarak ele alınabilmektedir. Paketler de tamamen modüller gibi import edilmektedir. Örneğin + bulunduğumuz dizin'in altında mypackage isimli bir dizin yaratıp onun içerisine "__init__.py" dosyasını yerleştirelim. + Dosyanın içi boş olabilir. Biz nasıl bir dosyayı import ediyorsak paketi de aynı biçimde import edebiliriz. Örneğin: import mypackage @@ -32084,8 +32143,8 @@ del s.a # delete s_a import mypackage as mp - Bir paket import edildiğinde paketin içerisindeki "__init__.py" dosyası otomatik çalıştırılmaktadır. Örneğin mypackage - isimli paketteki "__init__.py" dosyasının içeriği şöyle olsun: + Bir paket import edildiğinde paketin içerisindeki "__init__.py" dosyası otomatik olarak çalıştırılmaktadır. Örneğin + mypackage isimli paketteki "__init__.py" dosyasının içeriği şöyle olsun: # __init__.py @@ -32097,7 +32156,7 @@ del s.a # delete s_a Tabii paketi iki kere import edersek paket yalnızca ilk import edildiğinde "__init__.py" dosyası çalıştırılır. - Şimdi mypackage dizini içerisine "__init__.p"y dosyasının yanı sıra "a.py" ve "b.py" dosyalarını da yerleştirelim. + Şimdi mypackage dizini içerisine "__init__.py" dosyasının yanı sıra "a.py" ve "b.py" dosyalarını da yerleştirelim. Dosyaların içeriği şöyle olsun: # a.py @@ -32120,7 +32179,9 @@ del s.a # delete s_a import mypackage print(type(mypackage)) # +#------------------------------------------------------------------------------------------------------------------------ +#------------------------------------------------------------------------------------------------------------------------ Bir paketin içerisindeki spesifik bir dosya da import edilebilir. Örneğin: import mypackage.a @@ -32128,9 +32189,9 @@ del s.a # delete s_a Bu biçimde bir paketin içerisindeki dosyayı import etmeden önce paketin import edilmesine gerek yoktur. Bu tür durumlarda zaten paketin kendisi de otomatik olarak import edilmektedir. Yani yukarıdaki import işleminde sanki önce paketin kendisi import edilmiş sonra da paketin içerisindeki dosya import edilmiş gibi bir etki oluşacaktır. Dolayısıyla yine __init__.py - dosyasının içeriği ve a.py dosyasının içeriği çalıştırılacaktır. Yukarıdaki gibi bir paketin içerisindeki bir module import - edildiğinde iki modül nesnesi oluşturulacaktır. Birinci module nesnesi mypackage ismine atanacak, ikinci module nesnesi ise - mypackage.a ismine atanacaktır. Örneğin: + dosyasının içeriği ve a.py dosyasının içeriği çalıştırılacaktır. Yukarıdaki gibi bir paketin içerisindeki bir modul + import edildiğinde iki modül nesnesi oluşturulacaktır. Birinci module nesnesi mypackage ismine atanacak, ikinci module + nesnesi ise mypackage.a ismine atanacaktır. Örneğin: import mypackage.a import mypackage.b @@ -32176,6 +32237,17 @@ del s.a # delete s_a plt.plot(...) + Paketin içerisindeki bir dosyayı from import deyimiyle de import edebiliriz. Böylece bu ismi daha kolay kullanabiliriz. + Örneğin: + + from mypackage import a + + Burada yine mypackage paketindeki __init__.py dosyası çalıştırılacaktır. Tabii a.py import edildiğine göre onun da içi + çalıştırılacaktır. Ancak burada mypackage ismi artık kullanılmaz fakat a ismi kullanılabilir. Örneğin: + + a.foo() # geçerli + mypackage.a.foo() # error! çünkü mypackage ismini kullanamayız + Paketteki dosyanın içerisinde bulunan spesifik bir değişkeni form import deyimi ile de import edebiliriz. Tabii bu durumda yine paketin __init__.py dosyası ve paket içerisindeki dosyanın içerisindeki kodlar çalıştırılacaktır. Örneğin: @@ -32192,7 +32264,6 @@ del s.a # delete s_a this is mypackage.b module foo bar - #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ @@ -32230,7 +32301,7 @@ del s.a # delete s_a import b # error! Örneğin numpy kütüphanesini import ettiğimizde onun __init__.py dosyasında paket içerisindeki birtakım dosyalar - zaten import edilmektedir. Biz de aşağıdaki gibi işlemler yapabilmekteyiz: + zaten import edilmektedir. Biz de aşağıdaki gibi işlemleri yapabilmekteyiz: import numpy @@ -32241,8 +32312,8 @@ del s.a # delete s_a yapıldığı için biz randint fonksiyonunu numpy.random.randint biçiminde kullanabildik. Bazen programcı paketin __init__.py dosyasında from import deyimi ile ilgili modülün içerisideki değişkenleri paketin - içerisine taşır. Böylece artık yalnızca paket ismi ile o değişkenlere erişilebilir. Örneğin mypackage paketindeki __init__.py - dosyasının içeriği şöyle olsun: + içerisine taşır. Böylece artık yalnızca paket ismi ile o değişkenlere erişilebilir. Örneğin mypackage paketindeki + __init__.py dosyasının içeriği şöyle olsun: # mypackage.__init__.py @@ -32252,8 +32323,8 @@ del s.a # delete s_a from mypackage.b import * Şimdi biz bu paketi import ettiğimizde buradaki from import deyimleri çalıştırılacak (tabii bu deyimler çalışırken a.py - ve b.py dosyalarının içi de çalıştırılşacaktır) ve foo ile b modülünün içerisindeki değişken isimleri sankii bu paketin - içerisindeymiş gibi bir etki olulacaktır. Şimdi paketi aşağıdaki gibi kullanalım: + ve b.py dosyalarının içi de çalıştırılşacaktır) ve foo ile b modülünün içerisindeki tüm değişken isimleri sanki bu paketin + içerisindeymiş gibi bir etki oluşacaktır. Şimdi paketi aşağıdaki gibi kullanalım: import mypackage @@ -32272,20 +32343,20 @@ del s.a # delete s_a mypackage içerisindeymiş gibi kullanılabildiğidir. Bu da kullanım kolaylığı oluşturmaktadır. Burada kullandığımız teknik aslında çok yaygın kullanılmaktadır. Yani biz bir paketi import ettiğimizde o paketin __init__.py - dosyası içerisinde from import deyimleriyle aslında o paketin ieçrisindeki modüllerin (dosyaların) içerisindeki isimler paket - isim alanına taşınmaktadır. Örneğin aslında pandas içerisinde yüzlerce dosyanın olduğu bir pakettir. Ancak biz sanki bütün - isimler pandas isim alanındaymış gibi onları kullanırız. Örneğin: + dosyası içerisinde from import deyimleriyle aslında o paketin içerisindeki modüllerin (dosyaların) içerisindeki isimler + paket isim alanına taşınmaktadır. Örneğin aslında pandas içerisinde yüzlerce dosyanın olduğu bir pakettir. Ancak biz + sanki bütün isimler pandas isim alanındaymış gibi onları kullanırız. Örneğin: import pandas s = pandas.Series([1, 2, 3, 4, 5]) print(s) - Aslında Series sınıfı pandas paketinin içerisindeki bir dosyanın içerisinde bulunmaktadır. Ancak from import deyimi ile pandas - paket isim alanına taşınmıştır. + Aslında Series sınıfı pandas paketinin içerisindeki bir dosyanın içerisinde bulunmaktadır. Ancak pandas dizinindeki + __init__.py dosyasına yerleştirilmiş from import deyimlerisayesinde bu isimler paket isim alanına taşınmıştır. - Yukarıdaki örnekte mypackage paketinin __init__.py dosyasında import as uygulamanın kısaltma bakımından bir faydasının olmayacağına - dikkat ediniz: + Yukarıdaki örnekte mypackage paketinin __init__.py dosyasında import as uygulamanın kısaltma bakımından bir faydasının + olmayacağına dikkat ediniz: # __init__.py @@ -32294,16 +32365,13 @@ del s.a # delete s_a import mypackage.a as a Burada a ismi yine paket ismiyle kullanılmak zorundadır. - -#------------------------------------------------------------------------------------------------------------------------ - 63. Ders 07/12/2022 - Carsamba #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ İç içe paketler de oluşturulabilmektedir. Bir paketin içerisindeki pakete "alt paket (subpackage)" de denilmektedir. Yani paketlerin içerisinde modüller (python dosyaları) olabileceği gibi başka paketler de olabilmektedir. - Şimdi yukarıda oluşturmuş olduğumuz mypackage ismli paketin içerisinde util isimli bir paket daha oluşturalım. util + Şimdi yukarıda oluşturmuş olduğumuz mypackage isimli paketin içerisinde util isimli bir paket daha oluşturalım. util paketinin içerisinde de __init__.py dosyası ve bir de "c.py" dosyası olsun. Bu dosyaların içeriği de şöyle olsun: # mypackage/util/__init__.py @@ -32327,12 +32395,12 @@ del s.a # delete s_a __init__.py c.py - Şimdi bu alt paket içeisindeki "c.py" dosyasını import edecek olalım: + Şimdi bu alt paket içerisindeki "c.py" dosyasını import edecek olalım: import mypackage.util.c Burada her ne kadar biz c modülünü import etmek istiyorsak da aslında mypackage ve util paketleri de import edilmektedir. - Yani burada önce mypackage içerisindkei __init__.py dosyası, sonra "mypackage/util" içerisindeki __init__.py dosyası nihayet + Yani burada önce mypackage içerisindeki __init__.py dosyası, sonra "mypackage/util" içerisindeki __init__.py dosyası nihayet "mypackage/util içerisindeki "c.py" dosyası çalıştırılacaktır. Aynı durum from import deyimi için de geçerlidir: @@ -32340,7 +32408,7 @@ del s.a # delete s_a from mypackage.util.c import tar Tabii biz dış paketin __init__.py dosyasında iç paketlerdeki isimleri de from import deyimi ile dış paketin isim alanına - taşıyabiliriz. Örneğin mypackage paketinin __init__.py dosyası şöyle olsun: + taşıyabiliriz. Örneğin mypackage paketinin __init__.py dosyası şöyle olsun: print('__init__.py') @@ -32348,7 +32416,7 @@ del s.a # delete s_a from mypackage.b import * from mypackage.util.c import * - Burada "a.py", "b.py" ve "c.py" dosyalarının ieçrisindeki isimlerin hepsi sanki mypackage modülünün (paketinin) içerisindeymiş + Burada "a.py", "b.py" ve "c.py" dosyalarının içerisindeki isimlerin hepsi sanki mypackage modülünün (paketinin) içerisindeymiş gibi kullanılabilecektir. Pekiyi iç paketin __init__.py dosyası içerisinde nasıl import işlemi yapabiliriz? Biz iç pakette de import işlemi yaparken @@ -32364,14 +32432,20 @@ del s.a # delete s_a Şöyle import işlemi yapabiliriz: import mypackage.util.c +#------------------------------------------------------------------------------------------------------------------------ +#------------------------------------------------------------------------------------------------------------------------ + Biz bir paketi "pip install" ile indirdiğimizde aslında bu paketin içindeki dosyalar paket isminde bir dizine çekilmektedir. + O dizin de alt dizinlerden yani alt paketlerdne oluşabilmektedir. Ancak paketi oluşturanlar kolat bir kullanım sunabilmek + için __init__.py dosyaları içerisinde from import deyimleri ile alt paket içerisindeki ve alt paket içerisindeki isimleri + asıl paketin isim alanına taşıyabilmektedir. #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ Modüllerin (yani .py dosyalarının) __all__ isminde özel bir global değişkenleri vardır. Bu global değişken dolaşılabilir bir nesne biçiminde olmalıdır. Bu dolaşılabilir nesne string elemanlarından oluşur. Ancak pratikte genellikle bu __all__ değişkeni programcı tarafından bir liste biçiminde oluşturulmaktadır. İşte "from import" deyimi ile *'lı import yapıldığında - yalnızca modülün __all__ değişkeninde belirtilen değişkenler dışarıya import edilir. Örneğin "x.py" isminde bir Python + yalnızca modülün __all__ değişkeninde belirtilen değişkenler dışarıya import edilmektedir. Örneğin "x.py" isminde bir Python dosyamız olsun ve bu dosyanın içeriği aşağıdaki gibi olsun: # x.py @@ -32389,7 +32463,8 @@ del s.a # delete s_a b = 20 c = 30 - Normal olarak biz bu modülü *'lı import ettiğimizde foo, bar, tar, a, b, c değişken isimlerinin hepsini kullanabiliriz. Örneğin: + Normal olarak biz bu modülü *'lı import ettiğimizde foo, bar, tar, a, b, c değişken isimlerinin hepsini dışarıdan doğrudan + kullanabiliriz. Örneğin: from x import * @@ -32419,6 +32494,9 @@ del s.a # delete s_a c = 30 Şimdi biz bu modülü *'lı bir biçimde import edersek yalnızca foo, bar, a ve b'yi kullanabiliriz. + + __all__ global değişkeni yalnızca *'lı "from import" deyiminde etkili olmaktadır. Modül normal olarak import edilirse + bu __all__ değişkeninde belirtilmeyen modül değişkenleri yine modül ismi ile niteliklendirilerek kullanılabilir. #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ @@ -32448,17 +32526,21 @@ def square(a): result = square.__doc__ print(result) - + #------------------------------------------------------------------------------------------------------------------------ - Python'da bazen programcılar bir fonksiyon hakkında bir açıklama bulamayabilirler. Bu tür durumlarda fonksiyonun __doc__ - elemanına başvurulabilir. Örneğin: + 62. Ders 22/02/2025 - Cumartesi +#------------------------------------------------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------------------------------------------------ + Python'da bazen programcılar bir fonksiyon hakkında bir açıklama bulamayabilirler. Bu tür durumlarda fonksiyonun + __doc__ elemanına başvurulabilir. Örneğin: >>> import math >>> math.floor.__doc__ 'Return the floor of x as an Integral.\n\nThis is the largest integer <= x.' - Python'da built-in help isimli fonksiyon bir değişkenin ismini alıp ona ilişkin bilgileri ekrana yazdırmaktadır. Bu yazdırma - sırasında doküman yazıları da yazdırılmaktadır. Örneğin: + Python'da built-in help isimli fonksiyon bir değişkenin ismini alıp ona ilişkin bilgileri ekrana (stdout dosyasın) + yazdırmaktadır. Bu yazdırma sırasında doküman yazıları da yazdırılmaktadır. Örneğin: >>> help(math.floor) Active code page: 65001 @@ -32468,6 +32550,9 @@ print(result) Return the floor of x as an Integral. This is the largest integer <= x. + + help fonksiyonu yalnızca dokğman yazılarını değil birkaç kaynaktan toplayabildiği birtakım açıklama yazılarını da + yazdırmaktadır. #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ @@ -32497,6 +32582,9 @@ print(result) print(s.__doc__) print(sfoo.__doc__) + Bir sınıf için help fonksiyonu kullanılırsa hem sınıfın kendisine iliştirilen doküman yazısı hem de metotlarına ilişkin + dokğman yazıları görüntülenir. + Üç tırnaklı yazıların satırın başından itibaren boşluklu bir yazı oluşturacağına dikkat ediniz. Dolayısıyla ilgili deyimin doküman yazısını __doc__ özniteliği ile eldeettikten sonra bunu yazdırmadan önce baştaki boşlukları atabilirsiniz. Built-in help fonksiyonu zaten bu işlemleri yaparak doküman yazılarını düzgün bir biçimde yazdırmaktadır. @@ -32542,6 +32630,9 @@ print(s.bar.__doc__) print(x.__doc__) Kendi modülümüzün doküman yazısını ise doğrudan __doc__ değişkeni ile yazdırabiliriz. + + Yine help fonksiyonuyla modüle ilişkin bilgileri yazdırabiliriz. help fonksiyonu modülün doküman yazılarını da + görüntüleyecektir. #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ @@ -32558,21 +32649,21 @@ print(s.bar.__doc__) #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ - Bu bölümde gittikçe daha fazla kullanılır hale gelmekte olan "tür açıklamaları (type annotations)" konusu üzerinde - duracağız. Tür açıklamaları konusu python'un 3'lü versiyonlarıyla birlikte eklenmiş olan bir özelliktir. Bu özellik - Python2ın neredeyse her sürümünde genişletile genişletile bugünkü haline gelmiştir. Dolayısıyla kursumuzda güncel - son versiondaki tür açıklamaları üzerinde duracağız. + Bu bölümde gittikçe daha fazla kullanılır hale gelmekte olan "tür açıklamaları (type annotations / type hinting)" konusu + üzerinde duracağız. Tür açıklamaları konusu python'un 3'lü versiyonlarıyla birlikte eklenmiş olan bir özelliktir. Bu + özellik Python'un neredeyse her sürümünde genişletile genişletile bugünkü haline gelmiştir. Dolayısıyla kursumuzda + güncel son versiyondaki tür açıklamaları üzerinde duracağız. #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ Python dinamik tür sistemine sahip bir programlama dili olduğu için değişkenlerin, fonksiyon parametrelerinin, fonksiyonların - geri dönüş değerlerinin türleri değişebilmektedir. Dinamik tür sistemine sahip programlama dillerinde en önemli sorunlardan - biri tür kontrolünün çalışma zamanı sırasında yapılabilmesidir. Örneğin bir fonksiyon yanlış türden bir argümanla çağrıldığında - problem kod çalışırken akış o noktaya geldiğinde ortaya çıkmaktadır. Bu da kodu yazanın çok dikkatli olmasını gerektirmektedir. - İşte tür açıklamaları bir değişkenin niyet edilen türünün program çalışmadan önce üçüncü parti araçlar tarafından kontrol - edilmesini sağlamak amacıyla dile eklenmiştir. Tür açıklamaları yorumlayıcı için bir direktif ya da kontrol sağlamamaktadır. - Yalnızca insanlar ve üçüncü parti statik analiz araçları için kontrol imkanları sunmaktadır. Başka bir deyişle tür açıklamaları - tamamen yorumlayıcı tarafından görmezden gelinmektedir. + geri dönüş değerlerinin türleri programın çalışma zamanı sırasında değişebilmektedir. Dinamik tür sistemine sahip programlama + dillerinde en önemli sorunlardan biri tür kontrolünün çalışma zamanı sırasında yapılabilmesidir. Örneğin bir fonksiyon + yanlış türden bir argümanla çağrıldığında problem kod çalışırken akış o noktaya geldiğinde ortaya çıkmaktadır. Bu da kodu + yazanın çok dikkatli olmasını gerektirmektedir. İşte tür açıklamaları bir değişkenin niyet edilen türünün program çalışmadan + önce üçüncü parti araçlar tarafından kontrol edilmesini sağlamak amacıyla dile eklenmiştir. Tür açıklamaları yorumlayıcı + için bir direktif ya da kontrol sağlamamaktadır. Yalnızca insanlar ve üçüncü parti statik analiz araçları için kontrol + imkanları sunmaktadır. Başka bir deyişle tür açıklamaları tamamen yorumlayıcı tarafından görmezden gelinmektedir. Aşağıdaki banner fonksiyonuna dikkat ediniz: @@ -32586,9 +32677,9 @@ print(s.bar.__doc__) banner(123) - İşte bu durumda yukarıda da belirttiğimiz gibi kod çalışırken exception oluşacaktır (çünkü int türü len fonksiyonuna sokulamaz). - Eğer biz yukarıdaki fonksiyonu örneğin bir listeyle çağırırsak exception oluşmaz ancak fonksiyon istediğimizi de yapmaz. - Aslında bu durum exception oluşmasından da kötüdür. + İşte bu durumda yukarıda da belirttiğimiz gibi kod çalışırken exception oluşacaktır (çünkü int türü len fonksiyonuna + sokulamaz). Eğer biz yukarıdaki fonksiyonu örneğin bir listeyle çağırırsak exception oluşmaz ancak fonksiyon istediğimizi + de yapmaz. Aslında bu durum exception oluşmasından da kötüdür. Pekiyi biz bir Python programında yukarıdaki gibi hataları nasıl tespit edebiliriz? Bu tür hatalar yazılımın iyi bir biçimde test edilmesiyle büyük ölçüde düzeltilebilmektedir. Örneğin "birim testleri (unit testing)" bu tür işlemlerin @@ -32603,7 +32694,7 @@ print(s.bar.__doc__) pip install mypy Bu analiz araçlarının tür kontrollerini yapabilmesi için kodun "tür açıklamaları (type annotations)" ile oluşturulmuş - olması gerekir. Bu nedennle bizim tür açıklamalarının nasıl oluşturulacağını bilmemiz gerekmektedir. + olması gerekir. Bu nedenle bizim tür açıklamalarının nasıl oluşturulacağını bilmemiz gerekmektedir. #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ @@ -32619,7 +32710,7 @@ print(s.bar.__doc__) a: int = 123 b: float = 12.3 - Yukarıda da belirttiğimiz gibi ':' atomundna sonra tipik olarak bir tür ismi getirilmektedir. Ancak tasarımda daha + Yukarıda da belirttiğimiz gibi ':' atomundan sonra tipik olarak bir tür ismi getirilmektedir. Ancak tasarımda daha genel bir yol izlenmiş ve buradaki ifadenin herhangi bir türden olabilmesi sağlanmıştır. Örneğin: a: 'ankara' = 123 @@ -32630,7 +32721,8 @@ print(s.bar.__doc__) #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ - Global ya da yerel değişkenlere tür açıklaması yazılırken onun ilkdeğer verilerek yaratılması zorunlu değildir. Örneğin: + Global ya da yerel değişkenler için tür açıklaması yapılırken onun ilkdeğer verilerek yaratılması zorunlu değildir. + Örneğin: x: int @@ -32654,8 +32746,8 @@ print(s.bar.__doc__) olmasıdır. Yukarıda da belirttiğimiz gibi bir program çalıştırılırken Python yorumlayıcısı tür açıklamalarını dikkate almaz. Bu tür - açıklamaları üçüncü parti programlar tarafından (örneğin mypy) dikkate alınmaktadır. Yukarıdaki "sample.py" dosyasını mypy - ile şöyle işleme sokabiliriz: + açıklamaları üçüncü parti programlar tarafından (örneğin "mypy") dikkate alınmaktadır. Yukarıdaki "sample.py" dosyasını + mypy ile şöyle işleme sokabiliriz: mypy sample.py @@ -32669,17 +32761,32 @@ print(s.bar.__doc__) sample.py:6: error: Incompatible types in assignment (expression has type "float", variable has type "int") Found 1 error in 1 file (checked 1 source file) - Tabii biz tür açıklaması yaparken genellkle aynı zamanda ona değer atayarak aynı zamanda değişkeni yaratabiliriz. Örneğin: + Tabii biz tür açıklaması yaparken genellkle aynı zamanda ona değer atayarak aynı zamanda değişkeni yaratabiliriz. + Örneğin: x: int = 10 #------------------------------------------------------------------------------------------------------------------------ +#------------------------------------------------------------------------------------------------------------------------ + Tür açıklamasındaki ifade içerisindeki tür isimlerinin daha önceden tanımlanmış olması gerekir. Örneğin: + + int a: Sample + + Burada tür açıklamasında a değişkeninn Sample türündne olması gerektiği belirtilmiştir. Eğer bu noktaya kadar Sample + türü henüz oluşturulmadıysa yorumlayıcı da bu işlemi error olarak değerlendirir. Tabii built-in türler hiçbir import + işlemi yapılmadan da tanımlanmış kabul edilmektedir. Örneğin: + + int a: list + + Burada list built-in bir tür olduğu için bir sorun ortaya çıkmayacaktır. +#------------------------------------------------------------------------------------------------------------------------ + #------------------------------------------------------------------------------------------------------------------------ Tür açıklamaları IDE'lere entegre edilmiş araçlar tarafından da dikkate alınabilmektedir. Örneğin PyCharm IDE'sinde "Code/Inspect Code" menüsü ile tür kontrolü yapılabilir. Mypy aynı zamanda PyCharm IDE'sine bir plugin olarak eklenebilmektedir. - Bunun için "File/Settings/Plugins" sekmesine gelinit. Burada "mypy" seçilerek araç IDE'ye dahil edilir. Visual Studio + Bunun için "File/Settings/Plugins" sekmesine gelinir. Burada "mypy" seçilerek araç IDE'ye dahil edilir. Visual Studio Code IDE'sine de bir plugin olarak mypy eklenebilmektedir. Maalesef henüz tür kontrolü yapmak için Spyder IDE'sine entegre - edilmiş bir araç yoktur. + edilmiş bir araç yoktur. VSCode Editöründe de "mypy" bir plugin biçiminde bulunmaktadır. mypy programı başarısız olursa sıfır dışında bir exit kodu üretmektedir. Başarı durumunda mypy programının exit kodu 0'dır. Örneğin: @@ -32699,6 +32806,13 @@ print(s.bar.__doc__) :\Study\Python>echo %errorlevel% 0 + UNIX/Linux ve macOS sistemlerinde son çalıştırılan programın exit kodu $? ile elde edilebilmektedir. Örneğin: + + $ mypy sample.py + Success: no issues found in 1 source file + $ echo $? + 0 + Böylece build araçları ya da programcının oluşturduğu make dosyaları önce mypy programını çalıştırıp eğer bir hata varsa programı python yorumlayıcına hiç vermeyebilirler. #------------------------------------------------------------------------------------------------------------------------ @@ -32762,7 +32876,7 @@ print(s.bar.__doc__) Found 1 error in 1 file (checked 1 source file) Tabii yukarıdaki örnekten de gördüğünüz gibi eğer tür açıklaması yapılmamışsa değişken yaratılırken mymy ve üçüncü parti - statik analiz araçları herhangi bir hata rapor etmemktedir. Örneğin: + statik analiz araçları herhangi bir hata rapor etmemektedir. Örneğin: def square(a: float) -> float: return a * a @@ -32770,8 +32884,8 @@ print(s.bar.__doc__) result = square(10.2) print(result) - Burada biz result değişkeni şçin bir tür açıklaması yapmadık. Ancak square fonksiyonunun geri dönüş değerinin float türden - olması gerektiğini belirttik. Ancak result değişkenine atama için mypy bir hata rapor etmeyecektir. + Burada biz result değişkeni için bir tür açıklaması yapmadık. Ancak square fonksiyonunun geri dönüş değerinin float türden + olması gerektiğini belirttik. result değişkenine atama için mypy bir hata rapor etmeyecektir. mypy programı da versiyondan versiyona genişletilmektedir. Örneğin artık biz bir değişkene ilk kez değer atayıp onu yarattığımızda mypy sanki o değişken için atanan türe ilişkin tür açıklaması yapılmış gibi işlem uygulamaktadır. Örneğin: @@ -32787,12 +32901,10 @@ print(s.bar.__doc__) sample.py:4: error: Incompatible types in assignment (expression has type "float", variable has type "int") [assignment] Found 1 error in 1 file (checked 1 source file) - #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ - Biz bir değişkenin kendi sınıfımız türünden olmasını da sağlayabiliriz. Yani tür açıklamalarında kendi sınıflarımızı da - kullanabiliriz. Örneğin: + Tür açıklamalarında kendi sınıflarımızı da kullanabiliriz. Örneğin: class Sample: pass @@ -32801,12 +32913,11 @@ print(s.bar.__doc__) print(a) s = Sample() - foo(s) - Burada foo fonksiyonu Sample sınıfı türünden parametre almaktadır. Biz onu başka türden bir argümanla çağırırsak mypy hata - verecektir. Tabii türemiş sınıf taban sınıf gibi de kullanılabildiği için biz buradaki foo fonksiyonuna Sample sınıfından - türetilmiş bir sınıf türünden değişken de geçebiliriz. Örneğin: + Burada foo fonksiyonu Sample sınıfı türünden parametre almaktadır. Biz onu başka türden bir argümanla çağırırsak mypy + hata verecektir. Tabii türemiş sınıf taban sınıf gibi de kullanılabildiği için biz buradaki foo fonksiyonuna Sample + sınıfından türetilmiş bir sınıf türünden değişken de geçebiliriz. Örneğin: class Sample: pass @@ -32818,7 +32929,6 @@ print(s.bar.__doc__) print(a) m = Mample() - foo(m) Burada mypy herhangi bir hata mesajı vermeyecektir. @@ -32871,7 +32981,7 @@ foo(m) Found 1 error in 1 file (checked 1 source file) mypy gibi araçların "statik analiz araçları" olduğuna dikkat ediniz. Bu tür statik kod analizi yapan araçlar programı - çalıştırarak bir kontrol yapamadığı için her türlü ihlali kontrol edememektedir. Örneğin: + çalıştırarak bir kontrol yapamadığı için her türlü ihlali de kontrol edememektedir. Örneğin: def foo(x): x.append(1.2) @@ -32879,23 +32989,21 @@ foo(m) a: list[int] a = [1, 2, 3, 4] - foo(a) - print(a) Burada foo fonksiyonunun a listesine ekleme yaptığını dolayısıyla kuralın ihlal edildiğini mypy gibi statik analiz araçları - genellikle tespit edememektedir. + genellikle tespit edememektedir. Dolayısıyla burada mypy kodda bir ihlal olmadığına yönelik çıktı verecektir. #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ - list, tuple, dict gibi türlerin köşeli parantezlerle tür açıklamalarında kullanılabilmesi Python 3.9 ile eklenmiştir. - Python 3.8 ve öncesinde list, tuple ve dict sınıfları için tür açıklamaları typing modülü içerisisideki List, Tuple, - Dict isimli sınıflar kullanılarak yapılabiliyordu. Ancak Python 3.9 ile birlikte artık bu işlemler için orijinal list, - tuple ve dict sınıfları doğrudan kullanılabilir hale gelmiştir. typeing modülündeki List, Tuple ve Dict sınıfları orijinal - set, tuple ve dict sınıfları değildir. Bunlar yalnızca tür açıklamaları için bulundurulmuş olan sınıflardır. Tabii eski - sistem geçmişe doğru uyumu korumak için typeingmodülündeki bu sınıflar muhafaza edilmektedir. örneğin list sınıfı için - tür açıklamaları aşağıdaki gibi de yapılabilmektedir: + list, tuple, dict gibi türlerin köşeli parantezlerle tür açıklamalarında kullanılabilmesi Python 3.9 (Ekim 2020) ile + eklenmiştir. Python 3.8 ve öncesinde list, tuple ve dict sınıfları için tür açıklamaları typing modülü içerisisideki + List, Tuple, Dict isimli sınıflar kullanılarak yapılabiliyordu. Ancak Python 3.9 ile birlikte artık bu işlemler için + orijinal list, tuple ve dict sınıfları doğrudan kullanılabilir hale gelmiştir. typeing modülündeki List, Tuple ve Dict + sınıfları orijinal set, tuple ve dict sınıfları değildir. Bunlar yalnızca tür açıklamaları için bulundurulmuş olan + sınıflardır. Tabii eski sistem geçmişe doğru uyumu korumak için typing modülündeki bu sınıflar muhafaza edilmektedir. + örneğin list sınıfı için tür açıklamaları aşağıdaki gibi de yapılabilmektedir: from typing import List @@ -32911,11 +33019,10 @@ foo(m) pass foo([10, 20]) - #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ - Benzer biçimde set ve tuple sınıfları için de tür açıklamaları için kullanılabilmektedir. Örneğin: + Benzer biçimde set ve tuple sınıfları için de tür açıklamaları kullanılabilmektedir. Örneğin: def foo(s: set): pass @@ -32933,9 +33040,9 @@ foo(m) def foo(s: set[int]): pass - Burada s int değerleri tutan bir küme olmalıdır. + Burada s int değerleri tutan bir küme türünden olmalıdır. - Demetlerde elemanların türleri sırasıyla tek tek belirtilebilmektedir. Örneğin: + Demetlerde elemanların türleri sırasıyla tek tek belirtilir. Örneğin: def foo(t: tuple[int, str]): pass @@ -32961,7 +33068,7 @@ foo(m) pass Burada a için yapılan açıklama "elemanları int türden olan demet" biçiminde bir açıklama değildir. "Tek bir elemanı olan, - onun da int türden olan demet" açıklamasıdır. + o elemanın da int türden olduğu demet" açıklamasıdır. set ve tuple türlerinin bu biçimde doğrudan kullanılması yine Python 3.9 ile birlikte mümkün hale getirilmiştir. Yukarıda da belirttiğimiz gibi Python 3.8 ve aşağısında bunların yerine typing modülündeki Set ve Tuple sınıfları kullanılıyordu. Ancak @@ -33023,8 +33130,8 @@ foo(m) def foo(d: dict): pass - Bu durumda fonksiyonun d parametre değişkenine bir sözlük geçirilmelidir. Ancak sitenirse yine köşeli parantezler içerisinde - sözlüğün anahtar ve değer türleri ayrı ayrı belirtilebilir. Örneğin: + Bu durumda fonksiyonun d parametre değişkenine bir sözlük geçirilmelidir. Ancak sitenirse yine köşeli parantezler + içerisinde sözlüğün anahtar ve değer türleri ayrı ayrı belirtilebilir. Örneğin: def foo(d: dict[int, str]): pass @@ -33032,10 +33139,9 @@ foo(m) Burada sözlüğün anahtarları int, değerleri ise str türünden olmalıdır. Aşağıdaki çağrıda mypy bir hata rapor etmeyecektir: d = {10: 'ali', 20: 'veli', 30: 'selami'} - foo(d) - Yukarıda da belirttiğimiz gibi Python 3.8 ve öncesinde bu işlem typing modülü içerisindeki Dict sınıfı ile yapılıyordu. + Yukarıda da belirttiğimiz gibi Python 3.9'un öncesinde bu işlem typing modülü içerisindeki Dict sınıfı ile yapılıyordu. Örneğin: from typing import Dict @@ -33061,7 +33167,7 @@ foo(m) #------------------------------------------------------------------------------------------------------------------------ typing modülündeki Any sınıfı tür açıklamalarında "herhangi bir tür olabilir" anlamına gelmektedir. Any kullanmak bazı - durumlarda gereksidir. Örneğin: + durumlarda gereksizdir. Örneğin: def foo(a: Any): pass @@ -33100,7 +33206,7 @@ foo(m) pass Burada parametre değişkeni olan t için bizim üç elemanlı bir demet geçirmemiz gerekir. Bu demetin ilk elemanı int türden, - üçüncü elemanı float türden ancak ikinci elemanı ise herhangi bir türden olabilir. Dolayısıyla aşağıdaki çağrılarda mypy + üçüncü elemanı float türden ancak ikinci elemanı herhangi bir türden olabilir. Dolayısıyla aşağıdaki çağrılarda mypy bir hata rapor etmeyecektir: foo((100, 'ali', 12.4)) @@ -33135,8 +33241,7 @@ foo(m) def foo(a: int) -> None: if a < 0: - return 'a must not be negative' - + return 'a must not be negative' print('ok') Burada mypy şöyle bir hata rapor etmektedir: @@ -33152,10 +33257,8 @@ foo(m) delta = b ** 2 - 4 * a * c if delta < 0: return None - x1 = (-b + math.sqrt(delta)) / (2 * a) x2 = (-b - math.sqrt(delta)) / (2 * a) - return x1, x2 result: tuple[float, float]|None = getroots(1, 0, -4) @@ -33166,12 +33269,11 @@ foo(m) x1, x2 = result print(f'x1 = {x1}, x2 = {x2}') - Burada ikinci derece denklemin köklerini bulan getroots fonksiyonu ya elemanları float olan iki elemanlı bir - demet ile ya da None değeri ile geri dönmektedir. Fonksiyonun anımlamasına dikkat ediniz: + Burada ikinci derece denklemin köklerini bulan getroots fonksiyonu ya elemanları float olan iki elemanlı bir demet + ile ya da None değeri ile geri dönmektedir. Fonksiyonun tanımlamasına dikkat ediniz: def getroots(a: float, b: float, c: float) -> tuple[float, float]|None: - pass - + pass #------------------------------------------------------------------------------------------------------------------------ import math @@ -33208,7 +33310,6 @@ foo(m) def foo(a: int) -> Optional[int]: if a > 0: return a - return None result: Optional[int] = foo(10) @@ -33217,7 +33318,7 @@ foo(m) Burada biz result değişkenine int türden ya da None atayabiliriz. Yukarıda yazmış olduğumuz getroots fonksiyonunda fonksiyonun geri dönüş değeri için tuple[float, float]|None açıklamasını - yapmıştık. Aynı açıklamayı Optional kullnarak Optional[tuple[float, float]] biçiminde de yapabilirdik. + yapmıştık. Aynı açıklamayı Optional kullanarak Optional[tuple[float, float]] biçiminde de yapabilirdik. #------------------------------------------------------------------------------------------------------------------------ import math @@ -33240,10 +33341,10 @@ else: print('Kök yok') #------------------------------------------------------------------------------------------------------------------------ - Bir değişkene bir fonksiyon gibi çağrılabilecek (callable) bir değişken atanacaksa tür açıklamasında typing modülündeki + Bir değişkene bir fonksiyon gibi çağrılabilecek (callable) bir ifade atanacaksa tür açıklamasında typing modülündeki Callable sınıfı kullanılmaktadır. Örneğin: - from typing import Callable + from typing import Callable def foo(f: Callable, *args, **kwargs): f(*args, **kwargs) @@ -33279,7 +33380,7 @@ else: Dolayısıyla burada mypy herhangi bir hata rapor etmeyecektir. Ancak istenirse değişkene atanacak çağrılabilen nesnenin parametrik yapısı ve geri dönüş değeri için de tür açıklaması - yapılabilmektedir. Bunun için Callable siminden sonra köşeli parantezler içerisinde "bir liste biçiminde parametre türleri, + yapılabilmektedir. Bunun için Callable isminden sonra köşeli parantezler içerisinde "bir liste biçiminde parametre türleri, sonra da geri dönüş değerinin türü" girilmelidir. Örneğin: from typing import Callable @@ -33289,7 +33390,7 @@ else: f: Callable[[int, str], int] - Burada f değişkenine parametreleri sırasıyla int ve str olan geri dönüş değeri ise int olan çağrılabilen nesneler atanabilir. + Burada f değişkenine parametreleri sırasıyla int ve str olan, geri dönüş değeri ise int olan çağrılabilen nesneler atanabilir. Eğer buna uygun atama yapılmazsa mypy hata verecektir. Örneğin: f = foo @@ -33367,7 +33468,7 @@ foo(len) # geçerli değil foo(1.2) - Daha önce de belirtitğimiz gibi Python 3.10 ile birlikte Union işlemi '|' operatörü ile de yapılır hale getirilmiştir. + Daha önce de belirttiğimiz gibi Python 3.10 ile birlikte Union işlemi '|' operatörü ile de yapılır hale getirilmiştir. Örneğin: def foo(a: int|str): @@ -33391,50 +33492,54 @@ print(a) a = 'ankara' # geçersiz -#------------------------------------------------------------------------------------------------------------------------ - Bazen bir değişkene ilişkin tür açıklamasını başka bir tür açıklaması ile değiştirmek isteyebiliriz. Aslında ikinci kez - tür açıklaması yapmak Python yorumlayıcısı tarafından geçerlidir. Ancak mypy bu durumda hata rapor etmektedir. Örneğin: - - a: int - - a = 100 - print(a) - - a: float # mypy hata rapor edecektir - - a = 3.14 - print(a) - - Bu durum Python yorumlayıcısı tarafındna geçerli olsa da mypy aşağıdaki gibi bir hata rapor edecektir: - - sample.py:6: error: Name "a" already defined on line 1 [no-redef] - sample.py:8: error: Incompatible types in assignment (expression has type "float", variable has type "int") [assignment] - Found 2 errors in 1 file (checked 1 source file) - - typing modülü içerisindeki cast fonksiyonu atanan değişken üzerinde tür açıklaması yapılmasına olanak sağlayan bir fonksiyondur. - cast fonksiyonu birinci parametre olarak bir tür açıklamasını, ikinci parametre olarak bir değeri almaktadır. Bu değer açıklanmış - bir değişken de olabilir. +#------------------------------------------------------------------------------------------------------------------------ + typing modülü içeisindeki cast fonksiyonu belli bir tür açıklamasına sahip bir değişkene başka türdne bir değerin + atanması için kullanılmaktadır. cast fonksiyonu birinci parametre olarak bir tür açıklamasını, ikinci parametre olarak + bir değeri almaktadır. Bu değer açıklanmış bir değişken de olabilir. Biz cast fonksiyonu sayesinde bir değişkene hem bir açıklama yapıp hem de bir değer arayabiliriz. Örneğin: a: float = 12.2 b = cast(int, a) - Örneğimizde b değişkeni artık int olarak açıklanmış durumdadır. Burada önemli bir nokta cast fonksiyonun bir açıklama - amacıyla kullanılmasıdır. cast fonksiyonu bir dönüştürme yapmaz. Örneğimizde her ne kadar b int olarak açıklanmışsa da içerisinde yine 12.2 - değeri bulunacaktır. Yani biz burada b'ye hem bir float değer atamış olduk hem de a'yı int olarak açıklamış olduk. + cast fonksiyonu aynı zamanda atanan hedef değişkende bir tür açıklaması da oluşturmaktadır. Yukarıdaki örnekte artık + b de int türü olarak açıklanmıştır. Burada önemli bir nokta cast fonksiyonun bir açıklama amacıyla kullanılmasıdır. + cast fonksiyonu bir dönüştürme yapmaz. Örneğimizde her ne kadar b int olarak açıklanmışsa da içerisinde yine 12.2 değeri + bulunacaktır. Yani biz burada b'ye hem bir float değer atamış olduk hem de a'yı int olarak açıklamış olduk. Yorumlayıcı cast işleminde dönüştürme yapmamaktadır. Örneğin: from typing import cast a: float = 12.2 - b = cast(str, a) - print(b, type(a)) # 12.2 Burada b str olarak açıklanmıştır ancak b içerisine float bir değer atanmıştır. + + Pekiyi cast fonksiyonu tam olarak ne amaçla kullanılmaktadır? İşte bazen bri değişken belli bir türle açıklanmış + olabilir. Biz de ona başka bir türü atamak isteyebiliriz. Bu durumda mypy gibi araçların sorun bildirmemesi için + bu cast fonksiyonundan faydalanabiliriz. Örneğin: + + def foo(a: str): + print(b) + + Burada foo fonksiyonuna str türünden bir argüman geçirilmelidir. Eğer foo fonksiyonuna str türünden bir argüman + geçirilmezse mypy gibi araçlar sorun bildircektir. Örneğin bu fonksiyonu şöyle çağırmış olalım: + + foo(123) + + Burada mypy aşağıdaki gibi sorun bildirmektedir: + + sample.py:7: error: Argument 1 to "foo" has incompatible type "int"; expected "str" [arg-type] + Found 1 error in 1 file (checked 1 source file) + + Pekiyi biz hem bu atamayı yapmak isteyelim hem de bu mypy gibi araçların sorun bildirmemesini isteyelim. İşte bu + durumda cast fonksiyonundan faydalanırız: + + foo(cast(str, 123)) + + Artık mypy bir sorun bildirmeyecektir. #------------------------------------------------------------------------------------------------------------------------ from typing import cast @@ -33504,8 +33609,8 @@ foo(123) # geçerli değil foo([1, 2, 3, 4, 5]) - Çünkü fonksiyona geçirilen dolaşılabilir nesnenin elemanları int türdendir. Ancak aşağıdaki bir çağrı tür açıklamasına uygun - değildir: + Çünkü fonksiyona geçirilen dolaşılabilir nesnenin elemanları int türdendir. Ancak aşağıdaki bir çağrı tür açıklamasına + uygun değildir: foo([1, 2, 3., 4, 5.0]) @@ -33513,8 +33618,8 @@ foo(123) # geçerli değil a: Iterable[int|str] - Burada a değişkenine elemanları int ya da float olabilen dolaşılabilir nesneler atanabilir. Dolayısıyla aşağıdaki atama tür - açıklamasına uygundur: + Burada a değişkenine elemanları int ya da float olabilen dolaşılabilir nesneler atanabilir. Dolayısıyla aşağıdaki atama + tür açıklamasına uygundur: a = [1, 2, 3, 4, 'ankara', 'izmit'] @@ -33522,39 +33627,50 @@ foo(123) # geçerli değil a: Iterable[tuple[int, str]] - Burada a değişkenine demetlerden oluşan dolaşılabilir bir nesne atanabilir. Ancak bu demetlerin de ilk elemanları int ikinci elemanları - str türünden olmak zorundadır. Örneğin aşağıdaki atama tür açıklamasına uygundur: + Burada a değişkenine demetlerden oluşan dolaşılabilir bir nesne atanabilir. Ancak bu demetlerin de ilk elemanları + int ikinci elemanları str türünden olmak zorundadır. Örneğin aşağıdaki atama tür açıklamasına uygundur: a = [(6, 'Anakara'), (26, 'Eskişehir'), (35, 'İzmir')] #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ - Tür açıklamaları iç içe yapıldığı zaman biraz karmaşık bir görüntü oluşturabilmektedir. Bu biçimdeki tür açıklamalarını oluştururken - parantezlere dikkat ediniz. Örneğin: + Tür açıklamaları iç içe yapıldığı zaman biraz karmaşık bir görüntü oluşturabilmektedir. Bu biçimdeki tür açıklamalarını + oluştururken parantezlere dikkat ediniz. Örneğin: from typing import Callable, Iterable def foo(a: Iterable[tuple[Callable[[int, int], int], float]]): pass - Burada a değişkeninin parametresi dolaşılabilir bir nesne olmalıdır. Bu dolaşılabilir nesne bize iki elemanlı demet vermelidir. - Demetin birinci elemanı parametreleri int, int olan, geri dönüş değeri int olan bir çağrılabilir nesneden ikinci elemanı ise - float bir nesneden oluşmalıdır. + Burada a değişkeninin parametresi dolaşılabilir bir nesne olmalıdır. Bu dolaşılabilir nesne bize iki elemanlı demet + vermelidir. Demetin birinci elemanı parametreleri int, int olan, geri dönüş değeri int olan bir çağrılabilir nesneden + ikinci elemanı ise float bir nesneden oluşmalıdır. Örneğin: + + def bar(a: int, b: int) -> int: + return 0 + + def tar(a: int, b: int) -> int: + return 0 + + a = [(bar, 3.4 ), (tar, 4.34)] + foo(a) + + Buarad foo fonksiyonuna geçirilen argüman için mypy bir sorun bildirmeyecektir. #------------------------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------------------------ - typing modülü içerisindeki Sequence sınıfı da string gibi liste gibi __getitem__, __len__ metotları bulunan "reversible - seqeunce" türlerini belirtmek için kullanılmaktadır. Anımsanacağı gibi tipi tipik "seuqence" türleri list, range, tuple, - str" türleridir. dict türünün __getitem__ metodu olsa da dict türü bir "seauence türü değildir. Örneğin: + seqeunce" türlerini belirtmek için kullanılmaktadır. Anımsanacağı gibi tipik "seuqence" türleri list, range, tuple, + str" türleridir. dict türünün __getitem__ metodu olsa da dict türü bir "sequence türü değildir. Sequence ile açıklama + yaparken köşeli parantez içerisinde o türün tuttuğu nesnelerin türleri de belirtilebilmektedir. Örneğin: from typing import Sequence - def foo(a: Sequence[int]): + def foo(a: Sequence): pass - Burada biz foo fonksiyonunu bir listeyle, bir demetle, string'le ya da bir range nesnesiyle çağırabiliriz. Bu durumda tür - açıklamasına uygun çağrılar oluşturulmuş oluruz. Örneğin: + Burada biz foo fonksiyonunu bir listeyle, bir demetle, string'le ya da bir range nesnesiyle çağırabiliriz. Bu durumda + tür açıklamasına uygun çağrılar oluşturulmuş oluruz. Örneğin: foo([1, 2, 3, 4, 5]) foo('ali') @@ -33570,7 +33686,7 @@ foo(123) # geçerli değil def foo(a: Sequence[int]): pass - Burada a parametre değişkenine biz int elemanlardna oluşan bir "sequence türü" geçirebiliriz. Örneğin: + Burada a parametre değişkenine biz int elemanlardan oluşan bir "sequence türü" geçirebiliriz. Örneğin: foo([1, 2, 3, 4, 5]) foo(range(100)) @@ -33581,3 +33697,11 @@ foo(123) # geçerli değil çağrısı tür açıklamasına uygun değildir. #------------------------------------------------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------------------------------------------------ + Python'daki tür açıklamaları için birkaç PEP dokğmanı oluşturulmuştur. Bu dokümanları da gözden geçirebilirsiniz: + + https://peps.python.org/pep-0484/ + https://peps.python.org/pep-0526/ + https://peps.python.org/pep-0593/ +#------------------------------------------------------------------------------------------------------------------------#------------------------------------------------------------------------------------------------------------------------ \ No newline at end of file diff --git a/Unix-Linux-SysProg-OzetNotlar-Ornekler.txt b/Unix-Linux-SysProg-OzetNotlar-Ornekler.txt index 90843f5..cdc7e70 100644 --- a/Unix-Linux-SysProg-OzetNotlar-Ornekler.txt +++ b/Unix-Linux-SysProg-OzetNotlar-Ornekler.txt @@ -11,7 +11,7 @@ (Notları sabit genişlikli font kullanan programlama editörleri ile açınız.) (Editörünüzün "Line Wrapping" özelliğini pasif hale getiriniz.) - Son Güncelleme: 22/02/2025 - Cumartesi + Son Güncelleme: 25/02/2025 - Salı ---------------------------------------------------------------------------------------------------------------------------*/ @@ -52546,21 +52546,26 @@ void foo() ---------------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------------------------------------- - Kernel modüllerinde ve aygıt sürücülerde her türlü fonksiyon kullanılamaz. Bunları yazabilmek için özel başlık dosyalarına + Çekirdek modüllerinde ve aygıt sürücülerde her türlü fonksiyon kullanılamaz. Bunları yazabilmek için özel başlık dosyalarına ve ve amaç dosyalara gereksinim duyulmaktadır. Bu nedenle ilk yapılacak şey bu başlık dosyalarının ve kütüphanelerin ilgili sisteme yüklenmesidir. - Genellikle bir Linux sistemini yüklediğimizde zaten kernel modüllerini ve aygıt sürücüleri oluşturabilmek için gereken - kütüphaneler ve başlık dosyaları zaten yüklü biçimde bulunmaktadır. Tabii programcı kernel kodlarını da kendi makinesine - indirmek isteyebilir. Bunun için aşağıdaki komut kullanılabilir: + Genellikle bir Linux sistemini yüklediğimizde zaten çekirdek modüllerini ve aygıt sürücüleri oluşturabilmek için gereken + başlık dosyaları ve diğer gerekli öğeler zaten "/usr/src" dizini içerisindeki "linux-headers-$(uname -r)" dizininde yüklü + biçimde bulunmaktadır. Ancak bunlar yüklü değilse Debian tabanlı sistemlerde bunları şöyle yükleyebilirsiniz: + + $ sudo apt install linux-headers-$(uname -r) + + Tabii programcı o anda çalışılan çekirdeğin kodlarının hepsini de kendi makinesine indirmek isteyebilir. Bunun için aşağıdaki + komut kullanılabilir: $ sudo apt-get install linux-source - Eğer sisteminizde Linux'un kaynak kodları yüklü ise bu kaynak kodlar "/usr/src" dizininde bulunacaltır. Bu dizindeki - "linux-headers-$(uname -r)" dizini kaynak kodlar yüklü olmasa bile bulunan bir dizindir ve bu dizin çekirdek modülleri - ve aygıt sürücülerin "build edilmeleri" için gereken başlık dosyalarını ve bazı Maake dosyalarını barındırmaktadır. Benzer - biçimde "/lib/modules/$(uname -r)" isimli dizinde "build" isimli bir dizin vardır. Bu "build" dizini de genellikle zaten - "usr/src" dizinindeki "linux-headers-$(uname -r)" dizinine sembolik link yapılmıştır. + Bu indirmeler "/usr/src" dizinine yapılmaktadır. + + Ayrıca "/lib/modules/$(uname -r)" isimli dizindeki "build" isimli dizin de çekirdek kaynak kodlarının bulunduğu dizine ya da + aygıt sürücülerin derlenmesi için gereken öğelerin bulunduğu dizine (tipik olarak "linux-headers-$(uname -r)" dizinine) + sembolik link yapılmış durumdadır. ---------------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------------------------------------- @@ -52580,33 +52585,49 @@ void foo() ---------------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------------------------------------- - Mademki çekirdek modülleri işletim sisteminin çekirdek kodlarındaki fonksiyonları ve nesneleri kullanabiliyor o zaman çekirdek - modülleri o anda çalışılan çekirdeğin yapısına da bağlı durumdadır. Bu nednele işletim sistemlerinde "çekirdek modülü yazmak" - ya da "aygıt sürücü yazmak" biçiminde genel bir konu yoktur. Her işletim sisteminin çekirdek modül ve aygıt sürücü mimarisi - diğerlerinden farklıdır. Dolayısıyla bu konu spesifik bir işletim sistemi için geçerli olabilecek oldukça platform bağımlı - bir konudur. Hatta işletim sistemlerinde bazı versiyonlarda genel aygıt sürücü mimarisi bile değiştirilebilmektedir. Dolayısıyla - eski aygıt sürücüler yeni versiyonlarda, yenileri de eski versiyonlarda çalışamamaktadır. Örneğin Linux'ta çekirdek versiyonları - arasında çekirdekteki export edilmiş bazı fonksiyonlar isim ya da parametrik yapıolarak dğeiştirilmiş durumdadır. Bu nedenle - Linux çekirdeğinin belli bir versiyonu için yazılmış aygıt sürücüler başka bir versiyonunda geçersiz hale gelebilmektedir. + Mademki çekirdek modülleri işletim sisteminin çekirdek kodlarındaki fonksiyonları ve nesneleri kullanabiliyor o zaman + çekirdek modülleri o anda çalışılan çekirdeğin yapısına da bağlı durumdadır. Bu nedenle işletim sistemlerinde "çekirdek + modülü yazmak" ya da "aygıt sürücü yazmak" biçiminde genel bir konu yoktur. Her işletim sisteminin çekirdek modül ve aygıt + sürücü mimarisi diğerlerinden farklıdır. Dolayısıyla çekirdek modüllerinin ve aygıt sürücülerinin yazılması spesifik bir + işletim sistemi için geçerli olabilecek platform oldukça bağımlı bir konudur. Hatta işletim sistemlerinde bazı versiyonlarda + genel aygıt sürücü mimarisi bile değiştirilebilmektedir. Dolayısıyla bu tür durumlarda eski aygıt sürücüler yeni versiyonlarda, + yenileri de eski versiyonlarda çalışamamaktadır. Örneğin Linux'ta çekirdek versiyonları arasında çekirdekteki export edilmiş + bazı fonksiyonlar isim ya da parametrik yapı olarak değiştirilmiş durumdadır. Bu nedenle Linux çekirdeğinin belli bir + versiyonu için yazılmış olan aygıt sürücüler başka bir versiyonunda geçersiz hale gelebilmektedir. ---------------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------------------------------------- - Kernel modüllerinin ve aygıt sürücülerin yazımı için programcının genel olarak kernel yapısını bilmesi gerekmektedir. - Çünkü bunları yazarken kernel'ın içerisindeki export edilmiş fonksiyonlar kullanılmaktadır. Linux kernel modüller ve aygıt - sürücüler hakkında yazılmış birkaç kitap vardır. Bunların en klasik olanı "Linux Device Drivers (3. Edition)" kitabıdır. - Bu konudaki resmi dokümanlar kernel.org sitesindeki "documentation" kısmında bulunmaktadır. + Çekirdek modüllerinin ve aygıt sürücülerin yazımı için programcının çekirdek yapısını ana hatlarıyla bilmesi gerekmektedir. + Çünkü bunları yazarken çekirdeğin içerisindeki çeşitli veri yapıları ve export edilmiş fonksiyonlar kullanılmaktadır. + + Linux çekirdek modülleri ve aygıt sürücüleri hakkında yazılmış birkaç kitap vardır. Bunların en klasik olanı "Linux Device + Drivers (3. Edition)" kitabıdır. Ancak bu kitaptaki bazı içerikler güncel çekirdeklerle uyumsuz hale gelmiştir. Bu konudaki + resmi dokümanlar ise "kernel.org" sitesindeki "documentation" kısmında bulunmaktadır. ---------------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------------------------------------- Bir çekirdek modülünü derlemek ve link etmek maalesef sanıldığından daha zordur. Her ne kadar çekirdek modülleri ELF object dosyaları biçimindeyse de bunlarda özel bazı "bölümler (sections)" bulunmaktadır. Dolayısıyla bu modüllerin derlenmesinde - özel gcc seçenekleri devreye sokulmaktadır. Çekirdek modüllerinin link edilmeleri de bazı kütüphane dosyalarının devreye + özel "gcc" seçenekleri devreye sokulmaktadır. Çekirdek modüllerinin link edilmeleri de bazı kütüphane dosyalarının devreye sokulmasıyla yapılmaktadır. Dolayısıyla bir çekirdek modülünün manuel biçimde "build edilmesi" için bazı ayrıntılı bilgilere - gereksinim duyulmaktadır. İşte çekirdek tasarımcıları bu sıkıcı işlemleri kolaylaştırmak için özel "make dosyaları" düzenlemişlerdir. - Programcı bu make dosyalarından faydalanarak build işlemini çok daha kolay yapabilmektedir. Aslında çekirdek modüllerinin - build edilmesinde çekirdeğin KBuild sistemi devreye sokulmaktadır. Bu nedenle çekirdek modüllerinin build edilmesi için - çekirdek kaynak kodlarındaki birtakım başlık dosyalarının ve Make dosyalarının build işleminin yapılacağı makinede bulunması - gerekir. Biz yukarıda bu araçlara "/lib/modules/$(uname -r)/build" dizini yoluyla erişilebileceğini belirtmiştik. + gereksinim duyulmaktadır. İşte çekirdek modüllerinin build edilmesinde çekirdeğin KBuild sistemi devreye sokulmaktadır. Bu + nedenle çekirdek modüllerinin build edilmesi için çekirdek kaynak kodlarındaki birtakım başlık dosyalarının ve Make dosyalarının + build işleminin yapılacağı makinede bulunması gerekir. Biz yukarıda bu dosyalara "/lib/modules/$(uname -r)/build" dizini + yoluyla erişilebileceğini belirtmiştik. Bu dizin aslında Linux kaynak kod ağacının bulunduğu dizini belirtmektedir. Ancak + yukarıda da belirttiğimiz gibi çekidek modüllerinin ve aygıt sürücülerin derlenmesi için Linux'ın tüm kaynak kodlarına + gerek yoktur. Yalnızca başlık dosyaları ve make dosyalarının bulunması yeterlidir. +---------------------------------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------------------------------- + Çekirdek modülleri o anda çalışılan host sistem için derlenebileceği gibi gömülü bir sistem için de derlenebilir. Eğer + derleme gömülü sistem için yapılacaksa süphesiz çapraz derleyicilerin de ilgili sistemde kurulu olması gerekir. Yukarıda + da belirttiğimiz gibi çekirdek modüllerinin derlenmesi için ilgili çekirdeğe yönelik başlık dosyaları ve çeşitli make + dosyaları gibi bazı öğelerin de bulunuyor olması gerekir. Eğer derleme bir gömülü sistem için yapılacaksa o gömülü sistemdeki + çekirdeğe ilişkin bu dosyalar da host makinede bulunuyor olmalıdır. Örneğin biz masaüstü bilgisayardaki Mint dağıtımında + çalışıyor olalım. Bu sistemin kendisi için çekirdek modülü derleyeceksek zaten tüm gerkeli öğeler hazır durumdadır. + Ancak biz bu makinede BeagleBone Black için çekirdek modülü ve aygıt sürücü derlemesi yapacaksak çapraz derleyicimizin ve + BeagleBone Black'teki çekirdeğe yönelik temel başlık dosyalarının ve Make dosyalarının bulunuyor olamsı gerekecektir. Tabii + BeagleBone Black'teki çekirdek sürümüneilişkin çekirdek kaynak kodları bu makineye çekerek bu gereksinimi karşılayabiliriz. ---------------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------------------------------------- @@ -52620,14 +52641,29 @@ void foo() make -C /lib/modules/$(shell uname -r)/build M=${PWD} clean Burada önce "/lib/modules/$(uname -r)/build" dizinindeki "Makefile" dosyası çalıştırılmış ondan sonra çalışma bu yazdığımız - make dosyasından devam ettirilmiştir. Özetle bu make dosyası aslında çekirdeğin buil sistemini kullanarak "generic.c" isimli - dosyanın derlenmesini ve çekirdek modülü biçiminde link edilmesini sağlamaktadır. Çekirdek modül birden fazla kaynak dosyadan - oluşturulabilir. Bu durumda ilk satır şöyle oluşturulabilir: + make dosyasından devam ettirilmiştir. Özetle bu make dosyası aslında çekirdeğin build sistemini kullanarak "generic.c" isimli + dosyanın derlenmesini ve çekirdek modülü biçiminde link edilmesini sağlamaktadır. Bu make dosyasını şöyle de düzenleyebilirdik: + + obj-m += generic.o + + KDIR := /lib/modules/$(shell uname -r)/build + PWD := $(shell pwd) + + all: + make -C ${KDIR} M=${PWD} modules + clean: + make -C ${KDIR} M=${PWD} clean + + Çekirdek modül birden fazla kaynak dosyadan oluşturulabilir. Bu durumda ilk satır şöyle oluşturulabilir: obj-m += a.o b.o c.o... - Ya da bu belirtme işlemi ayrı satır halinde de yapılabilir: + Eğer bu dosyaları birden fazla satırda ayrı ayrı belirtirsek bu durumda birden fazla modül dosyası oluşturulacaktır: + obj-m += a.o b.o c.o... + + Eğer bu dosyaları birden fazla satırda ayrı ayrı belirtirsek bu durumda birden fazla modül dosyası oluşturulur: + obj-m += a.o obj-m += b.o obj-m += c.o @@ -52638,8 +52674,8 @@ void foo() $ make -C /lib/modules/$(shell uname -r)/build M=${PWD} modules make programının -C seçeneği Makefile dosyasını aramadan önce bu seçeneğin argümanında belirtilen dizine geçiş yapmaktadır. - Dolayısıyla aslında yukarıdaki satırla /lib/modules/$(shell uname -r)/build dizinindeki Makefile dosyası çalıştırılacaktır. - Make dosyasının başındaki kısım aslında standart bir make yönergesi değildir. Ana make dosyası bu dosyayı ele almaktadır. + Dolayısıyla aslında yukarıdaki satırla "/lib/modules/$(shell uname -r)/build" dizinindeki Makefile dosyası çalıştırılacaktır. + Buradaki M=${PWD} derlenecek kaynak dosyaların o anda ---------------------------------------------------------------------------------------------------------------------------*/ # Makefile @@ -52652,10 +52688,37 @@ clean: make -C /lib/modules/$(shell uname -r)/build M=${PWD} clean /*-------------------------------------------------------------------------------------------------------------------------- - Tabii aslında make dosyası parametrik biçimde de oluşturabilmektedir. Bu durumda make programı çalıştırılırken bu parametrenin - değeri de belirtilmelidir. Örneğin: + Çekirdek modüllerini va aygıt sürücüleri derlerken iki noktaya dikkat etmelisiniz: - $ make file=hellomodule + 1) Kullandığınız çekirdek kodları hedef mekinenin çekirdek sürümüne uygun olmalıdır. Eğer bu koşul sağlanmazsa çekirdekler + arasında farklılıklar söz konusu olabileceği için derlenmiş olan çekirdek modül dosyası hedef sisteme başarılı bir biçimde + yüklenemeyebilir. Tabii Linux çekirdeğindeki değişiklikler daha önce yazılmış olan her çekirdek modülünü ve aygıt sürücüyü + geçersiz hale getirmemektedir. Örneğin minör numara değişikliklerinde genellikle bir sorun oluşmamaktadır. Ancak ne olursa + olsun derleme yapılırken hedef sistemdeki çekirdeğe uygun kaynak dosyaların kullanılması şiddetle tavsiye edilmektedir. + Örneğin biz 6.9.2 çekirdeğinde çalışan makine için aygıt sürücüsü yazacaksak derleme yaptığımız makinede kullanacağımız + çekirdek kaynak kodlarının da bu 6.9.2 çekirdeğine ilişkin olması gerekir. Biz eski bir çekirdeğin kaynak kodlarıyla yeni + bir çekirdek için aygıt süsürücü derlemeye çalışmamalıyız. Tabii eski bir veriyon kullanılarak derleme yapılırsa çoğu + durumda bir sorun ortaya çıkmayabilecektir. Ancak sorunun ortaya çıkma olsılığı da vardır. + + 2) Kullanılan araç zincirinin de (yani derleyici, linker gibi programların da) çekirdeğin derlenmiş olduğu sistemle uyumlu + olmasına dikkat ediniz. Eğer bu temel araçların versiyonlarında geçmişe doğru uyumu bpzabilecek değişiklikler söz konusuysa + yine derleme işlemi başarısız olabilir ya da çekirdek modülü yüklenirken sorun oluşabilir. Aslında çekirdeğin KBuild sistemi + çekirdek konfigürasyon dosyası yoluyla bu kontrolü yapabilmektedir. Ancak bu kontrol bypass da edilebilmektedir. +---------------------------------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------------------------------- + Tabii aslında make dosyası parametrik biçimde de oluşturabilmektedir: + + obj-m += $(file).o + + all: + make -C /lib/modules/$(shell uname -r)/build M=${PWD} modules + clean: + make -C /lib/modules/$(shell uname -r)/build M=${PWD} clean + + Bu durumda make programı çalıştırılırken bu parametrenin değeri de belirtilmelidir. Örneğin: + + $ make file=helloworld ---------------------------------------------------------------------------------------------------------------------------*/ # Makefile @@ -52671,6 +52734,8 @@ clean: Şimdi en basit bir kernel modülü oluşturup bunu bir başlangıç noktası olarak kullanalım. Bu modülümüze "helloworld" ismini verelim: + /* helloworld.c */ + #include #include @@ -52690,25 +52755,30 @@ clean: Bu kernel modül aşağıdaki gibi build edilebilir: - $ make file=helloworld + $ make file=helloworld" - Build işlemi bittiğinde kernel modül "helloworld.ko" dosyası biçiminde oluşturulacaktır. Burada "ko" uzantısı "kernel object" - sözcüklerinden kısaltılmıştır. + Build işlemi bittiğinde kernel modül "helloworld.ko" dosyası biçiminde oluşturulacaktır. Burada "ko" uzantısı "kernel + object" sözcüklerinden kısaltılmıştır. ---------------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------------------------------------- - Bir kernel modül, kernel'ın içerisine "insmod" isimli programla yerleştirilmektedir. Tabii bu programın sudo ile "root" + Bir çekirdek modülü, çekirdeğin içerisine "insmod" isimli programla yerleştirilmektedir. Tabii bu programın sudo ile "root" önceliğinde çalıştırılması gerekmektedir. Örneğin: $ sudo insmod helloworld.ko - Artık kernel modülümüz kernel'ın içerisine yerleştirilmiştir. Yani modülümüz adeta kernel'ın bir parçası gibi işlev görecektir. + Artık çekirdek modülümüz çekirdeğin içerisine yerleştirilmiştir. Yani modülümüz adeta çekirdeğinbir parçası gibi işlev + görecektir. - Kernel modüller istenildiği zaman "rmmod" isimli programla kernel'dan çıkartılabilirler. Bu programın da yine sudo ile - "root" önceliğinde çalıştırılması gerekir. Örneğin: + Çekirdek modülleri istenildiği zaman "rmmod" isimli programla çekirdekten çıkartılabilirler. Bu programın da yine sudo + ile "root" önceliğinde çalıştırılması gerekir. Örneğin: $ sudo rmmod helloworld.ko + rmmod komutu kullanılırken ".ko" dosya uzantısı da belirtilmeyebilir. Örneğin: + + $ sudo rmmod generic + Aşağıda örnek için gerekli olan dosyalar verilmiştir. make işlemi şöyle yapılabilir: $ make file=helloworld @@ -52742,6 +52812,40 @@ all: clean: make -C /lib/modules/$(shell uname -r)/build M=${PWD} clean +/*-------------------------------------------------------------------------------------------------------------------------- + Modüller "modprobe" isimli programla da yüklenebilir. Ancak modprobe programı yüklenecek modülleri "/lib/modules/$(uname -r)" + dizininde aramaktadır. Dolayısıyla biz kendi derlediğimiz modülleri bu dizine yerleştirmemişsek yüklemeyi modprobe ile + yapamayız. Ancak çekirdek derlenirken oluşturulmuş olan modüller bu dizinde olduğu için bunları modprobe ile yükleyebiliriz. + modprobe programı yüklenecek aygıt sürücünün yalnızca ismini almaktadır. Çünkü zaten arama işlemini kendisi yapmaktadır. + Örneğin: + + $ modprobe g_ether + + insmod programının yüklenecek aygıt sürücü dosyasının tüm yol ifadesini aldığına dikkat ediniz. modprobe programında dosyanın + ".ko" uzantısı da belirtilmemektedir. Halbuki insmod programında bu uazantının da belirtilmesi gerekmektedir. modprobe aslında + "modules.dep" isimi bir dosyaya başvurmaktadır. Bu dosya çekirdek kaynak kodlarının kök dizininde çekirdek modülleri derlenirken + oluşturulmaktadır. Bu dosya içerisinde bağımlılık bilgileri vardır. Bir çekirdek modülü yazılırken başka bir çekirdek modülünün + içerisindeki fonksiyonlar kullanılmış olabilir. Bu durumda kullanan modülün yüklenmesi için önce onun kullandığı modülün + yüklenmesi gerekir. İşte bu biçimde durum karmaşık bir hal alabilmektedir. "modules.dep" dosyası içerisinde bir modülün + yüklenebilmesi için hangi modüllerin de yüklenmesi gerektiği bilgileri bulunmaktadır. Eğer biz kendi çekirdek modülümüzün de + modprobe ile yüklenmesini istiyorsak önce onu "/lib/modules/$(uname -r)/kernel" dizininin içerisindeki dizinlerden birine + yerleştirip sonra bu "modules.dep" dosyasının güncllenmesini sağlamamız gerekir. Bu işlem "depmod" programıyla "-a" seçeneği + kullanılarak yapılmaktadır: + + $ sudo depmod -a + + Kendi çekirdek modülünüzü ya da aygıt sürücünüzü örneğin "/lib/modules/$(uname -r)/kernel/drivers/misc" dizinine yerleştirebilirsiniz. + Tabii aygıt sürücü geliştirirken ikide bir modülü buraya yerleştirmenin bir anlamı yoktur. Bu nedenle geliştirme aşamasında + genellikle "insmod" programı kullanılmaktadır. + + modprobe ile yüklenen aygıt sürücü "modeprobe -r" ile boşaltılabilir. Örneğin: + + $ modprobe -r g_ether + + Tabii boşaltım sırasında yine eğer aygıt sürünün bağımlı olduğu çekirdek modülleri başka modüller tarafından kullanılmıyorsa + onlar da çekirdekten çıkartılacaktır. +---------------------------------------------------------------------------------------------------------------------------*/ + /*-------------------------------------------------------------------------------------------------------------------------- 128. Ders 17/03/2024 - Pazar ---------------------------------------------------------------------------------------------------------------------------*/ @@ -72544,6 +72648,11 @@ int main(void) Birinci indirmede tek bir DLL elde edilecektir. İkinci indirmede de "sqlite3.h" başlık dosyası ve kaynak dosyası elde edilecektir. + Ayrıca SQlite için "sqlite3" isminde komut satırından kullanılan bir program da bulundurulmuştur. Bu programın kullanımına + ilişkin bilgileri aşağıdaki bağlantıdan edinebilirsiniz: + + https://www.sqlite.org/cli.html + Birinci indirmede Windows için gereken sqlite3.dll ve sqlite3.def dosyaları elde edilir. Buradaki “.def” dosyasına “module definition file” denilmektedir. Bu dosya “DLL’in import kütüphanesi” gibi link aşamasına dahil edilebilir. Ya da istenirse aşağıdaki komutla bu “.def” dosyasından “.lib” uzantılı “import kütüphanesi de oluşturulabilmektedir: @@ -72551,13 +72660,198 @@ int main(void) LIB /DEF:sqlite3.def /machine:x86 Buradaki machine argümanı hedef sistemi belirtmektedir. Burada 32 bit Windows sistemleri için x86, 64 bit Windows - sistemleri için x64 kullanılmalıdır. İkinci indirmeden biz SQLite’ın kaynak dosyalarını elde ederiz. Buradaki “sqlite3.h” - dosyası SQLite fonksiyonları için başlık dosyası niteliğindedir. + sistemleri için "x64" kullanılmalıdır. Örneğin: + + LIB /DEF:sqlite3.def /machine:x64 + + İkinci indirmeden biz SQLite’ın kaynak dosyalarını elde ederiz. Buradaki “sqlite3.h” dosyası SQLite fonksiyonları için + başlık dosyası niteliğindedir. Mac OS X için kurulum Windows’takine benzemektedir. Yine ilgili “.zip” dosyaları indirilip kurulum yapılabilir. Bu sistemlerde derleme yaparken link aşamasında "-lsqlite3" lie kütüphane dosyasını belirtmeyi unutmayınız. ---------------------------------------------------------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------------------------------------------------------- + 200. Ders 23/02/2025 - Pazar +---------------------------------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------------------------------- + Kurulum sonrası her şeyin hazır oladuğunu anlamak için SQLite kütüphanesinin versiyon numarasını yazdıran aşağıdaki + gibi bir programla test işlemi yapabilirsiniz: + + #include + #include "sqlite3.h" + + int main(void) + { + printf("%s\n", sqlite3_libversion()); + + return 0; + } +---------------------------------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------------------------------- + C’de SQLite veritabanı ile işlem yapmak için önce o veritabanının sqlite3_open fonksiyonuyla açılması gerekir. Bu işlemden + sqlite3 türünden bir handle elde edilir. sqlite3_open fonksiyonun prototipi şöyledir: + + int sqlite3_open( + const char *filename, /* Database filename (UTF-8) */ + sqlite3 **ppDb /* OUT: SQLite db handle */ + ); + + Fonksiyonun birinci parametresi bizden sqlite dosyasının yol ifadesini alır. İkinci parametresi sqlite3 isimli yapı türünden + bir göstricinin adresini almaktadır. Fonksiyon handle alanını (yani sqlite yapı nesnesini) oluşturur. Onun adresini bu + göstericinin içerisine yerleştirir. Fonksiyonun geri dönüş değeri işlemin başarısını belirtmektedir. Fonksiyon başarılıysa + SQLITE_OK değerine geri döner. Fonksiyon başarısız olduğunda yine dosyanın sqlite3_close fonksiyonuyla kapatılması gerekir. + Hata nedeni de sqlite3_errmsg fonksiyonuyla yazdırılabilir. Bu durumda sqlite dosyasının açılması tipik olarak şöyle + yapılabilir: + + if (sqlite3_open("student.db", &db) != SQLITE_OK) { + fprintf(stderr, "sqlite3_open failed: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + exit(EXIT_FAILURE); + } + + sqlite3_open fonksiyonu dosya varsa olanı açar, yoksa yeni bir SQLite DB dosyası yaratır. + + sqlite3_errmsg fonksiyonunun parametrik yapısı şöyledir: + + const char *sqlite3_errmsg(sqlite3 *db); + + sqlite3_close fonksiyonun prototipi ise şöyledir: + + int sqlite3_close(sqlite3*); + + Dosya kapatılırken başarı kontrolü yapmaya gerek yoktur. Başarısızlık durumlarında hata mesajını stderr dosyasına yazdırıp + programı sonlandıran bir sarma fonksiyon şöyle yazılabilir: + + void sqlite3_exit(const char *msg, sqlite3 *db) + { + fprintf(stderr, "%s failed => %s\n", msg, sqlite3_errmsg(db)); + sqlite3_close(db); + + exit(EXIT_FAILURE); + } + + Böylece biz hata durumlarını aşağıdaki gibi ele alabiliriz: + + if (sqlite3_open("studentxxx.db", &db) != SQLITE_OK) + sqlite3_exit("sqlite3_open", db); +---------------------------------------------------------------------------------------------------------------------------*/ + +#include +#include +#include "sqlite3.h" + +void sqlite3_exit(const char *msg, sqlite3 *db); + +int main(void) +{ + sqlite3 *db; + + if (sqlite3_open("student.db", &db) != SQLITE_OK) + sqlite3_exit("sqlite3_open", db); + + printf("succes...\n"); + + sqlite3_close(db); + + return 0; +} + +void sqlite3_exit(const char *msg, sqlite3 *db) +{ + fprintf(stderr, "%s failed => %s\n", msg, sqlite3_errmsg(db)); + sqlite3_close(db); + + exit(EXIT_FAILURE); +} + +/*-------------------------------------------------------------------------------------------------------------------------- + SQLite'a bir SQL cümlesi göndermek için sqlite3_exec fonksiyonu kullanılmaktadır. Fonksiyonun prototipi şöyledir: + + int sqlite3_exec( + sqlite3 *db, /* An open database */ + const char *sql, /* SQL to be evaluated */ + int (*callback)(void*,int,char**,char**), /* Callback function */ + void *param, /* 1st argument to callback */ + char **errmsg /* Error msg written here */ + ); + + Fonskiyonun birinci parametresi sqlite3_open fonksiyonundan elde edilen handle değeridir. İkinci parametre sql cümlesinin + yazısını alır. Üçüncü parametre işlemden sonra çağrılacak “callback” fonksiyonun adresini almaktadır. Bu parametre NULL + geçilebilir. Dördüncü parametre bu “callback” fonksiyona geçirilecek argümanı belirtir. Bu parametre de NULL geçilebilir. + Son parametre ise char * türünden bir göstericinin adresini almaktadır. Hata drumunda hata mesajının adresi bu göstericiye + yerleştirilir. Bu parametre NULL olarak da geçilebilir. Fonksiyonun geri dönüş değeri işlemin başarısını belirtir. Fonksiyon + başarılıysa SQLITE_OK değerine geri dönmektedir. Bu durumda biz hata mesajını yazdırdıktan sonra sqlite3_free fonksiyonu + ile tahsis edilen alanı serbest bırakabiliriz. Örneğin: + + if (sqlite3_exec(db, "INSERT INTO student_info(student_name, student_no) VALUES('Rasim Öztekin', 367)", + NULL, NULL, NULL) != SQLITE_OK) + sqlite3_exit("sqlite3_exec", db); + + Aşağıdaki örnekte student_info veri tabanına sqlite3_exec fonksiyonu ile bir kayıt eklenmiştir. +---------------------------------------------------------------------------------------------------------------------------*/ + +#include +#include +#include "sqlite3.h" + +void sqlite3_exit(const char *msg, sqlite3 *db); + +int main(void) +{ + sqlite3 *db; + + if (sqlite3_open("student.db", &db) != SQLITE_OK) + sqlite3_exit("sqlite3_open", db); + + if (sqlite3_exec(db, "INSERT INTO student_info(student_name, student_no) VALUES('Rasim Öztekin', 367)", NULL, NULL, NULL) != SQLITE_OK) + sqlite3_exit("sqlite3_exec", db); + + printf("Success...\n"); + + sqlite3_close(db); + + return 0; +} + +void sqlite3_exit(const char *msg, sqlite3 *db) +{ + fprintf(stderr, "%s failed => %s\n", msg, sqlite3_errmsg(db)); + sqlite3_close(db); + + exit(EXIT_FAILURE); +} + +/*-------------------------------------------------------------------------------------------------------------------------- + +---------------------------------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------------------------------- + +---------------------------------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------------------------------- + +---------------------------------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------------------------------- + +---------------------------------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------------------------------- + +---------------------------------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------------------------------- + +---------------------------------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------------------------------- + +---------------------------------------------------------------------------------------------------------------------------*/ + /*-------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------*/