New version

This commit is contained in:
Kaan Aslan 2025-02-25 23:30:26 +03:00
parent 8ef9565f61
commit 9b64f4565d
4 changed files with 1144 additions and 399 deletions

View file

@ -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 <linux/module.h>
#include <linux/kernel.h>
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.
-----------------------------------------------------------------------------------------------------------------------------*/
<BURADA KALDIK>
/*-----------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------*/

View file

@ -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()])
<BURADA KALDIK>
#------------------------------------------------------------------------------------------------------------------------------------
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 <sütun_listesi> FROM <tablos_ismi> [WHERE <koşul];
SELECT <sütun_listesi> FROM <tabls_ismi> [WHERE <koşul];
Eğer WHERE cümleciği kullanlmazsa tablodaki tüm kayıtlar elde edilir. Sütun listesi sütunların isimlerinden olumaktadır.
Uygulamacı yalnızca bazı sütun bilgilerini elde edebilir. Sütun istesi yerine "*" kullanılırsa "hepsi" anlamına gelmektedir. Örneğin:
Eğer WHERE cümleciği kullanılmazsa tablodaki tüm kayıtlar elde edilir. Sütun listesi sütunların isimlerinden oluşmaktadır. Uygulamacı
yalnızca bazı sütun bilgilerini elde edebilir. Sütun istesi yerine * kullanılırsa "tüm sütunlar" anlamına gelmektedir. Örneğin:
SELECT * FROM student WHERE student_no > 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.
<BURADA KALDIK>
DataFrame nesnesine append metodu ile satır ve sütun eklenebiliyordu. Ancak bu metot artık "deprecated" yapılmış durumdadır.
#------------------------------------------------------------------------------------------------------------------------------------

View file

@ -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))
<BURADA KALDIK>
#------------------------------------------------------------------------------------------------------------------------
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)) # <class 'module'>
#------------------------------------------------------------------------------------------------------------------------
#------------------------------------------------------------------------------------------------------------------------
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
ı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:
ı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ııklamayı Optional kullnarak Optional[tuple[float, float]] biçiminde de yapabilirdik.
yapmıştık. Aynıı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 <class 'float'>
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
ı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 ı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
ı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 ı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/
#------------------------------------------------------------------------------------------------------------------------#------------------------------------------------------------------------------------------------------------------------

View file

@ -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 <linux/module.h>
#include <linux/kernel.h>
@ -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 “DLLin 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 Windowstakine 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 <stdio.h>
#include "sqlite3.h"
int main(void)
{
printf("%s\n", sqlite3_libversion());
return 0;
}
---------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------
Cde 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 <stdio.h>
#include <stdlib.h>
#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 <stdio.h>
#include <stdlib.h>
#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);
}
/*--------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------*/