New version
This commit is contained in:
parent
8ef9565f61
commit
9b64f4565d
4 changed files with 1144 additions and 399 deletions
|
@ -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>
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
#------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
açıklamaları üçüncü parti programlar tarafından (örneğin mypy) dikkate alınmaktadır. Yukarıdaki "sample.py" dosyasını mypy
|
||||
ile şöyle işleme sokabiliriz:
|
||||
açıklamaları üçüncü parti programlar tarafından (örneğin "mypy") dikkate alınmaktadır. Yukarıdaki "sample.py" dosyasını
|
||||
mypy ile şöyle işleme sokabiliriz:
|
||||
|
||||
mypy sample.py
|
||||
|
||||
|
@ -32669,17 +32761,32 @@ print(s.bar.__doc__)
|
|||
sample.py:6: error: Incompatible types in assignment (expression has type "float", variable has type "int")
|
||||
Found 1 error in 1 file (checked 1 source file)
|
||||
|
||||
Tabii biz tür açıklaması yaparken genellkle aynı zamanda ona değer atayarak aynı zamanda değişkeni yaratabiliriz. Örneğin:
|
||||
Tabii biz tür açıklaması yaparken genellkle aynı zamanda ona değer atayarak aynı zamanda değişkeni yaratabiliriz.
|
||||
Örneğin:
|
||||
|
||||
x: int = 10
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
Tür açıklamasındaki ifade içerisindeki tür isimlerinin daha önceden tanımlanmış olması gerekir. Örneğin:
|
||||
|
||||
int a: Sample
|
||||
|
||||
Burada tür açıklamasında a değişkeninn Sample türündne olması gerektiği belirtilmiştir. Eğer bu noktaya kadar Sample
|
||||
türü henüz oluşturulmadıysa yorumlayıcı da bu işlemi error olarak değerlendirir. Tabii built-in türler hiçbir import
|
||||
işlemi yapılmadan da tanımlanmış kabul edilmektedir. Örneğin:
|
||||
|
||||
int a: list
|
||||
|
||||
Burada list built-in bir tür olduğu için bir sorun ortaya çıkmayacaktır.
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
Tür açıklamaları IDE'lere entegre edilmiş araçlar tarafından da dikkate alınabilmektedir. Örneğin PyCharm IDE'sinde
|
||||
"Code/Inspect Code" menüsü ile tür kontrolü yapılabilir. Mypy aynı zamanda PyCharm IDE'sine bir plugin olarak eklenebilmektedir.
|
||||
Bunun için "File/Settings/Plugins" sekmesine gelinit. Burada "mypy" seçilerek araç IDE'ye dahil edilir. Visual Studio
|
||||
Bunun için "File/Settings/Plugins" sekmesine gelinir. Burada "mypy" seçilerek araç IDE'ye dahil edilir. Visual Studio
|
||||
Code IDE'sine de bir plugin olarak mypy eklenebilmektedir. Maalesef henüz tür kontrolü yapmak için Spyder IDE'sine entegre
|
||||
edilmiş bir araç yoktur.
|
||||
edilmiş bir araç yoktur. VSCode Editöründe de "mypy" bir plugin biçiminde bulunmaktadır.
|
||||
|
||||
mypy programı başarısız olursa sıfır dışında bir exit kodu üretmektedir. Başarı durumunda mypy programının exit kodu 0'dır.
|
||||
Örneğin:
|
||||
|
@ -32699,6 +32806,13 @@ print(s.bar.__doc__)
|
|||
:\Study\Python>echo %errorlevel%
|
||||
0
|
||||
|
||||
UNIX/Linux ve macOS sistemlerinde son çalıştırılan programın exit kodu $? ile elde edilebilmektedir. Örneğin:
|
||||
|
||||
$ mypy sample.py
|
||||
Success: no issues found in 1 source file
|
||||
$ echo $?
|
||||
0
|
||||
|
||||
Böylece build araçları ya da programcının oluşturduğu make dosyaları önce mypy programını çalıştırıp eğer bir hata varsa
|
||||
programı python yorumlayıcına hiç vermeyebilirler.
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -32762,7 +32876,7 @@ print(s.bar.__doc__)
|
|||
Found 1 error in 1 file (checked 1 source file)
|
||||
|
||||
Tabii yukarıdaki örnekten de gördüğünüz gibi eğer tür açıklaması yapılmamışsa değişken yaratılırken mymy ve üçüncü parti
|
||||
statik analiz araçları herhangi bir hata rapor etmemktedir. Örneğin:
|
||||
statik analiz araçları herhangi bir hata rapor etmemektedir. Örneğin:
|
||||
|
||||
def square(a: float) -> float:
|
||||
return a * a
|
||||
|
@ -32770,8 +32884,8 @@ print(s.bar.__doc__)
|
|||
result = square(10.2)
|
||||
print(result)
|
||||
|
||||
Burada biz result değişkeni şçin bir tür açıklaması yapmadık. Ancak square fonksiyonunun geri dönüş değerinin float türden
|
||||
olması gerektiğini belirttik. Ancak result değişkenine atama için mypy bir hata rapor etmeyecektir.
|
||||
Burada biz result değişkeni için bir tür açıklaması yapmadık. Ancak square fonksiyonunun geri dönüş değerinin float türden
|
||||
olması gerektiğini belirttik. result değişkenine atama için mypy bir hata rapor etmeyecektir.
|
||||
|
||||
mypy programı da versiyondan versiyona genişletilmektedir. Örneğin artık biz bir değişkene ilk kez değer atayıp onu
|
||||
yarattığımızda mypy sanki o değişken için atanan türe ilişkin tür açıklaması yapılmış gibi işlem uygulamaktadır. Örneğin:
|
||||
|
@ -32787,12 +32901,10 @@ print(s.bar.__doc__)
|
|||
|
||||
sample.py:4: error: Incompatible types in assignment (expression has type "float", variable has type "int") [assignment]
|
||||
Found 1 error in 1 file (checked 1 source file)
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
Biz bir değişkenin kendi sınıfımız türünden olmasını da sağlayabiliriz. Yani tür açıklamalarında kendi sınıflarımızı da
|
||||
kullanabiliriz. Örneğin:
|
||||
Tür açıklamalarında kendi sınıflarımızı da kullanabiliriz. Örneğin:
|
||||
|
||||
class Sample:
|
||||
pass
|
||||
|
@ -32801,12 +32913,11 @@ print(s.bar.__doc__)
|
|||
print(a)
|
||||
|
||||
s = Sample()
|
||||
|
||||
foo(s)
|
||||
|
||||
Burada foo fonksiyonu Sample sınıfı türünden parametre almaktadır. Biz onu başka türden bir argümanla çağırırsak mypy hata
|
||||
verecektir. Tabii türemiş sınıf taban sınıf gibi de kullanılabildiği için biz buradaki foo fonksiyonuna Sample sınıfından
|
||||
türetilmiş bir sınıf türünden değişken de geçebiliriz. Örneğin:
|
||||
Burada foo fonksiyonu Sample sınıfı türünden parametre almaktadır. Biz onu başka türden bir argümanla çağırırsak mypy
|
||||
hata verecektir. Tabii türemiş sınıf taban sınıf gibi de kullanılabildiği için biz buradaki foo fonksiyonuna Sample
|
||||
sınıfından türetilmiş bir sınıf türünden değişken de geçebiliriz. Örneğin:
|
||||
|
||||
class Sample:
|
||||
pass
|
||||
|
@ -32818,7 +32929,6 @@ print(s.bar.__doc__)
|
|||
print(a)
|
||||
|
||||
m = Mample()
|
||||
|
||||
foo(m)
|
||||
|
||||
Burada mypy herhangi bir hata mesajı vermeyecektir.
|
||||
|
@ -32871,7 +32981,7 @@ foo(m)
|
|||
Found 1 error in 1 file (checked 1 source file)
|
||||
|
||||
mypy gibi araçların "statik analiz araçları" olduğuna dikkat ediniz. Bu tür statik kod analizi yapan araçlar programı
|
||||
çalıştırarak bir kontrol yapamadığı için her türlü ihlali kontrol edememektedir. Örneğin:
|
||||
çalıştırarak bir kontrol yapamadığı için her türlü ihlali de kontrol edememektedir. Örneğin:
|
||||
|
||||
def foo(x):
|
||||
x.append(1.2)
|
||||
|
@ -32879,23 +32989,21 @@ foo(m)
|
|||
a: list[int]
|
||||
|
||||
a = [1, 2, 3, 4]
|
||||
|
||||
foo(a)
|
||||
|
||||
print(a)
|
||||
|
||||
Burada foo fonksiyonunun a listesine ekleme yaptığını dolayısıyla kuralın ihlal edildiğini mypy gibi statik analiz araçları
|
||||
genellikle tespit edememektedir.
|
||||
genellikle tespit edememektedir. Dolayısıyla burada mypy kodda bir ihlal olmadığına yönelik çıktı verecektir.
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
list, tuple, dict gibi türlerin köşeli parantezlerle tür açıklamalarında kullanılabilmesi Python 3.9 ile eklenmiştir.
|
||||
Python 3.8 ve öncesinde list, tuple ve dict sınıfları için tür açıklamaları typing modülü içerisisideki List, Tuple,
|
||||
Dict isimli sınıflar kullanılarak yapılabiliyordu. Ancak Python 3.9 ile birlikte artık bu işlemler için orijinal list,
|
||||
tuple ve dict sınıfları doğrudan kullanılabilir hale gelmiştir. typeing modülündeki List, Tuple ve Dict sınıfları orijinal
|
||||
set, tuple ve dict sınıfları değildir. Bunlar yalnızca tür açıklamaları için bulundurulmuş olan sınıflardır. Tabii eski
|
||||
sistem geçmişe doğru uyumu korumak için typeingmodülündeki bu sınıflar muhafaza edilmektedir. örneğin list sınıfı için
|
||||
tür açıklamaları aşağıdaki gibi de yapılabilmektedir:
|
||||
list, tuple, dict gibi türlerin köşeli parantezlerle tür açıklamalarında kullanılabilmesi Python 3.9 (Ekim 2020) ile
|
||||
eklenmiştir. Python 3.8 ve öncesinde list, tuple ve dict sınıfları için tür açıklamaları typing modülü içerisisideki
|
||||
List, Tuple, Dict isimli sınıflar kullanılarak yapılabiliyordu. Ancak Python 3.9 ile birlikte artık bu işlemler için
|
||||
orijinal list, tuple ve dict sınıfları doğrudan kullanılabilir hale gelmiştir. typeing modülündeki List, Tuple ve Dict
|
||||
sınıfları orijinal set, tuple ve dict sınıfları değildir. Bunlar yalnızca tür açıklamaları için bulundurulmuş olan
|
||||
sınıflardır. Tabii eski sistem geçmişe doğru uyumu korumak için typing modülündeki bu sınıflar muhafaza edilmektedir.
|
||||
örneğin list sınıfı için tür açıklamaları aşağıdaki gibi de yapılabilmektedir:
|
||||
|
||||
from typing import List
|
||||
|
||||
|
@ -32911,11 +33019,10 @@ foo(m)
|
|||
pass
|
||||
|
||||
foo([10, 20])
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
Benzer biçimde set ve tuple sınıfları için de tür açıklamaları için kullanılabilmektedir. Örneğin:
|
||||
Benzer biçimde set ve tuple sınıfları için de tür açıklamaları kullanılabilmektedir. Örneğin:
|
||||
|
||||
def foo(s: set):
|
||||
pass
|
||||
|
@ -32933,9 +33040,9 @@ foo(m)
|
|||
def foo(s: set[int]):
|
||||
pass
|
||||
|
||||
Burada s int değerleri tutan bir küme olmalıdır.
|
||||
Burada s int değerleri tutan bir küme türünden olmalıdır.
|
||||
|
||||
Demetlerde elemanların türleri sırasıyla tek tek belirtilebilmektedir. Örneğin:
|
||||
Demetlerde elemanların türleri sırasıyla tek tek belirtilir. Örneğin:
|
||||
|
||||
def foo(t: tuple[int, str]):
|
||||
pass
|
||||
|
@ -32961,7 +33068,7 @@ foo(m)
|
|||
pass
|
||||
|
||||
Burada a için yapılan açıklama "elemanları int türden olan demet" biçiminde bir açıklama değildir. "Tek bir elemanı olan,
|
||||
onun da int türden olan demet" açıklamasıdır.
|
||||
o elemanın da int türden olduğu demet" açıklamasıdır.
|
||||
|
||||
set ve tuple türlerinin bu biçimde doğrudan kullanılması yine Python 3.9 ile birlikte mümkün hale getirilmiştir. Yukarıda da
|
||||
belirttiğimiz gibi Python 3.8 ve aşağısında bunların yerine typing modülündeki Set ve Tuple sınıfları kullanılıyordu. Ancak
|
||||
|
@ -33023,8 +33130,8 @@ foo(m)
|
|||
def foo(d: dict):
|
||||
pass
|
||||
|
||||
Bu durumda fonksiyonun d parametre değişkenine bir sözlük geçirilmelidir. Ancak sitenirse yine köşeli parantezler içerisinde
|
||||
sözlüğün anahtar ve değer türleri ayrı ayrı belirtilebilir. Örneğin:
|
||||
Bu durumda fonksiyonun d parametre değişkenine bir sözlük geçirilmelidir. Ancak sitenirse yine köşeli parantezler
|
||||
içerisinde sözlüğün anahtar ve değer türleri ayrı ayrı belirtilebilir. Örneğin:
|
||||
|
||||
def foo(d: dict[int, str]):
|
||||
pass
|
||||
|
@ -33032,10 +33139,9 @@ foo(m)
|
|||
Burada sözlüğün anahtarları int, değerleri ise str türünden olmalıdır. Aşağıdaki çağrıda mypy bir hata rapor etmeyecektir:
|
||||
|
||||
d = {10: 'ali', 20: 'veli', 30: 'selami'}
|
||||
|
||||
foo(d)
|
||||
|
||||
Yukarıda da belirttiğimiz gibi Python 3.8 ve öncesinde bu işlem typing modülü içerisindeki Dict sınıfı ile yapılıyordu.
|
||||
Yukarıda da belirttiğimiz gibi Python 3.9'un öncesinde bu işlem typing modülü içerisindeki Dict sınıfı ile yapılıyordu.
|
||||
Örneğin:
|
||||
|
||||
from typing import Dict
|
||||
|
@ -33061,7 +33167,7 @@ foo(m)
|
|||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
typing modülündeki Any sınıfı tür açıklamalarında "herhangi bir tür olabilir" anlamına gelmektedir. Any kullanmak bazı
|
||||
durumlarda gereksidir. Örneğin:
|
||||
durumlarda gereksizdir. Örneğin:
|
||||
|
||||
def foo(a: Any):
|
||||
pass
|
||||
|
@ -33100,7 +33206,7 @@ foo(m)
|
|||
pass
|
||||
|
||||
Burada parametre değişkeni olan t için bizim üç elemanlı bir demet geçirmemiz gerekir. Bu demetin ilk elemanı int türden,
|
||||
üçüncü elemanı float türden ancak ikinci elemanı ise herhangi bir türden olabilir. Dolayısıyla aşağıdaki çağrılarda mypy
|
||||
üçüncü elemanı float türden ancak ikinci elemanı herhangi bir türden olabilir. Dolayısıyla aşağıdaki çağrılarda mypy
|
||||
bir hata rapor etmeyecektir:
|
||||
|
||||
foo((100, 'ali', 12.4))
|
||||
|
@ -33135,8 +33241,7 @@ foo(m)
|
|||
|
||||
def foo(a: int) -> None:
|
||||
if a < 0:
|
||||
return 'a must not be negative'
|
||||
|
||||
return 'a must not be negative'
|
||||
print('ok')
|
||||
|
||||
Burada mypy şöyle bir hata rapor etmektedir:
|
||||
|
@ -33152,10 +33257,8 @@ foo(m)
|
|||
delta = b ** 2 - 4 * a * c
|
||||
if delta < 0:
|
||||
return None
|
||||
|
||||
x1 = (-b + math.sqrt(delta)) / (2 * a)
|
||||
x2 = (-b - math.sqrt(delta)) / (2 * a)
|
||||
|
||||
return x1, x2
|
||||
|
||||
result: tuple[float, float]|None = getroots(1, 0, -4)
|
||||
|
@ -33166,12 +33269,11 @@ foo(m)
|
|||
x1, x2 = result
|
||||
print(f'x1 = {x1}, x2 = {x2}')
|
||||
|
||||
Burada ikinci derece denklemin köklerini bulan getroots fonksiyonu ya elemanları float olan iki elemanlı bir
|
||||
demet ile ya da None değeri ile geri dönmektedir. Fonksiyonun anımlamasına dikkat ediniz:
|
||||
Burada ikinci derece denklemin köklerini bulan getroots fonksiyonu ya elemanları float olan iki elemanlı bir demet
|
||||
ile ya da None değeri ile geri dönmektedir. Fonksiyonun tanımlamasına dikkat ediniz:
|
||||
|
||||
def getroots(a: float, b: float, c: float) -> tuple[float, float]|None:
|
||||
pass
|
||||
|
||||
pass
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
import math
|
||||
|
@ -33208,7 +33310,6 @@ foo(m)
|
|||
def foo(a: int) -> Optional[int]:
|
||||
if a > 0:
|
||||
return a
|
||||
|
||||
return None
|
||||
|
||||
result: Optional[int] = foo(10)
|
||||
|
@ -33217,7 +33318,7 @@ foo(m)
|
|||
Burada biz result değişkenine int türden ya da None atayabiliriz.
|
||||
|
||||
Yukarıda yazmış olduğumuz getroots fonksiyonunda fonksiyonun geri dönüş değeri için tuple[float, float]|None açıklamasını
|
||||
yapmıştık. Aynı açıklamayı Optional kullnarak Optional[tuple[float, float]] biçiminde de yapabilirdik.
|
||||
yapmıştık. Aynı açıklamayı Optional kullanarak Optional[tuple[float, float]] biçiminde de yapabilirdik.
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
import math
|
||||
|
@ -33240,10 +33341,10 @@ else:
|
|||
print('Kök yok')
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
Bir değişkene bir fonksiyon gibi çağrılabilecek (callable) bir değişken atanacaksa tür açıklamasında typing modülündeki
|
||||
Bir değişkene bir fonksiyon gibi çağrılabilecek (callable) bir ifade atanacaksa tür açıklamasında typing modülündeki
|
||||
Callable sınıfı kullanılmaktadır. Örneğin:
|
||||
|
||||
from typing import Callable
|
||||
from typing import Callable
|
||||
|
||||
def foo(f: Callable, *args, **kwargs):
|
||||
f(*args, **kwargs)
|
||||
|
@ -33279,7 +33380,7 @@ else:
|
|||
Dolayısıyla burada mypy herhangi bir hata rapor etmeyecektir.
|
||||
|
||||
Ancak istenirse değişkene atanacak çağrılabilen nesnenin parametrik yapısı ve geri dönüş değeri için de tür açıklaması
|
||||
yapılabilmektedir. Bunun için Callable siminden sonra köşeli parantezler içerisinde "bir liste biçiminde parametre türleri,
|
||||
yapılabilmektedir. Bunun için Callable isminden sonra köşeli parantezler içerisinde "bir liste biçiminde parametre türleri,
|
||||
sonra da geri dönüş değerinin türü" girilmelidir. Örneğin:
|
||||
|
||||
from typing import Callable
|
||||
|
@ -33289,7 +33390,7 @@ else:
|
|||
|
||||
f: Callable[[int, str], int]
|
||||
|
||||
Burada f değişkenine parametreleri sırasıyla int ve str olan geri dönüş değeri ise int olan çağrılabilen nesneler atanabilir.
|
||||
Burada f değişkenine parametreleri sırasıyla int ve str olan, geri dönüş değeri ise int olan çağrılabilen nesneler atanabilir.
|
||||
Eğer buna uygun atama yapılmazsa mypy hata verecektir. Örneğin:
|
||||
|
||||
f = foo
|
||||
|
@ -33367,7 +33468,7 @@ foo(len) # geçerli değil
|
|||
|
||||
foo(1.2)
|
||||
|
||||
Daha önce de belirtitğimiz gibi Python 3.10 ile birlikte Union işlemi '|' operatörü ile de yapılır hale getirilmiştir.
|
||||
Daha önce de belirttiğimiz gibi Python 3.10 ile birlikte Union işlemi '|' operatörü ile de yapılır hale getirilmiştir.
|
||||
Örneğin:
|
||||
|
||||
def foo(a: int|str):
|
||||
|
@ -33391,50 +33492,54 @@ print(a)
|
|||
|
||||
a = 'ankara' # geçersiz
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
Bazen bir değişkene ilişkin tür açıklamasını başka bir tür açıklaması ile değiştirmek isteyebiliriz. Aslında ikinci kez
|
||||
tür açıklaması yapmak Python yorumlayıcısı tarafından geçerlidir. Ancak mypy bu durumda hata rapor etmektedir. Örneğin:
|
||||
|
||||
a: int
|
||||
|
||||
a = 100
|
||||
print(a)
|
||||
|
||||
a: float # mypy hata rapor edecektir
|
||||
|
||||
a = 3.14
|
||||
print(a)
|
||||
|
||||
Bu durum Python yorumlayıcısı tarafındna geçerli olsa da mypy aşağıdaki gibi bir hata rapor edecektir:
|
||||
|
||||
sample.py:6: error: Name "a" already defined on line 1 [no-redef]
|
||||
sample.py:8: error: Incompatible types in assignment (expression has type "float", variable has type "int") [assignment]
|
||||
Found 2 errors in 1 file (checked 1 source file)
|
||||
|
||||
typing modülü içerisindeki cast fonksiyonu atanan değişken üzerinde tür açıklaması yapılmasına olanak sağlayan bir fonksiyondur.
|
||||
cast fonksiyonu birinci parametre olarak bir tür açıklamasını, ikinci parametre olarak bir değeri almaktadır. Bu değer açıklanmış
|
||||
bir değişken de olabilir.
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
typing modülü içeisindeki cast fonksiyonu belli bir tür açıklamasına sahip bir değişkene başka türdne bir değerin
|
||||
atanması için kullanılmaktadır. cast fonksiyonu birinci parametre olarak bir tür açıklamasını, ikinci parametre olarak
|
||||
bir değeri almaktadır. Bu değer açıklanmış bir değişken de olabilir.
|
||||
|
||||
Biz cast fonksiyonu sayesinde bir değişkene hem bir açıklama yapıp hem de bir değer arayabiliriz. Örneğin:
|
||||
|
||||
a: float = 12.2
|
||||
b = cast(int, a)
|
||||
|
||||
Örneğimizde b değişkeni artık int olarak açıklanmış durumdadır. Burada önemli bir nokta cast fonksiyonun bir açıklama
|
||||
amacıyla kullanılmasıdır. cast fonksiyonu bir dönüştürme yapmaz. Örneğimizde her ne kadar b int olarak açıklanmışsa da içerisinde yine 12.2
|
||||
değeri bulunacaktır. Yani biz burada b'ye hem bir float değer atamış olduk hem de a'yı int olarak açıklamış olduk.
|
||||
cast fonksiyonu aynı zamanda atanan hedef değişkende bir tür açıklaması da oluşturmaktadır. Yukarıdaki örnekte artık
|
||||
b de int türü olarak açıklanmıştır. Burada önemli bir nokta cast fonksiyonun bir açıklama amacıyla kullanılmasıdır.
|
||||
cast fonksiyonu bir dönüştürme yapmaz. Örneğimizde her ne kadar b int olarak açıklanmışsa da içerisinde yine 12.2 değeri
|
||||
bulunacaktır. Yani biz burada b'ye hem bir float değer atamış olduk hem de a'yı int olarak açıklamış olduk.
|
||||
|
||||
Yorumlayıcı cast işleminde dönüştürme yapmamaktadır. Örneğin:
|
||||
|
||||
from typing import cast
|
||||
|
||||
a: float = 12.2
|
||||
|
||||
b = cast(str, a)
|
||||
|
||||
print(b, type(a)) # 12.2 <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
|
||||
açıklamasına uygundur:
|
||||
Burada a değişkenine elemanları int ya da float olabilen dolaşılabilir nesneler atanabilir. Dolayısıyla aşağıdaki atama
|
||||
tür açıklamasına uygundur:
|
||||
|
||||
a = [1, 2, 3, 4, 'ankara', 'izmit']
|
||||
|
||||
|
@ -33522,39 +33627,50 @@ foo(123) # geçerli değil
|
|||
|
||||
a: Iterable[tuple[int, str]]
|
||||
|
||||
Burada a değişkenine demetlerden oluşan dolaşılabilir bir nesne atanabilir. Ancak bu demetlerin de ilk elemanları int ikinci elemanları
|
||||
str türünden olmak zorundadır. Örneğin aşağıdaki atama tür açıklamasına uygundur:
|
||||
Burada a değişkenine demetlerden oluşan dolaşılabilir bir nesne atanabilir. Ancak bu demetlerin de ilk elemanları
|
||||
int ikinci elemanları str türünden olmak zorundadır. Örneğin aşağıdaki atama tür açıklamasına uygundur:
|
||||
|
||||
a = [(6, 'Anakara'), (26, 'Eskişehir'), (35, 'İzmir')]
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
Tür açıklamaları iç içe yapıldığı zaman biraz karmaşık bir görüntü oluşturabilmektedir. Bu biçimdeki tür açıklamalarını oluştururken
|
||||
parantezlere dikkat ediniz. Örneğin:
|
||||
Tür açıklamaları iç içe yapıldığı zaman biraz karmaşık bir görüntü oluşturabilmektedir. Bu biçimdeki tür açıklamalarını
|
||||
oluştururken parantezlere dikkat ediniz. Örneğin:
|
||||
|
||||
from typing import Callable, Iterable
|
||||
|
||||
def foo(a: Iterable[tuple[Callable[[int, int], int], float]]):
|
||||
pass
|
||||
|
||||
Burada a değişkeninin parametresi dolaşılabilir bir nesne olmalıdır. Bu dolaşılabilir nesne bize iki elemanlı demet vermelidir.
|
||||
Demetin birinci elemanı parametreleri int, int olan, geri dönüş değeri int olan bir çağrılabilir nesneden ikinci elemanı ise
|
||||
float bir nesneden oluşmalıdır.
|
||||
Burada a değişkeninin parametresi dolaşılabilir bir nesne olmalıdır. Bu dolaşılabilir nesne bize iki elemanlı demet
|
||||
vermelidir. Demetin birinci elemanı parametreleri int, int olan, geri dönüş değeri int olan bir çağrılabilir nesneden
|
||||
ikinci elemanı ise float bir nesneden oluşmalıdır. Örneğin:
|
||||
|
||||
def bar(a: int, b: int) -> int:
|
||||
return 0
|
||||
|
||||
def tar(a: int, b: int) -> int:
|
||||
return 0
|
||||
|
||||
a = [(bar, 3.4 ), (tar, 4.34)]
|
||||
foo(a)
|
||||
|
||||
Buarad foo fonksiyonuna geçirilen argüman için mypy bir sorun bildirmeyecektir.
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
typing modülü içerisindeki Sequence sınıfı da string gibi liste gibi __getitem__, __len__ metotları bulunan "reversible
|
||||
seqeunce" türlerini belirtmek için kullanılmaktadır. Anımsanacağı gibi tipi tipik "seuqence" türleri list, range, tuple,
|
||||
str" türleridir. dict türünün __getitem__ metodu olsa da dict türü bir "seauence türü değildir. Örneğin:
|
||||
seqeunce" türlerini belirtmek için kullanılmaktadır. Anımsanacağı gibi tipik "seuqence" türleri list, range, tuple,
|
||||
str" türleridir. dict türünün __getitem__ metodu olsa da dict türü bir "sequence türü değildir. Sequence ile açıklama
|
||||
yaparken köşeli parantez içerisinde o türün tuttuğu nesnelerin türleri de belirtilebilmektedir. Örneğin:
|
||||
|
||||
from typing import Sequence
|
||||
|
||||
def foo(a: Sequence[int]):
|
||||
def foo(a: Sequence):
|
||||
pass
|
||||
|
||||
Burada biz foo fonksiyonunu bir listeyle, bir demetle, string'le ya da bir range nesnesiyle çağırabiliriz. Bu durumda tür
|
||||
açıklamasına uygun çağrılar oluşturulmuş oluruz. Örneğin:
|
||||
Burada biz foo fonksiyonunu bir listeyle, bir demetle, string'le ya da bir range nesnesiyle çağırabiliriz. Bu durumda
|
||||
tür açıklamasına uygun çağrılar oluşturulmuş oluruz. Örneğin:
|
||||
|
||||
foo([1, 2, 3, 4, 5])
|
||||
foo('ali')
|
||||
|
@ -33570,7 +33686,7 @@ foo(123) # geçerli değil
|
|||
def foo(a: Sequence[int]):
|
||||
pass
|
||||
|
||||
Burada a parametre değişkenine biz int elemanlardna oluşan bir "sequence türü" geçirebiliriz. Örneğin:
|
||||
Burada a parametre değişkenine biz int elemanlardan oluşan bir "sequence türü" geçirebiliriz. Örneğin:
|
||||
|
||||
foo([1, 2, 3, 4, 5])
|
||||
foo(range(100))
|
||||
|
@ -33581,3 +33697,11 @@ foo(123) # geçerli değil
|
|||
|
||||
çağrısı tür açıklamasına uygun değildir.
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------
|
||||
Python'daki tür açıklamaları için birkaç PEP dokğmanı oluşturulmuştur. Bu dokümanları da gözden geçirebilirsiniz:
|
||||
|
||||
https://peps.python.org/pep-0484/
|
||||
https://peps.python.org/pep-0526/
|
||||
https://peps.python.org/pep-0593/
|
||||
#------------------------------------------------------------------------------------------------------------------------#------------------------------------------------------------------------------------------------------------------------
|
|
@ -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 “DLL’in import kütüphanesi” gibi link aşamasına dahil edilebilir.
|
||||
Ya da istenirse aşağıdaki komutla bu “.def” dosyasından “.lib” uzantılı “import kütüphanesi de oluşturulabilmektedir:
|
||||
|
@ -72551,13 +72660,198 @@ int main(void)
|
|||
LIB /DEF:sqlite3.def /machine:x86
|
||||
|
||||
Buradaki machine argümanı hedef sistemi belirtmektedir. Burada 32 bit Windows sistemleri için x86, 64 bit Windows
|
||||
sistemleri için x64 kullanılmalıdır. İkinci indirmeden biz SQLite’ın kaynak dosyalarını elde ederiz. Buradaki “sqlite3.h”
|
||||
dosyası SQLite fonksiyonları için başlık dosyası niteliğindedir.
|
||||
sistemleri için "x64" kullanılmalıdır. Örneğin:
|
||||
|
||||
LIB /DEF:sqlite3.def /machine:x64
|
||||
|
||||
İkinci indirmeden biz SQLite’ın kaynak dosyalarını elde ederiz. Buradaki “sqlite3.h” dosyası SQLite fonksiyonları için
|
||||
başlık dosyası niteliğindedir.
|
||||
|
||||
Mac OS X için kurulum Windows’takine benzemektedir. Yine ilgili “.zip” dosyaları indirilip kurulum yapılabilir. Bu
|
||||
sistemlerde derleme yaparken link aşamasında "-lsqlite3" lie kütüphane dosyasını belirtmeyi unutmayınız.
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
200. Ders 23/02/2025 - Pazar
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
Kurulum sonrası her şeyin hazır oladuğunu anlamak için SQLite kütüphanesinin versiyon numarasını yazdıran aşağıdaki
|
||||
gibi bir programla test işlemi yapabilirsiniz:
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s\n", sqlite3_libversion());
|
||||
|
||||
return 0;
|
||||
}
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
C’de SQLite veritabanı ile işlem yapmak için önce o veritabanının sqlite3_open fonksiyonuyla açılması gerekir. Bu işlemden
|
||||
sqlite3 türünden bir handle elde edilir. sqlite3_open fonksiyonun prototipi şöyledir:
|
||||
|
||||
int sqlite3_open(
|
||||
const char *filename, /* Database filename (UTF-8) */
|
||||
sqlite3 **ppDb /* OUT: SQLite db handle */
|
||||
);
|
||||
|
||||
Fonksiyonun birinci parametresi bizden sqlite dosyasının yol ifadesini alır. İkinci parametresi sqlite3 isimli yapı türünden
|
||||
bir göstricinin adresini almaktadır. Fonksiyon handle alanını (yani sqlite yapı nesnesini) oluşturur. Onun adresini bu
|
||||
göstericinin içerisine yerleştirir. Fonksiyonun geri dönüş değeri işlemin başarısını belirtmektedir. Fonksiyon başarılıysa
|
||||
SQLITE_OK değerine geri döner. Fonksiyon başarısız olduğunda yine dosyanın sqlite3_close fonksiyonuyla kapatılması gerekir.
|
||||
Hata nedeni de sqlite3_errmsg fonksiyonuyla yazdırılabilir. Bu durumda sqlite dosyasının açılması tipik olarak şöyle
|
||||
yapılabilir:
|
||||
|
||||
if (sqlite3_open("student.db", &db) != SQLITE_OK) {
|
||||
fprintf(stderr, "sqlite3_open failed: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
sqlite3_open fonksiyonu dosya varsa olanı açar, yoksa yeni bir SQLite DB dosyası yaratır.
|
||||
|
||||
sqlite3_errmsg fonksiyonunun parametrik yapısı şöyledir:
|
||||
|
||||
const char *sqlite3_errmsg(sqlite3 *db);
|
||||
|
||||
sqlite3_close fonksiyonun prototipi ise şöyledir:
|
||||
|
||||
int sqlite3_close(sqlite3*);
|
||||
|
||||
Dosya kapatılırken başarı kontrolü yapmaya gerek yoktur. Başarısızlık durumlarında hata mesajını stderr dosyasına yazdırıp
|
||||
programı sonlandıran bir sarma fonksiyon şöyle yazılabilir:
|
||||
|
||||
void sqlite3_exit(const char *msg, sqlite3 *db)
|
||||
{
|
||||
fprintf(stderr, "%s failed => %s\n", msg, sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
Böylece biz hata durumlarını aşağıdaki gibi ele alabiliriz:
|
||||
|
||||
if (sqlite3_open("studentxxx.db", &db) != SQLITE_OK)
|
||||
sqlite3_exit("sqlite3_open", db);
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#include <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);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
|
Loading…
Add table
Reference in a new issue