Python Examples
Python Examples
--------------------------------------
Python Programlama Dili Kursu Örnek Kodlar
Kaan ASLAN
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
REPL (Read and Evalutate and Print Loop) Ortamı için yaygın üç seçenek
kullanılmaktadır:
#----------------------------------------------------------------------------------
--------------------------------------
Python'da "Merhaba Dünya" yazısını ekrana basan program aşağıdaki yazılabilir.
Bir Python programını komut satırından
şöyle çalıştırabiliriz:
Örneğin:
python [Link]
#----------------------------------------------------------------------------------
--------------------------------------
print('Merhaba Dünya')
#----------------------------------------------------------------------------------
--------------------------------------
Python'da ifadelerden oluşan basit deyimler tek bir satırda yazılmak
zorundadır. Eğer bir satıra birden fazla basit yerleştirilecekse
bunların arasına ';' atomu getirilmelidir. Örneğin:
a = 10; b = 20
#----------------------------------------------------------------------------------
--------------------------------------
Bir değişkene nokta içermeyen bir sayı atandığında o değişken artık int
türünden olur. Bir değişkenin o andaki türünü type
isimli built-in fonksiyonla elde edebiliriz. Python'da int türünün bir sınırı
yoktur. Yani sınırsız bir biçimde basamak içerebilir.
Halbuki C, C++, Java ve C# gibi dillerde int türünün belli bir byte uzunluğu
vardır. Dolayısıyla bu dillerde biz ancak belli aralıktaki
sayıları int türden bir değişkenin içerisine yerleştirebiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = 3928239847982347923749823749827349872398472983749823748982934234
>>> type(a)
<class 'int'>
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki kodu bir program olarak çalıştırınız.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10287364872364872348762348762
#----------------------------------------------------------------------------------
--------------------------------------
Python'da noktalı sayılar "float" isimli türle temsil edilmiştir. float türü 8
byte uzunlukta kayan noktalı (IEEE 754
long real format) bir formata sahiptir. Python'daki float türü C, C++, Java ve
C# gibi dillerdeki double türü ile aynıdır.
Noktalı sayıların tutuluş formatından kaynaklanan ve ismine "yuvarlama hatası
(rounding error)" denilen bir olgu vardır.
Yuvarlama hatası bir noktalı sayının tam olarak ifade edilemeyip ona yakın bir
sayının ifade edilmesiyle oluşan bir hatadır.
Yuvarlama hataları sayıyı ilk kez depolarken de oluşabilir. Aritmetik işlemler
sonucunda da oluşabilir.
#----------------------------------------------------------------------------------
--------------------------------------
>>> f = 123.456789
>>> f
123.456789
>>> type(f)
<class 'float'>
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Yuvarlama hataları her noktalı sayıda ortaya çıkmaz bazı noktalı sayılarda
ortaya çıkar. Yuvarlama hataları sayı ilk kez depolanırken
ortaya çıkabildiği gibi, bir işlem sonucunda da ortaya çıkabilmektedir. Bugün
işlemcilerin kullandığı IEEE 754 numaralı fırmatta
yuvarlama hatalarını ortadan kaldırmanın bir yolu yoktur. Eğer yuvarlama
hataları istenmiyorsa bu durumda özel başka türler tercih edilebilir.
Örneğin Pyton Standart Kütüphanesindeki "decimal" türü yapay bir tür olsa da
yuvarlama hatalarına olanak vermemktedir. Programlama dillerinde
yuvarlama hataları yüzünden iki noktalı sayının tam eşitliğinin
karşılaştırılması uygun kabul edilmemektedir. Çünkü iki noktalı
sayı yuvarlama hatalarından dolayı biribirine eşit olacakken olmayabilir.
>>> a = 0.3
>>> b = 0.1
>>> c = a - b
>>> c
0.19999999999999998
#----------------------------------------------------------------------------------
--------------------------------------
Bir değişkene True ya da False anahtar sözcükleri atanırsa o değişken "bool"
türünden olur. bool türü True ve False
biçiminde yalnızca iki değer tutabilmektedir. Programcılar "doğru-yanlış",
"olumsu-olumsuz", "evet-hayır" gibi iki
değer içeren olguları genellikle bool türü ile temsil ederler. Tabii yalnızca
Pyton'da değil diğer programlama dillerinin
çoğunda aslında bool türden bir değişken 0 ya da 1 gibi bir sayı tutmaktadır.
Yani biz bir değişkene True değerini atasak bile
aslında bu değişken içsel olarak muhtemelen bu değeri 1 sayısı biçiminde
tutmaktadır. Diğer programlama dillerinden geçenlerin
dikkat etmesi gereken bir nokta Python'da True ve False anahtar sözcüklerinin
ilk harfinin büyük harf olmasıdır. Halbuki pek çok dilde
bu anahtar sözcükler küçük harflerle oluşturulmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
>>> b = True
>>> b
True
>>> type(b)
<class 'bool'>
>>> b = False
>>> b
False
>>> type(b)
<class 'bool'>
#----------------------------------------------------------------------------------
--------------------------------------
Python'da yazıları tutan türe "str" denilmektedir. Bu türün ismi str olsa da
biz bu türe "string" de diyeceğiz. String
oluşturmak için tek tırnak ya da çift tırnak kullanılır. Bu bağlamda tek
tırnakla çift tırnak arasında Python'da hiçbir
farklılık yoktur. Bazı programlama dillerinde tek tırnak ile çift tırnak
tamamen farklı anlamlara gelmektedir. Bu dillerden
geçen kişiler Python'da tek tırmak ile çift tırnak arasında bir fark olmadığına
dikkat etmelidir.
Biz kursumuzda ağırlıklı olarak tek tırnak kullanımını tercih edeceğiz. Pek çok
programlama dilinde tek bir karakteri
tutmakta kullanılan "char" isimli bir tür de bulunmaktadır. Ancak Python'da
böyle bir tür yoktur. Tek karakterli string'ler bu gereksinimi
karşılamaktadır. Örneğin C, C++, Java ve C# dillerinde tek bir karakter tek
tırnakla bireden fazla karakterden oluşan yazılar da çift
tırnakla ifade edilmektedir. Oysa Python'da karakter ve string biçiminde ikşi
ayrı tür yoktur. Karakterler terk karakterden olulan string'lerle
ifade edilmektedir. Dolayısıyla Python'da tek tırnakla çift tırnak arasında da
bir farklılık kalmamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
>>> s = 'Ankara'
>>> s
'Ankara'
>>> type(s)
<class 'str'>
>>> k = "Ankara"
>>> k
'Ankara'
>>> type(k)
<class 'str'>
#----------------------------------------------------------------------------------
--------------------------------------
Python'da "complex" isimli bir tür de vardır. Bir complex nesne oluşturmak için
'j' harfi kullanılır. Ancak j harfinden önce
ona bitişik olan bir sayı getirilmesi zorunludur. Bu sayı genel olarak float
olabilir. Complex sayılara '+' ya da '-' operatörü ile bir gerçek kısım
da ekleyebiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = 1 + 2j
>>> b = 3j
>>> c = a + b
>>> a
(1+2j)
>>> b
3j
>>> c
(1+5j)
>>> type(a)
<class 'complex'>
>>> type(b)
<class 'complex'>
>>> type(c)
<class 'complex'>
#----------------------------------------------------------------------------------
--------------------------------------
i sayısını ifade etmek için yalnızca j kullanılamaz. Çünkü yalnızca j bir
değişken anlamına gelmektedir. Dolayısıyla
i sayısı 1j biçiminde ifade edilmelidir.
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = 1j
>>> a
1j
>>> type(a)
<class 'complex'>
#----------------------------------------------------------------------------------
--------------------------------------
Python'da None anahtar sözcüğü ile temsil edilen özel bir değer vardır. Bu
None değeri "NoneType" isimli bir türdendir.
None değeri "yokluk", "boşluk", "hiçlik", "öylesinelik" ve başarısızlık gibi
durumları ifade etmek için kullanılmaktadır.
Biz bir değişkenin içerisine None değerini atadığımızda artık o değişken
NoneType türünden olur. (None sşzcüğündeki ilk 'N'nin
büyük harf olduğuna dikkat ediniz.) Komut satırında içerisinde None bulunan bir
değişkenin ismini yazdığımızda ekrana bir şey
basılmaz. Ancak print fonksiyonuyla bu değişken yazdırılmak istendiğinde "None"
yazısı görüntülenmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = None
>>> a
>>> print(a)
None
>>> type(a)
<class 'NoneType'>
#----------------------------------------------------------------------------------
--------------------------------------
Statik tür sistemine sahip programlama dillerinde bir değişken kullanılmadan
önce derleyiciye tanıtılmak zorundadır.
Bu tanıtma eylemine "bildirim (declaration)" denilmektedir. Fakat Python gibi
dinamik tür sistemine sahip programlama dillerinde
bildirim diye bir işlem yoktur. Zaten bu tür dillerde bildirim işleminin bir
anlamı da olamaz. Python'da bir değişken ona
ilk kez değer atandığında yaratılmaktadır. (Halbuki statik tür sistemine sahip
programlama dillerinde değişkenler bildirim sırasında yaratılırlar.)
a = 12.3
b = a + 1 # geçerli a yaratılmış, b de yaratılıyor
c = x + 2 # x yaratılmamış error oluşur
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda da ifade ettiğimiz gibi Python'da bir değişken ona ilk kez değer
atandığında yaratılır ve değişkenin türü de
ona atanan değerin türünden olur. Eninde sonunda değişkenlere bir biçimde
sabitlerin atanması gerekmektedir. O halde
sabitlerin de türleri vardır. Şimdi sabit türleri üzerinde duracağız.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Programlarda doğrudan yazdığımız sayılara ya da yazılara "sabit (literals)"
denilmektedir. Sabitler işin başında değişkenleri yaratmak için
kullanılırlar. Sabitlerin türleri vardır. Sabitler bir değişkene atandığında
değişken de o sabitin türünden olur.
1) Sayı nokta içermiyorsa böyle sayılar int türden sabit belirtir. int türden
sabitler 0x ya da 0X ile başlatılarak 16'lık sistemde de
yazılabilirler. int bir sabit 0o ya da 0O önekiyle başlatılarak yazılırsa 8'lik
sistemde (octal system) yazıldığı
kabul edilmektedir. Nihayet int bir sabit 0b ya da 0B ile başlatılarak 2'lik
sistemde (binary system) yazılabilir.
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = 123
>>> a
123
>>> type(a)
<class 'int'>
>>> a = 0o123
>>> a
83
>>> type(a)
<class 'int'>
>>> a = 0x123
>>> a
291
>>> type(a)
<class 'int'>
>>> a = 0b101010
>>> a
42
>>> type(a)
<class 'int'>
#----------------------------------------------------------------------------------
--------------------------------------
Python'da int türden sabitler yazılırken sayıların aralarına alt tire karakteri
konulabilir. Bu kural uzun tamsayılarda
okunabilirliği artırmak amacıyla dile sokulmuştur. Ancak iki tane alt tire yan
yana bulundurulamaz ve sayının başında ve sonunda sonunda
alt tire bulundurulamaz. Alt tire 10'luk, 16'lık, 8'lik ve 2'lik sistemlerdeki
yazımlarda da kullanılabilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = 1_000_000_000
>>> a
1000000000
>>> a = 0x1234_5678
>>> a
305419896
>>> a = 0B1010_0101
>>> a
165
>>> a = 1_0_0_0
>>> a
1000
#----------------------------------------------------------------------------------
--------------------------------------
2) Bir sayı nokta içeriyorsa sabit float türdendir. Noktanın soluna ya da
sağına bir şey yazılmanışsa 0 yazılmış olduğu
varsayılır. float sabitlerde de alt tire benzer amaçla kullanılabilmektedir.
float sabitler tıpkı diğer programlama dillerinde
olduğu gibi üstel biçimde de belirtilebilmektedir. Bunun için önce nokta içeren
ya da içermeyen bir sayısal kısım sonra 'e' ya da 'E'
harfi sonra da on üzeri anlamına gelen kuvvet değeri bulundurulur. Örneğin
1.2e-3 değeri 1.2 x 10^-3 anlamına gelmektedir. Tabii
üstel biçimdeki sayıda hiç nokta olmasa bile (1e3 örneğindeki gibi) sabit float
kabul edilir. float sabitlerde de basamaklamak için
alt tire kullanılabilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = 2.3
>>> type(a)
<class 'float'>
>>> b = .28
>>> type(b)
<class 'float'>
>>> c = 12.
>>> type(c)
<class 'float'>
>>> d = 10_3.23_45_57
>>> type(d)
<class 'float'>
>>> e = 1e40
>>> type(e)
<class 'float'>
>>> f = -1.2e-2
>>> type(f)
<class 'float'>
>>> g = 1.23_45e2_3
>>> type(g)
<class 'float'>
#----------------------------------------------------------------------------------
--------------------------------------
Bir int sabiti pratik bir biçimde float sabit yapmak için sonuna nokta
konulması yeterlidir. Örneğin:
>>> a = 123
>>> type(a)
<class 'int'>
>>> a = 123.
>>> type(a)
<class 'float'>
C, C++, Java ve C# gibi dillerde sabitlerin sonlarına bazı sonekler
getirilebilmektedir. Python'da böyle bir sonek getirme
durumu yoktur.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
3) bool türden iki sabit vardır: True ve False. Python'da bool türü aritmetik
işlemlere sokulabilmektedir. Bu durumda True
1 olarak False 0 olarak işleme girmektedir. İki bool değeri kendi aralarında
işleme sokarsak sonuç int türden elde edilmektedir.
Python'daki bool sabitlerin ilk harflerinin büyük harf olduğuna dikkat ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = True
>>> type(a)
<class 'bool'>
>>> b = False
>>> type(b)
<class 'bool'>
>>> c = a * 3
>>> c
3
>>> type(c)
<class 'int'>
>>> d = True + True
>>> d
2
>>> type(d)
<class 'int'>
#----------------------------------------------------------------------------------
--------------------------------------
4) Python'da bir yazı tek tırnak ya da çift tırnak arasına alınırsa bu ifade
str türünden sabit belirtir. Bu bağlamda
tek tırnakla çift tırnak arasında hiçbir farklılık yoktur. Python'ın 2'li
versiyonları stringleri ASCII karakterleri biçiminde
saklıyordu. Ancak 3'lü versiyonlarla birlikte Python'da tüm string'ler UNICODE
tablo esas alınarak tutulmaya başlandı. UNICODE
tabloda her karakter 2 byte ile temsil edilmektedir. Böylece Python
stringlerinin içerisinde biz dünyanın bütün karakterlerini
yerleştirebiliriz. Boş bir string oluşturmak da geçerlidir.
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = 'ankara'
>>> a
'ankara'
>>> type(a)
<class 'str'>
>>> b = 'ağrı dağı'
>>> b
'ağrı dağı'
>>> type(b)
<class 'str'>
>>> c = "burdur"
>>> c
'burdur'
>>> type(c)
<class 'str'>
>>> d = ''
>>> d
''
>>> type(d)
<class 'str'>
#----------------------------------------------------------------------------------
--------------------------------------
UNICODE tablonun ilk 128 karakteri standart ASCII tablosu ile aynıdır. İkinci
128'lik karakteri ASCII Latin-1 (ISO 8859-1)
code page'i ile aynıdır. ASCII ve UNICODE tablonun ilk 32 karakteri
görüntülenemeyen (nonprintable) kontrol karakterlerinden oluşmaktadır.
Bu karakterleri yazdırmaya çalıştığımızda ekranda bir şey görmeyiz. Ancak bir
eylem oluşur. Bu özel karakterlerin bazıları ters bölü
tekniği ile kullanıma sunulmuştur. Ters bölü karakterlerinin listesi şöyledir:
s = 'Ali\nVeli'
print(s)
Ali
Veli
Python'da tek tırnak içerisindeki tek tırnak, çift tırnak içerisindeki çift
tırnak soruna yol açmaktadır. Bu nedenle tek tırnak
içerisinde tek tırnak karakteri \' biçiminde yazılır. Benzer biçimde çift
tırnak içerisindeki çift tırnak karakteri de \" biçiminde yazılmaktadır.
Ancak tek tırnak içerisindeki çift tırnak, çift tırnak içerisindeki tek tırnak
bir soruna yol açmamaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = 'ankara'
>>> a
'ankara'
>>> type(a)
<class 'str'>
>>> b = 'ağrı dağı'
>>> b
'ağrı dağı'
>>> type(b)
<class 'str'>
>>> c = "burdur"
>>> c
'burdur'
>>> type(c)
<class 'str'>
>>> d = ''
>>> d
''
>>> type(d)
<class 'str'>
>>> e = 'Türkiye\'nin başkenti Anraka\'dır'
>>> print(e)
Türkiye'nin başkenti Anraka'dır
>>> f = "Türkiye'nin başkenti Ankara'dır"
>>> print(f)
Türkiye'nin başkenti Ankara'dır
>>> g = "Bu konuyu \"vurgulamak\" istiyorum"
>>> print(g)
Bu konuyu "vurgulamak" istiyorum
>>> h = 'Bu konuyu "vurgulamak" istiyorum'
>>> print(h)
Bu konuyu "vurgulamak" istiyorum
#----------------------------------------------------------------------------------
--------------------------------------
String içerisindeki ters bölü karakterinin kendisini ters bölü biçiminde
yazarsak o karakter yanındaki karakterle birlikte
tek bir karakter olarak ele alınabilir. Örneğin:
path = 'C:\temp\[Link]'
path = 'C:\\temp\\[Link]'
C, C++, Java ve C# gibi dillerde ters bölü karakterinin yanındaki karakter ters
bölü karakterlerinden biri değilse bu durum
sentaks bakımından geçerli kabul edilmemektedir. Halbuki Python'da ters bölü
karakterinin yanındaki karakter yukarıda listesini verdiğimiz
ters bölü karakterlerinden biri değilse artık orada ters bölü karakteri
gerçekten ters bölü karakteri olarak ele alınmaktadır. Örneğin:
path = "C:\day\month";
path = "C:\\day\\month";
Komut satırında str türünden bir değişkeni print fonksiyonu ile yazdırdığımızda
gerçekten ters bölü karakterleri kendi işlevleri ile
işlem görmektedir. Ancak str türünden değişkenin ismini yazıp ENTER tuşuna
bastığımızda bu durumda ilgili yazı programcıların kolay anlaması
için ters bölülü biçimde görüntülenmektedir. Örneğin:
>>> s = 'ali\nveli'
>>> print(s)
ali
veli
>>> s
'ali\nveli'
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir string'in önüne (yani soluna) onunla yapışık 'r' ya da 'R' karakteri
getirilirse (Buradaki r "regular" sözcüğünden geliyor) bu string'ler içerisindeki
ters bölü karakterleri gerçekten ters bölü karakterleri anlamına gelmektedir.
Tabii içinde hiç ters bölü olmayan string'lerin başına r getirmenin
bir anlamı yoktur. Ancak yasak da değildir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da tek tırnak ve çift tırnak içerisindeki yazılar tek satıra (aynı
satıra) yazılmak zorundadır. Örneğin:
s = 'ali veli
selami'
Bu yazım geçersizdir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da string'ler tek tırnağın ve çift tırnağın yanı sıra üç tek tırnak ve
üç çift tırnakla da belirtilebilirler. (İki tek tırnak
ya da iki çift tırnak biçiminde bir belirtme yoktur.)
#----------------------------------------------------------------------------------
--------------------------------------
s = '''This is a test'''
print(s)
s = """This is a test"""
print(s)
#----------------------------------------------------------------------------------
--------------------------------------
Üç tek tırnak ile üç çift tırnak arasında bir farklılık yoktur. Üçlü
tırnakların tekli tırnaklardan iki farlılığı vardır:
String'in tek tırnak ya da üç tırnak ile ifade edilmesi arasında ters bölü
karakter sabitleri bakımından bir farklılık yoktur.
Yine üç tırnaklı string'lerin başına 'r' ya da 'R' öneki getirilebilir. Bu
durumda yine buradaki ters bölü karakterleri gerçekten
ters bölü karakteri olarak değerlendirilir.
#----------------------------------------------------------------------------------
--------------------------------------
s = '''Bugün hava
çok güzel'''
print(s)
s = '''c:\temp\[Link]'''
print(s)
s = r'''c:\temp\[Link]'''
print(s)
#----------------------------------------------------------------------------------
--------------------------------------
Üç tek tırnağın ya da üç çift tırnağın içerisindeki tek tırnak ya da çift
tırnak yazının başında olabilir. Ancak istisna olarak
sonunda olamaz. Çünkü Python'da ardışıl en uzun karakterlerden atom
yapılmaktadır. Örneğin:
s = """"ankara""""
s
=
"""
"ankara
"""
" (burada tek kalıyor)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'un 2'li versiyonlarında bir byte'lık ASCII tablosunu kullanılıyordu. Bu
versiyonlarda UNICODE string'ler için string'lere yapışık
'u' ya da 'U' önekleri kullanılıyordu. Ancak Python'ın stringleri zaten üçlü
versiınlarla birlikte default UNICODE haline getirilmiştir.
Bugün hala bu 'u' ya da 'U' öneki geçerli olsa da artık bunun bir işlevi
kalmamıştır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Tek tırnak ya da üç tırnağın önüne onunla yapışık 'b' ya da 'B' öneki
getirilirse elde edilen nesne "bytes" denilen bir türden olur.
Yani artık bu nesne bir string değildir. Bu konu ileride ele alınacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da (tıpkı C Programlama Dilinde olduğu gibi) aralarında hiç bir atom
olmayan yan yana iki string otomatik olarak birleştirilmektedir.
Tabii bu iki string'in aynı satır üzerinde olması gerekir. Örneğin:
s = 'ali' 'veli'
ile,
s = 'aliveli'
aynı anlamdadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
5) None anahtar sözcüğü NoneType türünden sabit belirtmektedir. Zaten bu türden
tek sabit None anahtar sözcüğüdür. None
değeri REPL ortamında ekrana bir şey basmamaktadır. None değeri print edilirse
ekrana None yazısı çıkar.
#----------------------------------------------------------------------------------
--------------------------------------
>>> a = None
>>> a
>>> print(a)
None
>>> type(a)
<class 'NoneType'>
#----------------------------------------------------------------------------------
--------------------------------------
6) Python'da 'j' harfi önünde yapışık bir float ya da int değerle
kullanıldığında Complex türden sabit anlamına gelir. Complex sabitler
'j' nin yanı sıra + ya da - operatörü ile bir gerçek kısma da sahip
olabilirler. Yalızca j değerine sahip karmaşık sayı oluşturmak için 1j
ifadesini kullanmak gerekir. Çünü tek başına j bir değişken ismi olarak ele
alınmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
z = 1j
print(z)
z = -2j + 5
print(z)
z = 0j + 2
print(z)
#----------------------------------------------------------------------------------
--------------------------------------
Python'da açıklama yazıları oluşturmak için (comment/remark) #
kullanılmaktadır. # karakterinden satır sonuna kadarki
karakterler yorumlayıcı tarafından dikkate alınmamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
pi = 3.14159
radius = 2
#----------------------------------------------------------------------------------
--------------------------------------
Python'da etkisiz kod oluşturmak geçersiz değildir. Örneğin:
10 + 20
Böyle bir kodun bir anlamı yoktur. Çünkü bu kod program içerisinde bir durum
değişikliği (side effect) oluşturmaz. Bu bağlamda bir değişkene
atanmamış string'ler de etkisiz koddur ve geçerlidir. O halde Python'da birden
fazla satır üzerinde yorumlama yapabilek için üç tırnaklı
string'ler kullanılabilir. Gerçekten de Python programcıları birden fazla
satıra yayılmış yorumlamaları üç tırnaklı string'lerle yaparlar.
#----------------------------------------------------------------------------------
--------------------------------------
"""
Merhaba Dünya
Programı
"""
print('Hello World')
#----------------------------------------------------------------------------------
--------------------------------------
Python'da bir değişken ona ilk kez değer atandaığında yaratılmaktadır. Değişken
isimlendirmesinde diğer programlama dillerinin
çoğunda söz konusu olan kurallar geçerlidir:
ağrı_dağının_yüksekliği = 5137
number_of_students
loop_count
2) Deve Notasyonu (Camel Casting): Burada ilk sözcüğün tamamı küçük harflerle
yazılır. Sonraki her sözcüğün yalnızca ilk harfi büyük harfle yazılır.
Örneğin:
numberOfStudents
loopCount
Her ne kadar Python büyük harf-küçük harf duyarlılığı olan bir dil ise de
Python'da küçük harf yoğun bir harflendirme tercih edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
CPU'ya elektriksel olarak doğrudan bağlı belleklere "ana bellek (main memory)"
ya da "birincil bellek (primary memory")
ya da halk arasında "RAM" denilmektedir. RAM'deki her byte'a ilk byte 0 olmak
üzere artan sırada bir sayı karşılık düşürülmüştür.
Bu sayıya "ilgili byte'ın adresi" denilmektedir. Bellekte erişilebilen bölgere
"nesne (object)" denilmektedir. Nesneler bir byte
ya da daha uzun olabilirler. 1 byte'tan daha uzun nesnelerin adresleri onların
en küçük adresiyle ifade edilir.
Bir nesnenin içerisinde tipik olarak bir değer tutulur. Ancak bazı nesnelerin
içerisinde başka nesnelerin adresleri vardır. İşte başka nesnelerin
adreslerini tutan nesnelere C Programlama Dİlinde "pointer", Java ve C# gibi
dillerde ise "reference" denilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da değişkenler her zaman adres tutarlar. Bir değişkene bir değer
atandığında aslında yorumlayıcı değişkene o değerin bulunduğu
yeri adresini atamaktadır. Örneğin:
a = 123
Burada yorumlayıcı RAM'de bir int nesne yaratır. Onun içerisine 123 değerini
yerleştirir. Onun adresini a'ya atar. Birazdan biz şöyle yapmış olalım:
a = 3.14
Şimdi derleyici RAM'de bir float nesne oluşturur. Onun içerisine 3.14 değerini
yerleştirir. Bu kez onun adresini a'ya atar.
Python'da tüm atamalar adres atamasıdır. Biz Python'da "değişken" terimini
adres tutan nesneler için kullanıyoruz. Ancak nesne terimini
değişkenlerin gösterdiği yer için kullanıyoruz. Aslında C, C++ gibi programlama
dillerinin terminolojisinde hem değişkenler hem de
onların gösterdikleri yerdeki değerler birer nesnedir. Ancak Python'da biz
değişken demekle içerisinde adres olan isimli varlıkları kastedeceğiz.
Değişkenlerin gösterdiği yerlere nesne diyeceğiz.
Dinamik tür sistemine sahip programlama dillerinde genel olarak tür bilgisi
değişkenin içerisinde değil nesnenin içerisinde turulmaktadır.
Yani değişken o anda hangi nesneyi gösteriyorsa o türden olmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir değişkenin içerisindeki adres id isimli built-in bir fonksiyonla elde
edilebilir. (Aslında Python referans kitabına göre
id fonksiyonu nesne ile ilgili tek (unique) bir değer vermelidir. Anack tipik
olarak bu değer o nesnenin adresidir.)
a = 123
print(id(a))
a = 'this is a test'
print(id(a))
a = 12.3
print(id(a))
#----------------------------------------------------------------------------------
--------------------------------------
Python'da tüm atamalar aslında adres atamasıdır. Ancak bir değişken atama
dışında kullanıldığında o değişkenin içindeki adresteki nesne kullanılır.
Örneğin:
a = 10
b = 20
c = a + b
>>> a = 10
>>> b = 20
>>> c = a + b
>>> id(a)
140711499592776
>>> id(b)
140711499593096
>>> id(c)
140711499593416
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da tür bilgisi değişkenin içerisinde saklanmaz. Nensenin içerisinde
saklanır. Örneğin:
a = 123
Burada a değişkeni içerisinde 123 olan int bir nesnenin adresini tutulmaktadır.
Bu int nesnenin içerisinde yalnızca 123 değeri yoktur.
Bu nesnenin tür bilgisi de vardır. Ancak biz kursumunda çizimlerde nesnenin
içerisindeki tür bilgisi alanını göstermeyeceğiz.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da tüm atamalar adres atamalarıdır. Bir değişkeni bir değişkene
atadığımızda biz aslında o değişkenin içindeki adresi diğer değişkene atamış
oluruz.
Örneğin:
a = 10
b = a
>>> a = 100
>>> b = a
>>> id(a)
140711499595656
>>> id(b)
140711499595656
a = b
#----------------------------------------------------------------------------------
--------------------------------------
Python'da türler (types), "değiştirilebilir (mutable)" ve "değiştirilemez
(immutable)" olmak üzere ikiye ayrılmaktadır.
Değiştirilebilir türler türünden bir nesne yaratıldığında o nesnenin
içerisindeki değer herhangi bir zaman değiştirilebilir.
Değiştirilemez türler türünden bir nesne yaratıldığında ise o nesnenin
içerisindeki değerler nesne yaratılırken onun içerisine yerleştirilir.
Bir daha da değiştirilemez. Python'ın 6 temel türü de kategorik olarak
"değiştirilemez" türlerdir. Daha açık bir biçimde belirtirsek:
#----------------------------------------------------------------------------------
--------------------------------------
int, float, str, bool, complex, NoneType türlerinin "değiştirilemez" olmasının
ilginç bazı sonuçları söz konusudur. Örneğin:
>>> a = 10
>>> id(a)
140711499592776
>>> a = 20
>>> id(a)
140711499593096
a = 10
print(id(a))
a = 20
print(id(a))
#----------------------------------------------------------------------------------
--------------------------------------
Bu durumda örneğin iki değişken aynı int nesneyi gösteriyor durumda olsun.
Bunlardan birine atama yapıldığında diğerinin değeri
değişmez. Örneğin:
a = 10
b = a
a = 20
Burada int türü değiştirilemez olduğu için yeni bir int nesne yaratılacak onun
içerisine 20 yerleştirilecek ve a da artık bu
yeni nesneyi gösterecektir. Halbuki b içerisinde 10 olan eski nesneyi
göstermektedir. Yani buradaki semantik
C, C++, Java ve C# gibi dillerdekiyle sonuç itibariyle aynı olacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = a
a = 20
print(a) # 20
print(b) # 10
#----------------------------------------------------------------------------------
--------------------------------------
Temel türlerin değiştirlemez olması Python'a geçen pek çok kişi tarafından
yadırganmaktadır. Çünkü böylesi bir tasarım
bazı basit işlemlerde bile yavaşlığa yol açabilmektedir. Örneğin bir döngü
içerisinde sürekli bir değişkeni 1 artırdığımızı
düşünelim. Döngünün her yinelenmesinde yeni bir nesne yaratılacak belki de eski
nesne çöp toplayıcı tarafından silinecektir.
Bunun yavaşlığa yol açacağı muhakkaktır. Her ne kadar döngüleri bilmiyor olsak
da aşağıda böyle bir örnek veriyoruz:
a = 0
for i in range(10):
print(a, id(a))
a = a + 1
0 2172851716304
1 2172851716336
2 2172851716368
3 2172851716400
4 2172851716432
5 2172851716464
6 2172851716496
7 2172851716528
8 2172851716560
9 2172851716592
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'ın geniş bir standart kütüphanesi vardır. Bu standart kütüphanede
neredeyse pek çok konuya ilişkin fonksiyonlar
ve sınıflar hazır bir biçimde bulunmaktadır.
import math
a = [Link](10)
[Link]
[Link]
[Link]
[Link]
#----------------------------------------------------------------------------------
--------------------------------------
import math
result = [Link](10)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Standart kütüphane içerisindeki bazı fonksiyonları ve sınıfları hiç import
işlemi yapmadan doğrudan kullanabilmekteyiz.
İşte bu fonksiyonlara ve sınıflara "built-in" fonksiyonlar ve sınıflar
denilmektedir. Örneğin print ve id fonksiyonları built-in
fonksiyonlardır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da ekrana (yani stdout dosyasına) yazan tek bir print fonksiyonu vardır.
Benzer biçimde klavyeden (yani stdin dosyasından)
okuyan da tek bir input isimli fonksiyon vardır. input fonksiyonu yazı
parametresi alır. Önce yazıyı ekrana basar. Sonra giriş ister.
Kullanıcı bir yazı girerek ENTER tuşuna basar. input fonksiyonu da girilmiş
olan yazıyı bize str nesnesi oalrak verir.
input parantezleri boş bırakılabilir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Tabii Python'da her türlü atama "adres ataması" anlamına geldiğine göre aslında
biz input fonksiyonunun geri dönüş değerini
bir değişkene atadığımızda string nesnesinin adresini o değişkene atamış oluru.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
input fonksiyonu her zaman okunan yazıyı string olarak verir. Eğer biz int ya
da float ya da diğer türlerden okuma yapmak istiyorsak bu yazıyı
int türüne, float türüne ya da diğer türlere dönüştürmeliyiz. Bu dönüştürma
aşağıdaki gibi yapılmaktadır:
print(a * a)
#----------------------------------------------------------------------------------
--------------------------------------
Programın çalışma zamanı sırasında ortaya çıkan ciddi hatalara "exception"
denilmektedir. Bir exception oluştuğunda
programcı oluşan bu exception'ı yakalayıp programın çalışmasını devam
ettirebilir. Ancak programcı exception'ı yakalamazsa
program çöker. Exception işlemleri ileride ayrıntılarıyla ele alınacaktır.
Örneğin biz klavyeden int bir değer okumak isterken
klavyeden girdiğimiz karakterler uygun değilse exception oluşur. Oluşan
exception'ların bir türü vardır. Bu tür genellikle
XXXError biçiminde isimlendirilmiştir. Örneğin ValueError, TypeError gibi.
Aşağıdaki örnekte bir sayı yerine bir isim girerek exception oluşturunuz.
#----------------------------------------------------------------------------------
--------------------------------------
print(a * a)
#----------------------------------------------------------------------------------
--------------------------------------
Python'da altprogramlara "fonksiyon (function)" denilmektedir. Küçük olmayan
bir programın tek parça halinde yazılması iyi bir
teknik değildir. Programlar prosedürel teknikte mantıksal bakımdan parçalara
ayrılır. Parçalar fonksiyonlar biçimin de yazılır.
Sonra da bu fonksiyonlar çağrılarak program çalıştırılır. Bir işin parçalara
bölünüp parçalardan bütünün oluşturulması yalnızca
programlamada değil pek çok mühendislik faaliyette uygulanan bir tekniktir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir fonksiyon ya bir sınıfın içerisindedir ya da sınıfın içerisinde değildir.
Python dünyasında sınıfın içerisindeki fonksiyonlara
"metot (method)" denilmektedir. Yani Python dünyasında "fonksiyon (function)"
sınıfın içerisinde olmayan yordamlar için "metot (method)"
ise sınıfın içerisinde bulunan yordamlar için kullanılmaktadır.
print(a)
print(a, b, c)
print(a + b, c + d)
Bir fonksiyon bir modülün içerisindeyse o fonksiyonu çağırmak için modülün ismi
de kullanılmaktadır. Örneğin:
[Link](10)
gibi.
Ancak bir metodun çağrılması için o metodun içinde bulunduğu sınıf türünden bir
değişkenin bulunuyor olması gerekir. Metot çağırma işleminin
genel biçimi de şöyledir:
Bir modül içerisindeki fonksiyonu çağırma biçimiyle bir metodu çağırma biçimi
birbirne benzemektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Son 20 yıldır programalama anlatan dokümanlarda öylesine uydurulmuş fonksiyon
isimleri için "foo", "bar", "tar" gibi
isimler kullanılmaktadır. Biz de kursumuzda çeşitli örneklerde bu isimleri
kullanacağız. Bu isimlerin hiçbir özel anlamı
yoktur. Öylesine uydurulmuş isimlerdir. Örneğin:
foo()
bar()
tar()
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da is operatörü iki değişkenin aynı nesneyi gösterip göstermediğini
anlamak için kullanılmaktadır. Bu operatör bool bir değer
üretmektedir. Eğer bu operatör True değer üretirse iki değişken aynı nesneyi
gösteriyor durumdadır (yani onların içerisinde aynı adres vardır). False
değer üretirse bu durum iki değişkenin aynı nesneyi göstermediği anlamına
gelir. Bu durumda a is b ile id(a) == id(b) aynı anlamdadır.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
result = a is b
print(result) # False
a = 100
b = a
result = a is b
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
is not operatörü de is operatörünün tersi işlemi yapmaktadır. Yani a is not b
işlemi eğer a'nın içerisindeki adres b'nin içerisindeki
adresten farklıysa True değerini, aynısya False değerini üretmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
result = a is not b
print(result) # True
a = 100
b = a
result = a is not b
print(result) # False
#----------------------------------------------------------------------------------
--------------------------------------
Python'da None değerini içeren tek bir nesne vardır. Dolayısıyla biz farklı
değişkenlere None değerini atadığımızda
aslında bu değişkenlere aynı adresi atamış oluruz. Örneğin:
a = None
b = None
#----------------------------------------------------------------------------------
--------------------------------------
Derleyiciler ve yorumlayıcılar kodu daha hızlı çalışacak ve/veya daha az yer
kaplayacak biçimde yeniden düzenleyebilmektedir.
Buna "kod optimizasyonu" denilmektedir. Optimizasyon kodun anlamı değişmeyecek
biçimde özenle yapılmaktadır. Örneğin
Python'da farklı değikenlere aynı sabitleri atadığımızda temel türler
değiştirilebilir olmadığı için aslında yorumlayıcı
bu sabitler için tek bir yer ayırıp değişkenlere aynı nesnenin adresini
atayabilmektedir. Örneğin:
>>> a = 10
>>> b = 10
>>> c = 8 + 2
>>> a is b
True
>>> a is c
True
>>> b is c
True
>>> id(a)
140732013605960
>>> id(b)
140732013605960
>>> id(c)
140732013605960
a = 12345
b = 12345
result = a is b
print(result)
s = 'ali'
k = 'ali'
result = s is k
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Bir işleme yol açan işlem sonucunda bir değerin üretilmesini sağlayan atomlara
operatör (operator) denilmektedir. Operatörler
genellikle üç biçimde sınıflandırılmaktadır:
1) İşlevlerine Göre
2) Operand Sayılarına Göre
3) Operatörün Konumuna Göre
İşlevlerine göre sınıflandırma operatörün hangi konu ile ile ilgili işlem
yaptığı ile ilgilidir. Tipik sınıflar şunlardır:
#----------------------------------------------------------------------------------
--------------------------------------
Bir ifadede birden fazla operatör varsa bu operatörler belli bir sırada
yapılır. Buna "operatörler arasındaki öncelik ilişkisi
(operator precedency)" denilmektedir. Örneğin:
a = b + c * d
İ1: c * d
İ2: b + İ1
İ3: a = İ2
a = (b + c) * d
İ1: (b + c)
İ2 = İ1 * d
İ3: a = İ2
Python'da pek çok operatör bulunduğu için programcının hangi operatörün hangi
operatörden daha öncelikli olduğunu bilmesi gerekmektedir.
() soldan sağa
* / soldan sağa
+ - soldan sağa
= sağdan sola
Bu durumda örneğin:
a = b - c * d + e
İ1: c * d
İ2: b - İ1
İ3: İ2 + e
İ4: a = İ3
#----------------------------------------------------------------------------------
--------------------------------------
*, /, + ve - operatörleri iki operand'lı araek aritmetik operatörlerdir. Bunlar
temel dört işlemi yaparlar. Öncelik tablosounda
* ve / operatörleri + ve - operatörlerinden daha yüksek önceliklidir.
() soldan sağa
* / soldan sağa
+ - soldan sağa
= sağdan sola
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da / operatörünün her iki operandı int olsa bile bu operatör float bir
değer üretmektedir. Bu anlamda / operatörünün
davranışı C, C++, Java ve C#'takinden farklıdır.
#----------------------------------------------------------------------------------
--------------------------------------
result = 10 / 4
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da noktadan sonraki kısmı atan // biçiminde ayrı bir operatör de vardır.
Bu operatöre "floordiv" operatörü denilmektedir.
Bu operatörde elde edilen sonuç pozitifse sayının noktadan sonraki kısmı
atılır. Tam kısmı elde edilir. Operandların her ikisi de int ise
sonuç int türden olur. Operand'ların en az biri float türdense sonuç float
olur.
#----------------------------------------------------------------------------------
--------------------------------------
result = 10 // 4
print(result, type(result)) # 2 <class 'int'>
result = 10. // 4
print(result, type(result)) # 2.0 <class 'float'>
#----------------------------------------------------------------------------------
--------------------------------------
// operatörü sonuç üzerinde "floor" işlemi uygulamaktadır. floor işlemi bir
noktalı sayının kendisinden küçük ya da ona eşit olan
en yakın tamsayıya dönüştürülmesine denilmektedir. Bu davranış C, C++, Java ve
C#'takinden farklıdır. Örneği Python'da 10 // 4 bize 2 verir.
Çünkü 2.5 değerinin floor işlemine sokulmasından 2 değeri elde edilmektedir.
Ancak -10 // 4 bize -3 verir. Çünkü -2.5 değerinin
floor işlemine sokulmasından -3 elde edilmektedir. C, C++, Java ve C#'taki
davranışa "truncation toward zero" denilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
result = -10 // 4
print(result, type(result)) # -3 <class 'int'>
#----------------------------------------------------------------------------------
--------------------------------------
() soldan sağa
* / // soldan sağa
+ - soldan sağa
= sağdan sola
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
% operatörü iki operandlı araek bir aritmetik operatördür. Bu operatör soldaki
operandın sağdaki operanda bölümünden elde edilen
kalan değerini üretir. Operatörün her iki operandı da int türündense sonuç int
türünden elde edilir. Operandlardan en az biri float ise
sonuç float türden olur. % operatörü de öncelik tablosunda * , / ve //
operatörü ile aynı satırda bulunmaktadır:
() soldan sağa
* / // % soldan sağa
+ - soldan sağa
= sağdan sola
#----------------------------------------------------------------------------------
--------------------------------------
result = 20 % 3
print(result, type(result)) # 2 <class 'int'>
result = 20.5 % 3
print(result) # 2.5 <class 'float'>
#----------------------------------------------------------------------------------
--------------------------------------
a sayısını b ye böldüğümüzde bölüm c kalan d ise aslında a = b * c + d ilişkisi
söz konusudur. Bu nedenle Python'da negatif bir sayının
pozitif bir sayıya bölümünden elde edilen kalan pozitif olmaktadır. Yani
örneğin -10 % 4 işleminin sonucu 2'dir. Çünkü -4 * 3 + 2 = -10'dur.
Bu opratörden elde edilen kalan her zaman sağ tarafındaki operandın
işartindendir. Operatör yukarıda da belirttiğimiz şu eşitliği
sağlamaktadır: a == (a // b) * b + d.
#----------------------------------------------------------------------------------
--------------------------------------
result = -10 % 4
print(result, type(result)) # 2 <class 'int'>
result = 10 % -4
print(result, type(result)) # -2 <class 'int'>
#----------------------------------------------------------------------------------
--------------------------------------
İşaret + ve işaret - operatörleri tek operandlı önek (unary prefix) aritmetik
operatörlerdir. İşaret - operatörü operandının negatif
değerini üretir. İşaret + operatörü operandı ile aynı değeri üretir. Bu iki
operatör de öncelik tablosunda diğer aritmetik operatörlerden
yüksek öncelikli biçimde "sağdan sola" grupta bulunmaktadır:
() soldan sağa
+ - sağdan sola
* / // % soldan sağa
+ - soldan sağa
= sağdan sola
Örneğin:
a = ----1
İ1: -1 => -1
İ2: -İ1 => 1
İ3: -İ2 => -1
İ4: -İ3 => 1
İ5: a = İ4 => 1
Örneğin:
a = 3----1
İ1: -1 => -1
İ2: -İ1 => 1
İ3: -İ2 => -1
İ4: 3 - İ2 => 4
İ5: a = İ4 => 4
#----------------------------------------------------------------------------------
--------------------------------------
a = 3----1
print(a) # 4
#----------------------------------------------------------------------------------
--------------------------------------
Python'da üs almak için ** operatörü bulundurulmuştur. ** iki operandlı araek
bir operatördür. Bu operatör soldaki operandının sağdaki
kuvvetini elde eder. Sayının 0.5'inci kuvvetinin karekök anlamına geldiğini
anımsayınız. Sayının negatif kuvvetlerinin de geçerli olduğuna dikkat ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
result = 3 ** 3
print(result) # 27
result = 3 ** 1.7
print(result) # 6.473007839923779
result = 3 ** 0.5
print(result) # 1.7320508075688772
result = 2 ** -2
print(result) # 0.25
#----------------------------------------------------------------------------------
--------------------------------------
** operatörü öncelik tablosunda işaret + ve işaret - operatörlerinden daha
yüksek öncelikli sağdan sola bir grupta bulunmaktadır.
Bu operatörün öncelik tablosundaki yeri matematiksel alana uygun olsa da
programlama dillerine göre biraz yadırganmaktadır:
() soldan sağa
** sağdan sola
+ - sağdan sola
* / // % soldan sağa
+ - soldan sağa
= sağdan sola
#----------------------------------------------------------------------------------
--------------------------------------
result = -3 ** 2
print(result) # -9
result = (-3) ** 2
print(result) # 9
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
** operatörünün sağdan sola öncelikli grupta olduğuna dikkat ediniz. Yani ifade
içerisinde birden fazla ** operatörü varsa
önce sağdaki yapılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
result = 2 ** 3 ** 2
print(result) # 512
result = (2 ** 3) ** 2
print(result) # 64
#----------------------------------------------------------------------------------
--------------------------------------
Python'da 6 karşılaştırma operatörü vardır. Karşılaştırma operatörlerinin hepsi
iki operandlı araek (binary infix) operatörlerdir:
result = 3 > 2
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
bool türünden değerler karşılaştırma operatörleri de dahil olmak üzere diğer
türlerle işleme sokulursa önce int türüne dönüştürülürler
sonra karşılaştırma işlemine girerler. bool değerler int türüne dünüştürülürken
True 1 olarak, False ise 0 olarak dönüştürülmektedir.
Örneğin:
Bu işlemde operandlardan biri bool diğeri int türdendir. Bu durumda True int
türüne 1 olarak dönüştürülecek ve sonuç True olarak
elde edilecektir. Benzer biçimde iki bool türü de kendi aralarında
karşılaştırma işlemine sokulursa önce operand'lar int türüne
dönüştürülür, sonra karşılaştırma yapılır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Python'da karşılaştırma operatörleri kombine edildiğinde "and" etkisi oluşur.
Örneğin a < b < c işlemi geçerlidir ve bu işlem
a < b and b < c anlamına gelmektedir. Ya da örneğin a == b != c geçerlidir. Bu
işlem a == b and b != c anlamına gelmektedir. Örneğin:
a == b == c
result = 10 < val < 20 # eşdeğeri: 10 < val and val < 20
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Karşılaştırma operatörleri öncelik tablosunda aritmetik operatörlerdne düşük
öncelikli gruplardadır.
() soldan sağa
** sağdan sola
+ - sağdan sola
* / // % soldan sağa
+ - soldan sağa
< > <= >= == != soldan sağa
= sağdan sola
Yani örneğin:
result = a + b > c + d
#----------------------------------------------------------------------------------
--------------------------------------
Python'da üç mantıksal operatör vardır: and, or ve not operatörleri. and ve or
operatörleri iki operandlı araek (binary infix),
not operatörü ise tek operandlı önek (unary prefix) operatörlerdir.
() soldan sağa
** sağdan sola
+ - sağdan sola
* / // % soldan sağa
+ - soldan sağa
< > <= >= == != soldan sağa
not sağdan sola
and soldan sağa
or soldan sağa
= sağdan sola
Örneğin:
Örneğin:
result = 3 + 2 or False
print(result) # 5
Örneğin:
Örneğin:
Örneğin:
result = 10 and 20
print(result) # 20
#----------------------------------------------------------------------------------
--------------------------------------
and ve or operatörleri aynı ifadede kullanıldığında her zaman sol taraftaki
operatörün sol tarafı önce yapılmaktadır.
Örneğin:
Burada önce ifade1 yapılır eğer ifade1 True ise ifade2 yapılır ve and
operatmründen ifade2 elde edilir. ifade2 True
ise ifade3 yapılmaz sonuç olarak ifade2 elde edilir. Örneğin:
Yani bu ifade aslında ifade1 ile (ifade and ifade3)'ün or'lanması anlamına
gelmektedir. and ve or operatörlerinin her zaman sol tarafı
önce yapılacağına göre burada önce ifade1 yapılır. Eğer ifade1 True ise
doğurdan ifade1'in değeri elde edilir. Eğer ifade1
False ise bu kez ifade2 yapılır. ifade2 False ise ifade3 hiç yapılmaz,
ifade2'nin sonucu elde edilir. Eğer ifade1 False ancak
ifade2 True ise ifade3'ün sonucu elde edilir. Özetle and ve or operatörleri
kombine edildiğinde her zaman en soldaki operatörün en solu
önce yapılacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
result = 10 and 0 or 5
print(result) # 5
result = 0 and -5 or 5
print(result) # 5
result = 0 or 0 and 5
print(result) # 0
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
not operatörü tek operandlı önek (unary prefix) mantıksal operatördür. Bu
operatör True değerini False, False değerini True yapar.
Operatörün ürettiği değer her zaman bool türdendir. Bu operatörün de
operandları int ya da float türünden olabilir. Sıfır dışı değerler
(nonzero değerler) True olarak sıfır değeri False olarak ele alınır.
#----------------------------------------------------------------------------------
--------------------------------------
result = not 0
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
not operatörü öncelik tablosunda and ve or operatörlerinden daha yüksek
önceliklidir ve kandi içerisinde sağdan sola
grupta bulunmaktadır.
() soldan sağa
** sağdan sola
+ - sağdan sola
* / // % soldan sağa
+ - soldan sağa
< > <= >= == != soldan sağa
not sağdan sola
and soldan sağa
or soldan sağa
= sağdan sola
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da mantıksal not operatörü C, C++, Java ve C# gibi dillerle
kıyaslandığında düşük önceliklidir. Özellikle bu dillerden geçen
kişiler Python'da not operatörünün aritmetik ve karşılaştırma operatörlerinden
daha düşük öncelikli olduğuna dikkat etmelidir. Örneğin:
result = not a + b
işleminde ya da:
#----------------------------------------------------------------------------------
--------------------------------------
Python'da boş olmayan bir string mantıksal olarak True, boş bir string False
biçiminde ele alınmaktadır. Başka bir deyişle
dolu string'ler bool türüne True olarak boş string'ler False olarak
dönüştürülmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da None değeri mantıksal olarak ele alınacağı zaman her zaman False
biçimde ele alınır. Bşka bir deyişle
None değeri bool türüne her zaman False olarak dönüştürülür.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Ayrıca henüz görmemiş olsak da önemli türler bool türüne şöyle
dönüştürülmektedir:
#----------------------------------------------------------------------------------
--------------------------------------
Python'da atama işlemi aslında bir operatör değil bir deyim statüsündedir.
Ancak biz geleneksel olarak burada atama işlemi için
"operatör" terimini kullanacağız. Bir operatör olarak atama operatörü iki
operandlı araek bir operatördür. Anımsanacağı gibi Python'da
tüm atama işlemleri aslında adres ataması anlamına gelmektedir. Örneğin:
a = b
Burada b'nin içerisindeki değer a'ya atanmamaktadır. b'nin içerisindeki adres
a'ya atanmaktadır. Çünkü bütün değişkenler adres tutmaktadır.
Bu işlemin sonucunda a is b ifadesi True değerini verecektir.
Örneğin:
a = 10
() soldan sağa
** sağdan sola
+ - sağdan sola
* / // % soldan sağa
+ - soldan sağa
< > <= >= == != soldan sağa
not sağdan sola
and soldan sağa
or soldan sağa
= sağdan sola
Örneğin:
a = b = 10
İ1: b = 10
İ2: a = İ1
Yani burada 10 değeri bir nesneye yerleştirilecek, o nesnenin adresi hem b'ye
hem de a'ya atanacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da atama işlemi aslında bir operatör olarak değil deyim olarak
değerlendirilmektedir. Dolayısıyla atama işleminden bir değer elde edilmez.
Örneğin biz C, C++, Java ve C# gibi dillerde atama operatörünü parantez
içerisine alıp oradan elde edilen değeri diğer operatörlerle
işleme sokabiliriz. Ancak Python'da bunu yapamayız.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Atama işleminden Python'da bir değer elde edilememesi yüzünden diğer dillerde
yapılan bazı faydalı işlemler Python'da
atama işlemiyle yapılamamaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Python'a 3.8 versiyonu ile birlikte (2019 Ekim) "Walrus operatörü" diye
isimlendirilen değer üreten bir atama operatörü de eklenmiştir.
Bu operatör sayesinde yukarıda belirttiğimiz diğer dillerde yapamadığımız
işlemleri yapabiliriz. Walrus operatörü := sembolü ile
temsil edilmektedir. İki operandlı araek bir operatördür.
#----------------------------------------------------------------------------------
--------------------------------------
a = (b := 10) + 20
print(a, b) # 30 10
#----------------------------------------------------------------------------------
--------------------------------------
Python'da gereksiz bir biçimde Walrus operatörünün kullanılması konusunda
programcının cesaretini kırmak için gereksiz
Walrus operatörünün kullanılması error oluşturmaktadır. Örneğin:
a := 10
a = (b := 10) + 20
a := b := 10
(a := 10)
a := 10 # error!
(a := 10) #geçerli
Örneğin:
b := (a := 10) # error!
Fakat örneğin:
b = (a := 10) # geçerli
(b := (a := 10)) # geçerli
Örneğin:
a = 10
print(a)
Özetle atama operatörü ile yapabileceğimiz bir işlemi Walrus operatörü ile
yapmaya çalışırsak bu durum error oluşturur. Ancak error oluşturmasın istiyorsak
paranteze almalıyız. Ancak atama operatörü ile yapamadığımız bir şeyi Walrus
operatörüyle yapabiliyorsak bu durum error oluşturmaz. Örneğin:
print(a := 10)
Biz bu işlemi atama operatör ile yapamazdık. O halde burada Walrus operatörünün
kullanılması geçerlidir. Ancak örneğin:
a := 10 # error!
if, while gibi deyimlerde zaten atama operatörü kullanılamadığı için Walrus
operatörünün paranteze alınması gerekmez. Bu tür deyimlerde
atanan değer doğrudan işleme sokulmaktadır. Örneğin :
while s := input():
pass
() soldan sağa
** sağdan sola
+ - sağdan sola
* / // % soldan sağa
+ - soldan sağa
< > <= >= == != soldan sağa
not sağdan sola
and soldan sağa
or soldan sağa
= := sağdan sola
#----------------------------------------------------------------------------------
--------------------------------------
Python'da ++ ve -- biçiminde operatörler yoktur. ++ ve -- operatörleri C, C++,
Java ve C# gibi dillerde vardır ve çok kullanılmaktadır.
Bu dillerde ++ operatörü değişkenin içerisindeki değeri 1 artıtmak için, --
operatörü değişken içerisindeki değeri 1 eksiltmek için
kullanılmaktadır. Python'da değişkenin içerisindeki değeri 1 artırmak ve 1
eksiltmek için sonraki paragrafta açıklayacak olduğumuz
+= ve -= gibi işlemli atama operatörleri (augmented assignment operators) ile
yapılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da +=, -=, *=, /=, //=, %= gibi bir grup işlemli atama operatörü
(augmented assignment statments) vardır. Bu operatörler iki operandlı araek
operatörlerdir. op bir operatör belirtmek üzere:
a op= b
a = a op b
a += 1
a -= 1
>>> a = 10
>>> id(a)
140712253256776
>>> a += 1
>>> a
11
>>> id(a)
140712253256808
#----------------------------------------------------------------------------------
--------------------------------------
İşlemli atama operatörleri atama operatörleriyle sağdan sola aynı öncelik
grubunda bulunmaktadır:
() soldan sağa
** sağdan sola
+ - sağdan sola
* / // % soldan sağa
+ - soldan sağa
< > <= >= == != soldan sağa
not sağdan sola
and soldan sağa
or soldan sağa
= := , +=, -=, *=, ... sağdan sola
#----------------------------------------------------------------------------------
--------------------------------------
*=, /= ve //= operatörlerinin atama operatörü önceliğinde olduğuna dikkat
ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
a *= 2 + 1
print(a) # 30
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi print fonksiyonu çıktıları imlecin bulunduğu yerden itibaresn
ekrana (stdout dosyasına) yazdırıp imleci
aşağıda satırın başına geçirmektedir. print fonksiyonu değişken sayıda argüman
alabilmektedir. Yani biz print sonksiyonu
ile birden fazla ifadeyi yazdırabiliriz. Bu durumda print fonksiyonu her
argümanın değerini yazdırdıktan sonra bir
SPACE boşluk bırakır. print fonksiyonu tüm işlemini bitirince '\n' karakterini
ekrana (stdout dosyasına) yazdırmaktadır.
Yani print işleminden sonra imleç aşağı satırın başına geçmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10; b = 20; c = 30
print(a, b, c) # 10 20 30
#----------------------------------------------------------------------------------
--------------------------------------
print fonksiyonuyla birden fazla argümanı yazdırırken her argümandan sonra
print araya bir SPACE karakteri bırakmaktadır.
Ancak programcı isterse "sep" isimli parametreyle boşluk yerine argümanlar
arasında istenilen yazının bastırılmasını sağlayabilir.
sep için girilen yazının tek karakter olması gerekmemektedir.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
c = 30
#----------------------------------------------------------------------------------
--------------------------------------
sep aparemtresinin hiç girilmemesiyle bunun sep=' ' biçiminde girilmesi
arasında bir fark olmadığına dikkat ediniz.
Yani sep parametresinin default değerini ' ' olarak düşünebilirsiniz.
Aşağıdaki örnekte her argümandan sonra print ", " yazısını ekrana (stdout
dosyasına) bastırmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
c = 30
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte her argüman yazdırıldıktan sonra imleç aşağıda staırın başına
geçirilecek ve dolayısıyla argümanlar
satır satır alt alta yazdırılmış olacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
c = 30
print(a, b, c, sep='\n')
#----------------------------------------------------------------------------------
--------------------------------------
print fonksiyonunun "end" isimli parametresi tüm yazdırma işlemi bittiğinde
ekrana basılacak yazıyı belirtmektedir. Default durumda
bu yazı '\n' biçimindedir. Yani imleç aşağı satırın başına geçirilir. Ancak biz
end parametresi yoluyla print fonksiyonunun işlemini bitirdikten
sonra istediğimiz bir yazıyı ekrana bastırmasını isteyebiliriz. Örneğin:
10*20*30---ankara
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
c = 30
# # 10xxx20xxx30yyyOk
#----------------------------------------------------------------------------------
--------------------------------------
Bilindiği gibi bir değişkenin içerisinde belli bir türden nesnenin adresi
olabilir. Örneğin:
a = 12.3
Burada a değişkeninin içerisinde float bir nesnenin adresi vardır. Yani biz
a'yı kullandığımızda float bir değeri işleme sokmuş oluruz.
İşte bir nesnenin içerisindeki değeri değiştirerek başka bir türden bir nesne
biçiminde elde etme işlemine programlama dillerinde
"tür dönüştürmesi (type conversion / type cast)" denilmektedir. Örneğin biz
yukarıdaki a değişkeninin gösterdiği nesnenin içerisindeki değeri
int bir nesne biçiminde elde etmek isteyebiliriz.
<dönüştürülecek tür>(<ifade>)
Örneğin:
a = 13.2
b = int(a)
Tür dönüştürme işlemiyle yeni bir nesne yaratılmaktadır. Her tür dönüştürme
işlemi yeni bir nesnenin yaratılmasına yol açmaktadır. Yani tür
dönüştürmesi mevcut nesnesinin türünü değiştirmemektedir. Mevcut nesnenin
değerindne hareketle arzu edilen türden yeni bir nesnenin yaratılmasını
sağlamaktadır. Örneğin:
a = 13.2
b = int(a)
Burada a'nın türü değiştirilkmemiştir. Yeni bir int nesne yaratılmış ve onun
adresi b'ye atanmıştır.
Aslında T bir tür belirtmek üzere T türünden bir nesnenin yaratılması T(...)
biçiminde bir ifadeyle yapılmaktadır.
Bu konu ileride "sınıflar" konusunda ayrıntılarıyla ele alınacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
float bir değer int türüne dönüştürüldüğünde noktadan sonraki kısmı atılmış
olan bir int değer elde edilir. Burada yuvarlama
yapılmadığına float değer negatif de olsa pozitif de olsa noktadan sonraki
kısmın atıldığına dikkat ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
a = 13.99
print(id(a))
b = int(a)
print(id(b))
print(a) # 13.99
print(b) # 13
a = -13.99
b = int(a)
print(b) # -13
#----------------------------------------------------------------------------------
--------------------------------------
bool bir değer int türüne dönüştürülürse eğer değer True ise 1, False ise 0
elde edilir.
#----------------------------------------------------------------------------------
--------------------------------------
a = True
b = int(a)
print(b) # 1
a = False
b = int(a)
print(b) # 0
#----------------------------------------------------------------------------------
--------------------------------------
Bir string'i int türüne dönüştürürken string içerisindeki yazı int türü için
anlamlı sayısal karakterlerden oluşuyorsa
yeni bir int nesne yaratılır ve yazı int bir nesne biçiminde ifade edilir.
Ancak yazının içerisindeki karakterler int
türü için anlamlı değilse bu durumda exception (ValueError) oluşur. Daha
önceden de belirttiğimiz gibi bir exception oluştuğunda eğer
exception programcı tarafından ele alınmadıysa program çökmektedir. String'in
başındaki boşluk karakterlerine "leading space", string'in
sonundaki boşluk karakterlerine "trailing space" denilmektedir. Dönüştürme
sırasında yazının başındaki ve sonundaki boşluk karakterleri
dikkate alınmamaktadır. Örneğin:
>>> a = '123'
>>> b = int(a)
>>> b
123
>>> a = '12.3'
>>> b = int(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '12.3'
>>> a = ' -123 '
>>> b = int(a)
>>> b
-123
#----------------------------------------------------------------------------------
--------------------------------------
a = '123'
b = int(a)
print(b) # 123
a = 'ali'
print('unreachable code')
#----------------------------------------------------------------------------------
--------------------------------------
Bir string'i int türüne dönüştürürken string içerisindeki yazının kaçlık
sistemde bir sayı belirttiğini de int fonksiyonunun
ikinci parametresiyle ifade edebiliriz. Örneğin:
a = '100'
b = int(a)
a = '100'
b = int(a, 16)
Burada artık biz '100' yazısında belirtilen sayının 16'lık sistemde bir sayı
olması gerektiğini belirtiyoruz. Örneğin:
a = '100'
b = int(a, 2)
>>> s = '0x1234'
>>> a = int(s, 16)
>>> a
4660
>>> s = '0o1234'
>>> a = int(s, 8)
>>> a
668
>>> s = '0b10101'
>>> a = int(s, 2)
>>> a
21
#----------------------------------------------------------------------------------
--------------------------------------
a = '0x10'
b = int(a, 16)
print(b) # 16
a = '0b1010'
b = int(a, 2)
print(b) # 10
a = '0x10'
b = int(a) # exception oluşur!
print(b)
#----------------------------------------------------------------------------------
--------------------------------------
Complex türünden int türüne dönüştürme geçerli kabul edilmemektedir. Böyle bir
dönüşüm yapılmak istendiğinde exception (TypeError)
oluşmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
None değerinin int türüne dönüştürülmesi de Python'da geçerli kabul
edilmemektedir. Bu dönüştürme de exception'a (TypeError) yol açmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
int bir ifadenin int türüne dönüştürülmesi geçerlidir ancak anlamsızdır. Bu
durumda dönüştürüleek ifade ile aynı değer
elde edilir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
O halde int türüne dönüştürme özetle şöyle yürütülmektedir:
1) float bir değer int türüne dönüştürüldüğünde noktandan sonraki kısım atılırç
3) bool bir değer int türüne dönüştürüldüğünde False için 0, True için 1 elde
edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
int bir değer float türüne dönüştürülürken eğer int değer float formatıyla
(IEEE 754 long real format) tam olarak ifade
edilebiliyorsa .0 biçiminde float türüne kayıp olmadan dönüştürülür. Ancak
Python'da int değerlerin bir sınırının olmadığını anımsayınız.
Bu durumda çok büyük int değerler float türüne dönüştürülemeyecektir. İşte bu
tür durumlarda exception (OverrlowError) oluşmaktadır.
Bazı int değerler mantis kayıplarıyla basamaksal bir kayıp olmadan float türe
dönüştürülebilmektedir. Bu durumda bir exception
oluşmaz. Sayı float türüyle tam olarak ifade edilemese bile mantis kaybıyla ona
en yakın büyük ya da ona en yakın küüçük
sayı elde edilmektedir. Ancak basamaksal kayıplar exception'a (OverflowError)
yol açmaktadır. Örneğin:
>>> a = 12345678901234567890123456789
>>> b = float(a)
>>> b
1.2345678901234568e+28
>>> int(b)
12345678901234568227576610816
>>> a =
18172368172638172638712638712638712638716238761238761287361872638712638172638712638
71623812638716238716238761238
76128376182736187236871263817263871263871263871263817263812638712638712361872361827
3618273618723681723618236182361827361
28361283612836187236187236182368172638172638712638712638172368712638172638712638712
6381723618276387126387126387123
>>> b = float(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too large to convert to float
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir string float türüne dönüştürülebilir. Bu durumda string float ile ifade
edilebiliyorsa (yani string'i oluşturan karakterler
float sayı için anlamlıysa) dönüştürme yapılır. String float türüyle ifade
edilemiyorsa exception (ValueError) oluşur.
float fonksiyonu her zaman tek parametrelidir. flaot türü ile ifade
edilemeyecek çok büyük büyük ya da küçük yazısal değerlerin
float türüne dönüştürülmesi sırasında ne olacağı konusunda "Python Standard
Library Reference" açık bir şey söylememiştir.
Mevcut CPythob gerçekleştirimi bu durumda +inf ya da -inf değerlerini
üretmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
b = float(a)
print(b)
a = '123.23ali'
#----------------------------------------------------------------------------------
--------------------------------------
bool türünden bir ifade float türüne dönüştürüldüğünde eğer bool değer True ise
1.0 biçiminde false ise 0.0 biçiminde
float bir değer elde edilir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Complex bir ifade float türüne dönüştürülemez. Dönüştürülmek istenirse
exception (TypeError) oluşur. Benzer biçimde None değeri de
flaot türüne döüştürülememektedir. Dönüştürülmek istenirse yine exception
(TypeError) oluşmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
O halde float türüne dönüştürme özetle şöyle yürütülmektedir:
1) int bir değer float türüne bilgi kaybı olmadan, mantissel kayıp ile
dönüştürülebilir. Ancak basamaksal bir kayıp
oluşacaksa dönüştürme exception (OverflowError) ile sonuçlanır.
3) bool türü float türüne dönüştürülürse True için 1.0, False için 0.0 değeri
elde edilir.
#----------------------------------------------------------------------------------
--------------------------------------
Bir int değer bool türüne dönüştürülürken eğer değer sıfır dışı bir değerse
True olarak 0 değeri ise False olarak dönüştürülür.
Benzer biçimde float bir değer de bool türüne aynı biçimde dönüştürülmektedir.
(Sıfır dışı değer demekle 0'ın dışında pozitif ya da
negatif herhangi bir değer kastedilmektedir.) None değer bool türüne her zaman
False olarak dönüştürülmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
a = 123801928309123
b = bool(a)
print(b) # True
a = -12.34
b = bool(a)
print(b) # True
a = None
b = bool(a)
print(b) # False
#----------------------------------------------------------------------------------
--------------------------------------
Bir string bool türüne dönüştürülürken string'in içerisindeki yazıya bakılmaz.
String'in boş mu dolu mu olduğuna bakılır.
Dolu bir string bool türüne True olarak, boş bir string False olarak
dönüştürülmektedir. Örneğin aşağıdaki dönüştürme
True değerini verecektir:
s = 'False'
b = bool(s)
print(b) # True
#----------------------------------------------------------------------------------
--------------------------------------
a = 'ankara'
b = bool(a)
print(b) # True
a = 'False'
b = bool(a)
print(b) # True
a = ''
b = bool(a)
print(b) # False
#----------------------------------------------------------------------------------
--------------------------------------
Ayrıca boş listeler, demetler, sözlükler, kümeler bool türüne False biçiminde,
dolu listeler, demetler, sözlükler ve kümeler de True biçiminde
dönüştürülürler. Listeler, demetler, sözlükler, kümeler konusu izleyen
bölümlerde ele alınmaktadırç
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir int değer ya da bir float değer complex türüne dönüştürülebilir. Bu durumda
sanal kısmı 0 olan bir complex sayı elde edilir.
bool bir değer complex türüne dönüştürülürse gerçek kısmı 1 ya da 0 olan sanal
kısmı 0 olan bir complex sayı elde edilmektedir.
Dönüştürülecek tür ne olursa olsun complex türünün gerçek ve sanal kısımları
float türdendir. Örneğin:
>>> z = 3j+1
>>> [Link]
1.0
>>> [Link]
3.0
>>> a = 10
>>> z = complex(a)
>>> z
(10+0j)
>>> [Link]
10.0
>>> [Link]
0.0
complex sayılar print edilirken her gerçek ve sanal kısımları tamsayı ise güzel
bir görünüt oluşturmak için nokta kullanılmamaktadır.
Ancak aslında her durumda complex türünün gerçek ve sanal kısımları float bir
sayı biçimindedir.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = complex(a)
print(b) # (10+0j)
a = 10.5
b = complex(a)
print(b) # (10.5+0j)
a = True
b = complex(a)
print(b) # (1+0j)
#----------------------------------------------------------------------------------
--------------------------------------
Bir string de complex türüne dönüştürülebilir. 'a+bj' biçimindeki bir yazının
başında ve sonunda boşluk karakterleri bulunabilir.
Ancak arasında bulunamaz. Ayrıca yazı 'aj+b' biçiminde de belirtilemektedir.
Örneğin:
>>> s = '2+3j'
>>> z = complex(s)
>>> print(z)
(2+3j)
>>> s = '10'
>>> z = complex(s)
>>> print(z)
(10+0j)
>>> s = '3j-2'
>>> z = complex(s)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: complex() arg is a malformed string
#----------------------------------------------------------------------------------
--------------------------------------
a = '3+2j'
b = complex(a)
print(b) # (3+2j)
a = '10'
b = complex(a)
print(b) # (10+0j)
a = 10.5
b = complex(a)
print(b) # (10.5+0j)
#----------------------------------------------------------------------------------
--------------------------------------
Temel türlerin hepsi str türüne dönüştürülebilir. Yani örneğin int bir değer,
float bir değer, bool bir değer, complex bir değer,
None değeri str türüne dönüştürülebilmektedir. Dönüştürme sonucunda o değere
ilişkin bir yazı elde edilmektedir. Örneğin:
>>> a = 10
>>> s = str(a)
>>> s
'10'
>>> a = 12.34
>>> s = str(a)
>>> s
'12.34'
>>> a = True
>>> s = str(a)
>>> s
'True'
>>> a = None
>>> s = str(a)
>>> s
'None'
>>> a = 3j+2
>>> s = str(a)
>>> s
'(2+3j)'
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da klavyeden (stdin dosyasından) okuma yapan input isimli tek bir
fonksiyon vardır. input fonksiyonun da str türünden
bir değer verdiğini anımsayınız. Bu durumda biz kalvyeden int, float gibi
okumalar yapmak için aslında string'i bu türlere dönüştürmekteyiz.
Örneğin:
print(a * a)
Ya da örneğin:
print(a * a)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Programlama dillerinde farklı türler ile işlem yapıldığında nasıl bir sonucun
elde edileceği programcı tarafından biliniyor
olması gerekir. C, C++, Java, C# ve Python gibi dillerde farklı türlerle işlem
yapılabilmektedir. Ancak Swift gibi bazı dillerde
farklı türlerle işlemler yapılamamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da iki int değeri aritmetik işleme sokarsak sonuç int türden çıkar.
Benzer biçimde iki float değeri aritmetik işleme sokarsak
sonuc float türden çıkar. Ancak int bir değer ile float bir değeri aritmetik
işleme sokarsak sonuç float türünden çıkacaktır.
Tabii özel bir durum olarak / operatörünün her zaman float değer ürettiğine
dikkat ediniz. Yani biz iki int değeri / operatörüyle
işleme sokarsak sonuç int türden değil float türden çıkacaktır. Mantıksal
operatörlerin de her zaman bool türden değer ürettiğine dikkat
ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 3.14
c = a + b
print(c, type(c)) # 13.14 <class 'float'>
#----------------------------------------------------------------------------------
--------------------------------------
bool türü ile int türü işleme sokulursa sonuç int türünden, bool türü ile float
türü işleme sokulursa sonuç float türünden,
bool türü ile complex türü işleme sokulursa sonuç complex türünden elde
edilmektedir. Bu tür işlemlerde bool değer True ise
1 olarak, False ise 0 olarak işleme sokulmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = True
c = a + b
print(c, type(c)) # 11 <class 'int'>
a = 12.34
c = a + b
print(c, type(c)) # 13.34 <class 'float'>
a = 3j+2
c = a + b
print(c, type(c)) # (3+3j) <class 'complex'>
#----------------------------------------------------------------------------------
--------------------------------------
iki bool değer kendi aralarında işleme sokulduğunda sonuç int türünden
çıkmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
a = True
b = False
c = a + b
#----------------------------------------------------------------------------------
--------------------------------------
int bir değerle float bir değer işleme sokulduğunda sonuç float çıkacaktır
ancak sonuç üzerinde bir kayıp oluşabilir.
Eğer oluşan kayıp basamaksal değilse bu bir hata olarak değerlendirilmez.
Gerçek değer en yakın ondan büyük olan ya da küçük olan değer
elde edilir. Ancak basamaksal bir kayıp söz konusu olursa exception
(OverflowError) oluşmaktadır. Örneğin:
>>> a =
19872398127398172398172398712938719283719872398172398172398712938712983719827398123
79812739182739812739
81723981723987123987129387129837198237918273981723981723987129837192837918273981723
9817298379827349827349872349
87239847293847928374982734982734982734987239847293847928374982374982374982374982374
982374982371234234234234
>>> b = a * 2.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too large to convert to float
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
complex türü ile int türü, float türü ve bool türü aritmetik işleme sokulursa
sonuç complex türünden elde edilmektedir. Örneğin:
>>> a = 3j+2
>>> b = 1
>>> c = a + b
>>> c
(3+3j)
>>> b = 1.2
>>> c = a + b
>>> c
(3.2+3j)
>>> b = True
>>> c = a + b
>>> c
(3+3j)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da iki str türündne değişken toplama işlemine sokulduğunda işlem
sonucunda yine bir str nesnesi elde edilir.
Elde edilen string iki string'in birleşiminden oluşur. Java, C# gibi pek çok
dilde de bu özellik benzer biçimde vardır.
Örneğin:
>>> s = 'ankara'
>>> k = 'istanbul'
>>> result = s + k
>>> result
'ankaraistanbul'
Ancak str türünden bir değişkenle diğer türden bir değer toplanamaz. Oysa Java
ve C# gibi bazı dillerde bu durum mümkündür.
O dillerde bu toplama işlemi yapılırken string olmayan tür otomatik olarak
string türüne dönüştürülüp sonuç string türünden elde
edilmektedir. Ancak yukarıda da belirttiğimiz gibi Python'da bu durum geçerli
değildir. Özellikle diğer dillerden geçen kişilerin
bu duruma dikkat etmesi gerekir. Örneğin:
>>> s = 'Ankara'
>>> a = 6
>>> b = s + a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
Tabii programcı böyle bir şeyi yapmak isterse diğer operandı kendisi str türüne
dönüştürmelidir. Örneğin:
>>> s = 'Ankara'
>>> a = 6
>>> b = s + str(a)
>>> b
'Ankara6'
#----------------------------------------------------------------------------------
--------------------------------------
Aralarında fiziksel ya da mantıksal ilişki olan bir grup nesneden oluşan
topluluğa "veri yapısı (data structure)" denilmektedir.
Veri yapısı çoğul bir anlam ifade etmektedir. Python'da da built-in çeşitli
veri yapıları vardır. Bunlar "listeler", "demetler",
"sözlükler", "kümeler" ve "stringler"dir. Python'daki bu built-in bu veri
yapıları doğrudan dilin sentaksıyla desteklenmiş durumdadır.
Bu da programlamayı oldukça kolaylaştırmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Dolaşılabilir (iterable) nesne demek bu nesne dolaşıldığında birtakım
değerlerin elde edilmesi demektir. Yani dolaşılabilir nesneler
üzerinde dolaşım (iteration) işlemi yapılabilir. Dolaşım işlemi sırasında biz
dolaşılabilir nesneden birtakım değerler elde ederiz.
Dolaşılabilir nesneler genellikle kendi içlerinde birtakım nesneleri tutarlar.
Başka nesneleri tutan nesnelere Java, C# gibi fillerde
"collection", C++'da ise "container" denilmektedir. Örneğin str türü
dolaşılabilir (iterable) bir türdür. Bu durumda str nesnesi de dolaşılabilir
bir nesnedir. Bir string nesnesi dolaşıldığında tek tek stirng'in karakterleri
elde edilmektedir. (Tabii string'in karakterleri de
aslında birer string belirtmektedir.) Özetle:
#----------------------------------------------------------------------------------
--------------------------------------
Listeler Python'da en çok kullanılan veri yapısıdır. Listeler diğer programlama
dillerindeki "dizilere (arrays)"
benzetilebilir. Bir liste yaratmanın çeşitli yolları vardır. Liste yaratmak
için en çok kullanılan yöntem köşeli parantez sentaksıdır.
Köşeli parantezler içerisine ',' atomu ile ayrlmış değerler girilirse bir liste
nesnesi yaratılmaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Liste elemanları aynı türden olmak zorunda değildir. (Halbuki C, C++, Java ve
C# gibi bazı dillerde diziler aynı türden
elemanlara sahip olmak zorundadır.)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Listenin belli bir elemanına [] operatörü ile erişebiliriz. Yani listelerin
elemanları bu sayede sanki bağımsız nesnelermiş gibi
kullanılabilmektedir. Listenin ilk elemanı 0'ıncı indekslş elemanıdır. Bu
durumda n elemanlı bir listenin son elemanı (n - 1)'nci
indekste olacaktır. Köşeli parantez içerisine bir ifade yerleştirilebilir.
Örneğin a bir liste belirtmek üzere a[2], a[i + 2],
a[i + k - 1] gibi kullanımlar geçerlidir. Bu duurmda önce köşeli parantezin
içerisindeki ifadenin sayısal değeri hesaplanır.
Sonra o değerdeki indekse erişilir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Köşeli parantez içerisindeki ifade int türden olma zorundadır.
#----------------------------------------------------------------------------------
--------------------------------------
i = 1
i = 1.0
val = a[i] # exception! köşeli parantez içerisindeki ifade int türden
olmak zorunda
i = 1
val = a[i + 2.] #exception! köşeli parantez içerisindeki ifade int türden olmak
zorunda
#----------------------------------------------------------------------------------
--------------------------------------
Bir listenin olmayan bir elemanına erişmeye çalışırsak IndexError isimli bir
exception oluşur.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Listeler "değiştirilebilir (mutable)" türlerdir. Bir türün değiştirilebilir
olması demek o türdne bir nesnenin içeriğinde
değişiklik yapılabilmesi demektir. Yani list türü değiştirilebilir olduğu iiçin
biz bir listenin elemanlarını atama yoluyla değiştebiliriz.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
a[2] = 'ali'
#----------------------------------------------------------------------------------
--------------------------------------
Bir listedeki eleman sayısı built-in len isimli fonksiyonla elde edilebilir.
len fonksiyonu bize listenin eleman sayısını
int bir değer olarak vermektedir.
#----------------------------------------------------------------------------------
--------------------------------------
n = len(a)
print(n) # 5
#----------------------------------------------------------------------------------
--------------------------------------
Boş liste de söz konusu olabilir. Boş bir liste 0 eleman uzunluğundadır.
#----------------------------------------------------------------------------------
--------------------------------------
a = []
n = len(a)
print(n) # 0
#----------------------------------------------------------------------------------
--------------------------------------
Daha önceden de belirttiğimiz gibi T bir tür belirtmek üzere T(...) hem T
türüne dönüştürme anlamına gelir hem de T türünden
nesne yaratma anlamına gelir. Örneğin int() ifadesinden içinde 0 olan bir int
nesne, float() ifadesinden içinde 0 olan bir float nesne elde ederiz.
bool() ifadesi bize False bir nesne verir. İşte list() ifadesi de içi boş bir
listeyi bize vermektedir. Yani aşağıdaki iki ifade eşdeğerdir:
a = list()
a = []
Örneğin:
>>> a = int()
>>> a
0
>>> b = float()
>>> b
0.0
>>> c = bool()
>>> c
False
>>> d = str()
>>> d
''
>>> e = complex()
>>> e
0j
>>> f = list()
>>> f
[]
#----------------------------------------------------------------------------------
--------------------------------------
a = list()
n = len(a)
print(n) # 0
#----------------------------------------------------------------------------------
--------------------------------------
list sınıfının tür fonksiyonu olan list fonksiyonuna biz dolaşılabilir bir
nesne verebiliriz. Bu durumda bu nesne dolaşılır.
Dolaşım sonucunda elde edilen değerlerden liste oluşturulur. Örneğin str
nesneleri dolaşılabilir nesnelerdir. Yani str sınıfı dolaşılabilir
bir sınıftır. Bir str nesnesi dolaşıldığında tek tek string'in karakterleri
elde edilmektedir. O halde bir list fonksiyonuna
bir string nesnesi verirsek list o string'i dolaşıp o string'in karakterlerini
elde edecek ve o karakterlerden liste oluşturacaktır. Örneğin:
s = 'ankara'
a = list(s)
Burada s string'i dolaşıldığında sırasıyla 'a', 'n', 'k', 'a', 'r', 'a' str
nesneleri elde edilmektedir. İşte bu nesnelerden
liste oluşturulmuştur.
int türü, float türü, bool türü, complex türü, NonType türü "dolaşılabilir
(iterable)" türler değildir.
Bu nedenle biz list fonksiyonuna argüman olarak bu türden değerler veremeyiz.
Eğer bunu yaparsak exception (TypeError) oluşur.
Örneğin:
>>> s = 'ankara'
>>> a = list(s)
>>> a
['a', 'n', 'k', 'a', 'r', 'a']
>>> k = 123
>>> a = list(k)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankara'
a = list(s)
print(a) # ['a', 'n', 'k', 'a', 'r', 'a']
#----------------------------------------------------------------------------------
--------------------------------------
list sınıfı da dolaşılabilir bir sınıftır. Yani list nesneleri de dolaşılabilir
nesnelerdir. Bir list nesnesi dolaşıldığında
tek tek liste içerisindeki değerler elde edilmektedir. Örneğin:
a = [1, 2, 3, 4, 5]
b = list(a)
a = [1, 2, 3, 4, 5]
b = list(a)
print(a, id(a))
print(b, id(b))
#----------------------------------------------------------------------------------
--------------------------------------
Bir list nesnesi elemanların kendilerini değil adreslerini tutmaktadır.
Örneğin:
aslında burada bu listenin ilk elemanı (yani a[0] elemanı) içerisinde 10 değeri
bulunan nesnenin adresini tutar. Diğer elemanlar da
bu bimimdedir. Örneğin:
Biz listenin bir elemanını değiştirdiğimizde aslında yeni bir nesne yaratıp o
nesnenin adresini o elemana yerleştiririz. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Listelerin değiştirilebilir olması demek liste elemanlarına başka adreslerin
atanabilmesi demektir. Örneğin:
a = [1, 2, 3]
a[1] = 100
Burada biz içerisinde 2 değeri olan int nesneyi değiştirmedik. Listenin 1'inci
indisli elemanındaki adresi değiştirdik.
Yani artık listenin 1'inci indisli elemanı içerisinde 100 olan başka bir int
nesneyi göstermektedir. Örneğin:
>>> a = [1, 2, 3]
>>> id(a[1])
1667362351440
>>> a[1] = 100
>>> id(a[1])
1667362543056
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir listenin bir elemanı başka bir liste olabilir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Python'da matrissel bir veri yapısı listenin içerisinde liste ile oluşturulur.
Bunun başka bir yolu yoktur. Örneğin biz
3x3'lük bir matris oluşturmak isteyelim:
val = a[2][1]
print(val) # 8
val = a[1][2]
print(val) # 6
#----------------------------------------------------------------------------------
--------------------------------------
liste elemanlarına erişirken köşeli parantez içerisindeki değer negatif
olabilir. Eğer köşeli parantez içerisindeki indeks belirten
ifade negatif ise bu özel bir anlam ifade etmektedir. Bu durumda efektif indeks
(yani gerçek indeks) bu negatif değerle listenin
uzunluğu toplanarak elde edilir ve erişim bu efektif indekse yapılır. a[i] gibi
bir erişimde i'nin negatif bir değerde olduğunu varsayalım.
Bu durumda a[i] ile a[i + len(a)] tamamen eşdeğerdir. Örneğin:
a = [10, 20, 30, 40, 50]
Burada a[-1] ile aslında a[-1 + len(a)] aynı anlamdadır. -1 + len(a) burada 4
değerini verir. O zaman a[-1] ifadesi aslında
listenin son elemanını belirtmektedir. O halde a[-2] ifadesi listenin sondan
bir önceki elemanını belirtecektir. Yani negatif indeksler
sondan başa doğru listeyi indekslemektedir. n elemanlı bir listenin son negatif
indeksinin -n olduğuna ancak son pozitif indeksinin
n - 1 olduğuna dikkat ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
print(a[-1]) # 50
print(a[-2]) # 40
print(a[-3]) # 30
print(a[-4]) # 20
print(a[-5]) # 10
#----------------------------------------------------------------------------------
--------------------------------------
Köşeli parantez içerisindeki negatif değer büyük olursa bu durumda efektif
indeks gerçekten negatif olur. Yani bu durum dizide
olmayan bir elemana erişmek znlamına gelir. Böylesi durumlarda exception
(IndexError) oluşmaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Listenin elemanı liste olduğu durumda da negatif indeksler benzer biçimde
kullanılabilir.
#----------------------------------------------------------------------------------
--------------------------------------
val = a[-2][1] # 5
print(val)
val = a[-2][-1] # 6
print(val)
#----------------------------------------------------------------------------------
--------------------------------------
Listelerin negatif indekslenmesi onun son elemanlarına erişme işlemini
kolaylaştırmaktadır. Yine bazı algoritmalarda
bu durum daha okunabilir ve kısa yazımlar sağlamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir liste içinde bir grup elemanı bir liste olarak elde edebiliriz. Bunun için
"dilimleme (slicing)" denilen bir sentaks
kullanılmaktadır. Dilimleme sentaksının genel biçimi şöyledir:
a[start:stop]
a[start:stop:step]
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
b = a[2:7]
print(b) # [30, 40, 50, 60, 70]
#----------------------------------------------------------------------------------
--------------------------------------
Dilimleme sırasında start ya da stop indeks liste uzunluğundan büyük olursa
liste uzunluğuna çekilir. Yani bu durum bir
exception oluşturmaz. Dilimleme sırasında start ile stop aynı değerdeyse ya da
start stop değerinden büyükse boş liste elde
edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
b = a[3:4]
print(b) # [40]
b = a[3:3]
print(b) # []
b = a[5:2]
print(b) # start stop ile aynı olursa ya da stop'tan büyük olursa boş
liste elde edilir
b = a[2:10]
print(b) # [30, 40, 50, 60, 70, 80, 90, 100]
b = a[2:20]
print(b) # [30, 40, 50, 60, 70, 80, 90, 100]
#----------------------------------------------------------------------------------
--------------------------------------
Dilimlemede start ve stop ifadelerinde negatif indeksler kullanılabilir.
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
b = a[5:-2]
print(b) # [60, 70, 80]
b = a[-5:-1]
print(b) # [60, 70, 80, 90]
#----------------------------------------------------------------------------------
--------------------------------------
Dilimlemede start ve stop ifadelerinde negatif indeks girildikten sonra efektif
indeks hesaplandığında (yani bu değer len(a) ile toplandığında)
eğer negatif çıkıyorsa 0 olarak işleme girer. Bu durumda bir exeption oluşmaz.
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
#----------------------------------------------------------------------------------
--------------------------------------
Dilimlemede start belirtilmezse start değerinin 0 olarak belirtildiği girildiği
gelir, stop belirtilmezse stop değerinin listenin uzunlu biçiminde
girildiği anlamına gelir. Örneğin a[:n] ifadesi a[0:n] ile eşdeğerdir. a[n:]
ifadesi ise a[n:len(a)] ile eşdeğerdir. Başka bir deyişle dilimlemde start
belirtilmezse "baştan itibaren", stop belirtilmezse "geri kalan hepsi" anlamına
gelmektedir. Tabii start da stop da belirtilmeyebilir.
Bu durumda listenin tüm elemanlardna liste oluşturulur. Yani a[:] ifadesi
a[0:len(a)] eşdeğerdir.
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
#----------------------------------------------------------------------------------
--------------------------------------
Dilimlemede step atlama miktarını belirtir. step hiç belirtilmezse sanki 1
olarak belirtilmiş gibi kabul edilir.
Tabii elde edilecek değerlere hiçbir zaman stop dahil olmaz.
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
b = a[2:9:2]
print(b) # [30, 50, 70, 90]
b = a[:8:3]
print(b) # [10, 40, 70]
#----------------------------------------------------------------------------------
--------------------------------------
step değeri negatif de olabilir. Ancak bu durumda ilerleme yönü ters olur.
İlerleme ters olduğu için start indeksinin,
stop indeksinden daha büyük olması gerekir. Yine start indeksi dahil stop
indeksi dahil değildir. step negatif ise
bu durumda start indeksin boş bırakılması "listenin uzunluğı - 1" anlamına stop
indeksin boş bırakılması efektif -1 anlamına (yani ilk
eleman dahil olacak biçimde) gelmektedir. Örneğin a[:2:-1] ifadesinde ilerleme
yönü terstir. start yazılmamıştır. Bu durumde sanki start
yerine len(a) - 1 yazılmış gibi işlem yapılır. Başka bir deyişle ifadenin
eşdeğeri a[len(a) - 1:2:-1] biçimindedir. Örneğin a[4::-1]
ifadesinde ilerleme yönü yine terstir. stop belirtilmemiştir. Bu durumda sanki
efektif -1 gibi baka bir deyişle atop inswka "-len(a) - 1"
gibi ele alınmaktadır. İfadenin eşdeğeri a[4:-len(a)-1:-1] biçimindedir.
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
b = a[:2:-1]
print(b) # [100, 90, 80, 70, 60, 50, 40]
#----------------------------------------------------------------------------------
--------------------------------------
Bir listeyi tersyüz etmek için a[::-1] kalıbı kullanılmaktdır.
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
#----------------------------------------------------------------------------------
--------------------------------------
Dilimleme yoluyla liste elemanları güncellenebilir. Eğer step belirtilmezsa bu
işlemin genel sentaksı şöyledir:
a[2:6] = [1, 2, 3, 4, 5, 6, 7]
print(a) # [10, 20, 1, 2, 3, 4, 5, 6, 7, 70, 80, 90, 100]
#----------------------------------------------------------------------------------
--------------------------------------
String'lerin de dolaşılabilir nesneler olduğunu anımsayınız.
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
a[2:6] = 'ankara'
print(a) # [10, 20, 'a', 'n', 'k', 'a', 'r', 'a', 70, 80, 90, 100]
#----------------------------------------------------------------------------------
--------------------------------------
Dolaşılabilir nesnede hiç eleman yoksa bu durum bir silme anlamına gelecektir.
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
a[2:6] = []
print(a) # [10, 20, 70, 80, 90, 100]
#----------------------------------------------------------------------------------
--------------------------------------
Dilimleme yoluyla liste elemanları güncellenirken eğer step belirtilirse bu
durumda ona atanan dolaşılabilir
nesnenin eleman sayısı dilimleme yoluyla silinecek eleman sayısı ile aynı olmak
zorundadır. (Python standard kütüphane dokümanlarında
step değerinin 1 olması durumunda da atanan dolaşılabilir nesnenin eleman
sayısının dilimlemedne elde edilen eleman sayısı ile aynı olması gerektiği
dolaylı olarak belirtilmektedir. Ancak CPython gerçekleştirimi step değeri 1
ise sanki step değeri hiç belirtilmemiş gibi işlem yapmaktadır.)
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
Dilimleme yoluyla atama yapılırken dilimleme sonucunda hiçbir eleman
seçilmiyorsa bu durum start indeks'ten itibaren insert işlemi
anlamına gelmektedir. Örneğin:
>>> a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
>>> a[3:3] = 'ali'
>>> a
[10, 20, 30, 'a', 'l', 'i', 40, 50, 60, 70, 80, 90, 100]
>>> a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
>>> a[4:3] = 'ali'
>>> a
[10, 20, 30, 40, 'a', 'l', 'i', 50, 60, 70, 80, 90, 100]
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Listenin belli bir elemanına atama yapmak ile dilimleme yoluyla atama yapmak
arasındaki farklılığa dikkat ediniz:
>>> a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
>>> a[0] = [1, 2, 3, 4, 5]
>>> a
[[1, 2, 3, 4, 5], 20, 30, 40, 50, 60, 70, 80, 90, 100]
>>> a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
>>> a[0:1] = [1, 2, 3, 4, 5]
>>> a
[1, 2, 3, 4, 5, 20, 30, 40, 50, 60, 70, 80, 90, 100]
Biz dilimleme yapmadan bir dolaşılabilir nesneyi belli bir indekse atadığımızda
listenin o indeksinde dolaşılabilir nesne olur.
Halbuki dilimleme yoluyla atama yapıldığında dolaşılabilir nesnenin elemanları
dilimlenen elemanların yerlerine insert edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Dilimleme yoluyla güncelleme yapılırken step değeri negatif ise yine
dolaşılabilir nesnedeki eleman sayısı dilimlenen
eleman sayısı kadar olmak zorundadır. Ancak elemanlar ters yönde insert
edilecektir. Örneğin:
>>> a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
>>> a[7:2:-1] = [100, 200, 300, 400, 500]
>>> a
[10, 20, 30, 500, 400, 300, 200, 100, 90, 100]
>>> a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
>>> a[7:2:-2] = [100, 200, 300]
>>> a
[10, 20, 30, 300, 50, 200, 70, 100, 90, 100]
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Listeyi dilimlediğimizde biz yeni bir list nesnesi oluştururuz. Ancak bu yeni
oluşturulan list nesnesi aslında dilimlenen list nesnesindeki
dilimlenen elemanların adreslerinden oluşmaktadır. Yani dilimleme işlemi "sığ
kopyalama (shallow copy)" biçiminde yapılmaktadır.
Sığ kopyalama demek yerni oluşturulan listenin elemanlarının dilimlene listenin
dilimlenen elemanlarıyla aynı nesneleri göstermesi demektir.
Sığ kopyaalama sırasında dilimlenen listedeki adresler yeni oluşturulan listeye
kopyalanmaktadır. Böylece dilimlenen listenin ilgili elemanlarıyla
yeni oluşturulan listenin elemanları aynı nesneleri gösterir hale gelmektedir.
Aşağıdaki örneği inceleyiniz:
Burada a[2] elemanın gösterdiği nesne ile b[0] elemanının gösterdiği nesnenin,
a[3] elemanının gösteridği nesne ile b[1] elemanının
gösteridiği nesnenin aynı nesneler olduğuna dikkat ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Listeler değiştirilebilir (mutable) nesneler olduğuna göre listenin elemanı bir
liste ise dilimlemede yeni oluşturulan listeye
bu eleman olan listenin adresi kopyalanacaktır. Bu durumda bu eleman olan
listede yapılacak değişiklik her iki listede de
görünür olacaktır. Aşağıdaki örneğe dikkat ediniz:
>>> b = a[0:2]
>>> id(a[1])
2644868980608
>>> id(b[1])
2644868980608
>>> a[1] is b[1]
True
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi bir sınıf içerisindeki fonksiyonlara Python'da "metot
(method)" denilmekteydi. Bir metodun aynı sınıf türünden
bir değişkenle "." operatörü kullanılarak çağrılması gerektiğini anımsayınız.
Örneğin:
[Link]()
Burada a hangi sınıf türündense foo da o sınıfın bir metodudur. Metotlar belli
bir nesne üzerinde işlem yapan fonksiyonlardır. Dolayısıyla
[Link]() ifadesinde foo metodu a nesnesi üzerinde (a değişkeninin gösterdiği
nesne üzerinde) işlem yapmaktadır. Örneğin:
x = [1, 2, 3, 4, 5]
Burada x list sınıfı türünden bir değişkendir. foo list sınıfının bir metodu
olsun:
[Link](...)
Burada foo bu x nesnesi üzerinde onun elemanları ile ilgili bir işlem
yapacaktır. eğer fonksiyonlar bir sınıf içerisinde değilse
belli bir nesne üzerinde işlem yapmak yerine genel işlemleri yapmak üzere
yazılmış olurlar.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
list sınıfının append isimli metodu list nesnesinin sonuna yeni bir eleman
ekler. Örneğin:
a = [1, 2, 3, 4, 5] # [1, 2, 3, 4, 5]
print(a)
[Link]('ankara') # [1, 2, 3, 4, 5, 'ankara']
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
[Link](100)
print(a) # [10, 20, 30, 100]
[Link]('ali')
print(a) # [10, 20, 30, 100, 'ali']
[Link](12.4)
print(a) # [10, 20, 30, 100, 'ali', 12.4]
#----------------------------------------------------------------------------------
--------------------------------------
append her zaman tek bir nesnenin eklenmesine yol açmaktadır. Yani append ile
biz listeye liste eklemek istesek listenin içerisindekiler
eklenmeyecektir. Listenin kendisi tek bir eleman olarak eklenecektir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir listenin sonuna birden fazla eleman extend metoduyla eklenmektedir. extend
metodu bizden dolaşılabilir bir nesne alır.
O nesneyi dolaşarak elde ettiği tüm nesneleri listeye ekler. Listelerin ve
string'lerin dolaşılabilir nesneler olduğunu anımsayınız.
Eğer extend metoduna dolaşılabilir olmayan bir argüman gireresek exception
(TypeError) oluşacaktır. extend metodu tek bir argüman almaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
[Link]([1, 2, 3, 4, 5])
print(a) # [10, 20, 30, 1, 2, 3, 4, 5]
#----------------------------------------------------------------------------------
--------------------------------------
str dolaşılabilir bir sınıf olduğuna göre bir string'in karakterlerini extend
metodu ile listeye ekleyebiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
[Link]('ankara')
print(a) # [10, 20, 30, 'a', 'n', 'k', 'a', 'r', 'a']
#----------------------------------------------------------------------------------
--------------------------------------
list sınfının index isimli metodu parametresiyle aldığı nesneyi listede arar.
Eğer bulursa ilk bulduğu yerin indeks numarasıyla
geri döner. Eğer bulamazsa exception (ValueError) oluşmaktadır. (Dolayısıyla bu
metot genellikle zaten var olduğunu bildiğimiz bir elemanın yerini
aramak için kullanılmaktadır.) Bu metot bize int türünden index belirten bir
değer vermektedir. Örneğin:
İleride ayrı bir konuda ele alınacak olsa da şimdiden bir noktayı belirtmek
istiyoruz: Python'da farklı sınıflar türünden (int, float ve bool
haricinde) değişkenler == ve != operatörleriyle karşılaştırıldığında == işlemi
her zaman False, != işlemi her zaman True vermektedir. Örneğin:
>>> 'ali' == 19
False
Eğer değer liste içerisinde birden fazla yerde varsa list metodu değerin
listede ilk bulunduğu indeksi vermektedir. Örneğin:
>>> a = [1, 4, 7, 8, 4, 7]
>>> result = [Link](4)
>>> result
1
index metodu asında iki argüman ve üç argüman da alabilir. Eğer metodu biz iki
argümanlı kullanırsak birinci argüman aranacak
değeri, ikinci argüman ise aramanın başlatılacağı indeksi belirtir. Örneğin:
>>> a = [10, 4, 5, 8, 9, 4, 8]
>>> result = [Link](4, 2)
>>> result
5
Burada 4 değeri aranmıştır. Ancak arama baştan itibaren değil 2'inci indeksten
itibaren başlatılmıştır. Eğer index üç argümanla
kullanılırsa üçüncü argüman aramanın bitirileceği indeksi belirtir. Ancak bu
indeks aramaya dahil değildir. Örneğin:
>>> a = [10, 4, 5, 8, 9, 4, 8]
>>> result = [Link](4, 2, 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 4 is not in list
#----------------------------------------------------------------------------------
--------------------------------------
result = [Link]('veli')
print(result) # 4
#----------------------------------------------------------------------------------
--------------------------------------
list sınıfının count isimli metodu bizden bir değer alır. Liste içerisinde o
değerden kaç tane olduğunu bize verir. Örneğin:
>>> a = [1, 3, 7, 4, 3, 8, 3, 4, 3]
>>> result = [Link](3)
>>> result
4
>>> result = [Link](10)
>>> result
0
>>> result = [Link]('ali')
>>> result
0
#----------------------------------------------------------------------------------
--------------------------------------
a = [3, 5, 7, 7, 4, 7, 9, 7]
result = [Link](7)
print(result) # 4
result = [Link](5)
print(result) # 1
result = [Link](70)
print(result) # 0
#----------------------------------------------------------------------------------
--------------------------------------
Bir listeden tek bir elemanı silmek için pop isimli metot kullanılabilir. Bu
metot argümansız kullanılırsa son eleman silinir.
Argümanlı kullanılırsa belirtilen indeksteki eleman silinir. Yani metot
argümanlı ya da argümansız kullanılabilmektedir.
Argümanlı kullanılacaksa argüman index belirtmelidir. pop silinen elemanın
kendisini de bize geri dönüş değeri olarak vermektedir.
pop metoduna verilen index sınır dışındaysa ya da liste boşsa exception
(IndexError) oluşmaktadır. index metodu negatif indeksleri
kabul etmektedir. Bu durumda negatif değerler yine list uzunluğu ile toplanıp
efektif indeks elde edilmektedir. Yani örneğin [Link](-2)
işlemi son elemandan bir önceki elenı siler. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
list sınıfının remove metodu da silme yapar. Ancak silinecek eleman pop
metodunda olduğu gibi indeks numarasıyla değil
bizzat değeriyle belirtilmektedir. remove parametresiyle belirtilen değer,
liste içerisinde arar. Eğer bulursa yalnızca ilk
bulduğunu siler, eğer bulamazsa exception (ValueError) oluşur. remove metodu
bize herhangi bir geri dönüş değeri vermez.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
[Link](40)
print(a) # [10, 20, 30, 50, 40]
#----------------------------------------------------------------------------------
--------------------------------------
clear isimli metot listenin tüm elemanlarını siler. Yani liste 0 elemanlı boş
bir liste haline gelir. clear metodunun
parametresi yoktur.
#----------------------------------------------------------------------------------
--------------------------------------
[Link]()
print(a) # []
#----------------------------------------------------------------------------------
--------------------------------------
reverse metodu listeyi ters yüz eder. Bu metot bize bir geri dönüş değeri
vermez. Ters yüz etme işlemi nesnenin üzerinde (in place)
yapılmaktadır. a bir liste belirtmek üzere a[::-1] işlemi de listeyi ters yüz
eder. Ancak bu işlem ters yüz edilmiş yeni bir liste
vermektedir. Bu işlem sonucunda a listesinde bir değişiklik olmamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
[Link]()
print(a) # [50, 40, 30, 20, 10]
#----------------------------------------------------------------------------------
--------------------------------------
Bir veri yapısının (list, dict vs.) bir metodu işlemi veri yapısının üzerinde
yapıyorsa buna "in place" işlem denilmektedir.
Tabii her metot işlemini in place yapmayabilir. Metot işlem yapılmış yeni bir
veri yapısını bize verebilir. Bu "in place" bir işlem değildir.
Örneğin [Link]() ile biz "in place" bir işlem yapmış olduk. Ancak a[::-1]
işlemi "in place" bir işlem değildir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
list sınıfının sort isimli metodu liste elemanlarını sıraya dizmektedir.
Default durum küçükten byüğe sıraya dizmedir.
sort bize bir değer vermez. Bizzat elemanları sıraya dizer (in place işlem).
sort metodu "stable" sort yapmaktadır.
Stable sort aynı elemanların sort edilmiş listede orijinal listedeki sırasına
göre yan yana sıraya dizilmesi anlamına gelmektedir.
Örneğin:
[Link]()
print(a) # [-4, 2, 3, 6, 8, 9, 16, 22]
#----------------------------------------------------------------------------------
--------------------------------------
sort metodu sıraya dizme işleminde listenin elemanları arasında <
karşılaştırması yapmaktadır. Eğer listenin herhangi iki
elemanı arasında < karşılaştırması yapılamazsa bu durumda exception (TypeError)
oluşur. Örneğin listenin bir elemanı str bir elemanı int
türden olsun. Bu iki eleman < operatöryüyle karşılaştırılamaz. Bu durumda sort
işlemi exception'a yol açacaktır. Tabii
string'ler kendi aralarında < operatörü ile karşılaştırılabilmektedir:
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda da belirttiğimiz gibi string'ler kendi aralarında
karşılaştırılabilmektedir. Dolayısıyla string'lerden
oluşmuş bir liste sort edilebilr.
#----------------------------------------------------------------------------------
--------------------------------------
[Link]()
print(a) # ['adana', 'izmir', 'kayseri', 'samsun', 'sivas']
#----------------------------------------------------------------------------------
--------------------------------------
sort metodu default durumda listeyi küçükten büyüğe (ascending) sıraya
dizmektedir. Listeyi büyükten küçüğe (descending)
sıraya dizmek için reverse=True isimli parametresinin kullanılması gerekir.
(Yani [Link]() ile [Link](reverse=False) aynı
anlamdadır.)
#----------------------------------------------------------------------------------
--------------------------------------
[Link]()
print(a) # ['adana', 'izmir', 'kayseri', 'samsun', 'sivas']
#----------------------------------------------------------------------------------
--------------------------------------
list sınıfının reverse metodunun yanı sıra ayrıca reversed isimli bir built-in
bir fonksiyon (metot değil) da vardır.
Bu reversed fonksiyonu bizden dolaşılabilir bir nesne alır. Onun ters yüz
edilmiş halini bize dolaşılabilir (iterable)
bir nesne biçiminde verir. reversed fonksiyonu "in place" bir işlem yapmaz.
reversed fonksiyonu bize bir liste değil dolaşılabilir
bir nesne vermektedir. Yani biz ters yüz edilmiş değerleri o dolaşılabilir
nesneyi dolaşarak elde edebiliriz. list fonksiyonunun
dolaşılabilir nesneyi dolaşarak bir liste yaptığını anımsayınız. Örneğin:
>>> a = [1, 2, 3, 4, 5]
>>> result = reversed(a)
>>> type(result)
<class 'list_reverseiterator'>
>>> b = list(result)
>>> b
[5, 4, 3, 2, 1]
>>> a
[1, 2, 3, 4, 5]
>>> s = 'ankara'
>>> result = reversed(s)
>>> type(result)
<class 'reversed'>
>>> b = list(result)
>>> b
['a', 'r', 'a', 'k', 'n', 'a']
#----------------------------------------------------------------------------------
--------------------------------------
b = reversed(a)
c = list(b)
#----------------------------------------------------------------------------------
--------------------------------------
Aslında biz reversed fonksiyonunu her "dolaşılabilir" nesneyle kullanamayız.
reversed fonksiyonu "tersten dolaşılabilir (reverse iterable)"
nesneler ile kullanılabilmektedir. Bu bağlamda listeler, stringler aynı zamanda
tersten dolaşılabilir biçimdedir. Bu nedenle biz
reverse fonksiyonunu listeler ve string'lerle kullanabilmekteyiz.
#----------------------------------------------------------------------------------
--------------------------------------
s = list(reversed('ankara'))
print(s) # ['a', 'r', 'a', 'k', 'n', 'a']
#----------------------------------------------------------------------------------
--------------------------------------
sort işlemi için sorted isimli global built-in bir fonksiyon da bulunmaktadır.
Bu fonksiyon bize her zaman sort edilmiş yeni bir liste vermektedir.
Fonksiyon dolaşılabilir herhangi bir nesneyi parametre olarak alabilmektedir.
Örneğin:
>>> a = [5, 4, 3, 2, 1]
>>> b = sorted(a)
>>> a
[5, 4, 3, 2, 1]
>>> b
[1, 2, 3, 4, 5]
>>> a = sorted('ankara')
>>> a
['a', 'a', 'a', 'k', 'n', 'r']
#----------------------------------------------------------------------------------
--------------------------------------
b = sorted(a)
print(b) # [1, 3, 6, 10, 23, 34, 51]
a = 'ankara'
b = sorted(a)
print(b) # ['a', 'a', 'a', 'k', 'n', 'r']
#----------------------------------------------------------------------------------
--------------------------------------
Python'da pek çok veri yapısı ile kullanılabilen "in" isimli iki operandlı
araek özel amaçlı bir operatör bulunmaktadır.
in operatörünün sol tarafındaki operand olup olmadığı kontrol edilecek değeri
belirtir. Sağ tarafındaki operand ise arama
yapılacak nesneyi belirtir. Bu operatör eğer sol tarafındaki operand ile
belirtilen değer sağ tarafındaki operand ile belirtilen
veri yapısında var ise True değer, yok ise False üretmektedir. Yani in
operatörü bool bir değer üretmektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Biz bir liste içerisinde başka bir liste var mı diye in operatörü ile
bakabiliriz. in operatörü == karşıalştırması yapmaktadır.
İki listenin bu biçimde karşılaştırılması ileride ele alınacaktır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
result = 34 in a
print(result) # False
#----------------------------------------------------------------------------------
--------------------------------------
not in operatörü ise bir elemanın bir listede olmadığını sorgulamak için
kullanılmaktadır. Yani in operatörünün tersini yapmaktadır.
Örneğin:
>>> a = [1, 2, 3, 4, 5]
>>> 5 in a
True
>>> 5 not in a
False
#----------------------------------------------------------------------------------
--------------------------------------
result = 30 not in a
print(result) # False
result = 15 not in a
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
Veri yapılarından eleman silmek için genel amaçlı "del" isiminde bir deyim de
kullanılmaktadır.
del deyimi yalnızca listelerde değil "değiştirilebilir (mutable)" başka
nesnelerde de kullanılabilmektedir. del deyimi
listelerle kullanılırken listenin belli bir elemanı köşeli parantezler ile
belirtilir. Örneğin del a[3] gibi. Örneğin:
Bu örnekte biz aynı işlemi pop metoduyla da yapabilirdik. Yine biz del deyimi
ile listenin olmayan bir elemnanını silmek istersek
del deyimi exception (IndexError) oluşturur. Örneğin:
del a[0]
print(a) # [3, 'veli', 45, 73, 'ali', 21, 16, 7]
#----------------------------------------------------------------------------------
--------------------------------------
del deyimi ile listelerde silme yapılırken silinecek elemanlar dilimleme ile de
belirtilebilmektedir. Bu durumda önce dilimlenen
elemanlar belirlenir sonra hepsi tek hamlede silinir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
del a[::2]
print(a) # [3, 45, 'ali', 16]
#----------------------------------------------------------------------------------
--------------------------------------
del deyimi ile ',' atomuyla ayrılmış birden fazla silme işlemi yapılabilir. Bu
durumda silme ayrı ayrı soldan sağa biçimde yapılmaktadır.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Aynı liste üzerinde silme yapılırken ilk silmeden sonra elemanların indeks
numaralarının değişeceğine dikkat ediniz.
Yani:
del a[i]
del a[k]
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da iki liste nesnesi + operatörü ile toplanabilir. Ancak çıkartılamaz,
çarpılamaz ve bölünemez. İki liste toplandığında
bu işlemden yeni bir liste edilmektedir. Bu yeni listenin elemanları +
operatörünün solundaki liste elemanlarına sağındaki liste elemanlarının
eklenmesiyle oluşturulmuş bir liste olur. a + b işleminde önce len(a) + len(b)
kadar uzunlukta yeni bir liste yaratılır. Bu listeye
önce a listesindeki adresler, sonra b listesindeki adresler eklenir. Böylece c
listesi aslında a listesindeki adreslerden ve
b listesindeki adreslerden oluşuyor durumda olur. Bu da bir çeşit sığ kopyalama
anlamına gelmekteir.
c = a + b
print(c) # [10, 'ali', 20, 30, 'veli', 40]
print(id(b[2]))
print(id(c[5])) # b[2] ile c[5] aynı nesnesyi gösteriyorlar
#----------------------------------------------------------------------------------
--------------------------------------
İki list nesnesi toplanırken oluşturulan liste toplanan iki list nesnesinin
tuttukları adreslerden oluştuğuna göre list nesnesinin
bir elemanu "değiştirilebilir (mutable)" bir nesne ise orada yapılan değişiklik
dolaylı biçimde toplama işlemiyle elde edilen nesnede de
gözükecektir. Aşağıdaki örneği izleyiniz:
c = a + b
print(c) # [10, 20, 30, 40, [50, 60], 70]
c[4][0] = 100
print(b) # [40, [100, 60], 70]
#----------------------------------------------------------------------------------
--------------------------------------
İki listeyi toplarken yeni oluşturulan liste her zaman iki listenin uzunlukları
toplamı kadar uzunluğa sahip olur.
#----------------------------------------------------------------------------------
--------------------------------------
c = a + b
#----------------------------------------------------------------------------------
--------------------------------------
İki listenin toplanması durumunda listelerden biri ya da her ikisi boş liste
olsa bile yine de toplama işlemi sonucunda
yeni bir liste yaratılmaktadır. Tabii genel olarak bir programda
"gözlemlenebilir bir yan etki (observable side effects)" oluşmadıktan
sonra derleyiciler ve yorumlayıcılar kodu daha hızlı çalışacak biçimde ya da
daha az yer kaplayacak biçimde yeniden düzenleyebilirler.
Yani aşağıdaki örnekte aslında yorumlayıcı bu toplama işlemini hiç
yapmayabilir. Çünkü yorumlayıcının bu toplama işlemini yapıp
yapmadığı program içerisinde anlaşılamamaktadır. Başka bir deyişle yorumlayıcı
bu toplama işlemini yapsa da yapmasa da biz
bunun yapılıp yapılmadığını gözlemleyemeyiz:
print(c)
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi a += b işlemi a = a + b anlamına gelmektedir. Ancak
listelerde bu eşdeğerlik söz konusu değildir. a ve b birer liste
olmak üzere a += b aslında b listesinin lemanlarının mevcut a listesinin sonuna
eklenmesi anlamına gelmektedir. Dolayısıyla a += b aslında
listelerde adeta [Link](b) gibi bir etki yaratmaktadır. Örneğin:
>>> a = [1, 2, 3]
>>> b = [4, 5]
>>> id(a)
1570379741824
>>> a = a + b
>>> a
[1, 2, 3, 4, 5]
>>> id(a)
1570379772992
>>> a = [1, 2, 3]
>>> b = [3, 4]
>>> id(a)
1570379782464
>>> a += b
>>> a
[1, 2, 3, 3, 4]
>>> id(a)
1570379782464
print(id(a))
a = a + b
print(a)
print(id(a)) # a'nın id'si değişiyor, çünkü a artık yeni bir listeyi
gösteriyor
a += b
print(a)
print(id(a)) # a'nın id'si değişmiyor. Çünkü ekleme a'ya yapılıyor
#----------------------------------------------------------------------------------
--------------------------------------
Python dokümanlarına göre (Python Library Reference) a list türünden olmak
üzere [Link](b) ile a += b tamamen eşdeğerdir.
extend metodunda metodun parametresinin herhangi bir dolaşılabilir nesne
olabileceğini belirtmiştik. Aynı durum a += b operatöründe de
geçerlidir. Bu işlemde a list türündense b aslında dolaşılabilir herhangi bir
nesne belirtebilir. Örneğin:
>>> a = [1, 2, 3]
>>> [Link]('ali')
>>> a
[1, 2, 3, 'a', 'l', 'i']
>>> a = [1, 2, 3]
>>> a += 'ali'
>>> a
[1, 2, 3, 'a', 'l', 'i']
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir liste int bir değerle çarpılabilir. (İki liste çarpılamaz, bölünemez ve
çıkartılamaz. Ayrıca bir liste int bir toplanamaz ve bölünemez.)
Bu işleme Python'da "yineleme (repitition)" denilmektedir. a bir liste, n de
int bir değer belirtmek üzere a * n ya da n * a tamamen
n defa a yı kendisiyle toplamaya eşdeğerdir. Örneğin a * 3 tamamen a + a + a
anlamına gelmektedir. Yani a içerisindeki değerler üç kere yinelenmektedir.
Listeler toplanırken oluşturulan yeni listeye operand olarak kullanılan
listelerdekş adreslerin kopyalandığını anımsayınız. Örneğin:
>>> a = [1, 2, 3]
>>> b = a * 2
>>> a
[1, 2, 3]
>>> b
[1, 2, 3, 1, 2, 3]
>>> id(a[0])
1570340235568
>>> id(b[0])
1570340235568
>>> id(b[3])
1570340235568
>>> b = a + a
>>> b
[1, 2, 3, 1, 2, 3]
#----------------------------------------------------------------------------------
--------------------------------------
b = a * 3 # eşdeğeri a + a + a
print(b) # [10, 20, 30, 10, 20, 30, 10, 20, 30]
b = 3 * a
print(b) # [10, 20, 30, 10, 20, 30, 10, 20, 30]
b = [0] * 10
print(b) # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
#----------------------------------------------------------------------------------
--------------------------------------
a * 3 gibi bir ifade a + a + a anlamına geldiğine göre aslında burada yeni
yaratılacak listenin elemanları tekrarlı bir biçimde
a listesinin elemanlarındaki adresleri tutacaktır. Yani yineleme işlemi aslında
yinelen listenin (örneğimzde a) adreslerinin yinelenmesiyle
oluşturulmaktadır. Sonuçta bir "sığ kopyalama (shallow copy)" durumu oluşur.
Yani yineleme liste elemanlarının gösterdiği nesnelerin kopyasını
oluşturmamaktadır. Örneğin:
a = [10, 20]
b = a * 3
#----------------------------------------------------------------------------------
--------------------------------------
Yineleme eişlemi bazı durumlarda çok pratik olanaklar sağlamaktadır. Örneğin
biz her elemanı 0 olan 100 elemanlık bir listeyi
kolay bir biçimde yineleme ile oluşturabiliriz:
>>> id(a[0])
1570340235536
>>> id(a[1])
1570340235536
>>> id(a[99])
1570340235536
Bu durum bir sorun oluşturmaz. Nasıl olsa int "değiştirilemez (immutable)" bir
türdür. Biz listeninin bir elemanına değer
atadığımızda diğer elemanların değeri değişmeyecektir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Yineleme işleminde çarpılan değer 0 ya da negatif bir değerse yineleme
işleminden boş bir liste elde edilmektedir. Örneğin:
>>> a = [1, 2, 3]
>>> a * 0
[]
>>> a * -3
[]
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Biz Python'da ne zaman bir köşeli parantez açsak yeni bir liste
yaratılmaktadır. Örneğin:
Ancak aşağıdaki gibi bir listenin her elemanının aynı listeyi göstermesini de
saplayabiliriz:
a = [1, 2]
b = [a, a, a]
>>> a = [1, 2]
>>> b = [a, a, a]
>>> id(b[0])
1570379735488
>>> id(b[1])
1570379735488
>>> id(b[2])
1570379735488
O halde burada biz a listesi üzerinde değişilik yaparsak bu durumda b'nin
elemanları aynı a listesini gösterdiğine göre sanki
onlar üzerinde de eğişiklik yapılmış gibi bir etki oluşacaktır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki gibi boş bir listemiz olsun:
a = []
b = [a, a, a]
b[0].append(100)
Yani biz bir listenin elemanına bir değer yerleştirdiğimzde aslında o değeri
liste elemanına yerleştirmeyiz. O elemanın belirttiği nesnenin
adresini liste elemanına yerleştirmiş oluruz.
>>> a = []
>>> id(a)
1672976339968
>>> b = [a, a, a]
>>> id(b[0])
1672976339968
>>> id(b[1])
1672976339968
>>> id(b[2])
1672976339968
>>> b[0].append(100)
>>> a
[100]
>>> b
[[100], [100], [100]]
>>> [Link](200)
>>> b
[[100, 200], [100, 200], [100, 200]]
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki koda dikkat ediniz:
a = [[], [], []]
Burada her köşeli parantez yeni bir listenin yaratılmasına yol açtığı için
a'nın elemanları farklı boş listeleri gösterecektir.
Ancak örneğin:
a = [[]] * 3
#----------------------------------------------------------------------------------
--------------------------------------
a = [[]]
b = a * 3
print(b)
b[0].append(100)
print(b) # [[100], [100], [100]]
b[1].append(200)
print(b) # [[100, 200], [100, 200], [100, 200]]
#----------------------------------------------------------------------------------
--------------------------------------
* operatörü soldan sağa öncelikli olduğuna göre a bir liste olmak üzere
aşağıdaki ifade de geçerlidir:
b = a * 2 * 3
Burada önce a * 2 işlemi yapılacak buradan bir liste elde edilecek sonra elde
edilen bu liste 3 ile çarpılacaktır. Yani sonuçta
a'nın elemanlarından 6 kez oluşturulmuş olacaktır. Örneğin:
>>> a = [1, 2, 3]
>>> b = a * 2 * 3
>>> b
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
#----------------------------------------------------------------------------------
--------------------------------------
Listelerde a = a * n ile a *= n işlemi de aynı anlama gelmemektedir. a = a * n
işleminde a * n ile yeni bir liste yaratılır,
a artık bu yeni listeyi gösterir. Halbuki a *= n işleminde (n - 1) tane a,
a'nın sonuna eklenmektedir. Örneğin:
>>> a = [1, 2, 3]
>>> id(a)
1570379775488
>>> a = a * 2
>>> a
[1, 2, 3, 1, 2, 3]
>>> id(a)
1570379885120
>>> a = [1, 2, 3]
>>> id(a)
1570379931200
>>> a *= 2
>>> id(a)
1570379931200
>>> a
[1, 2, 3, 1, 2, 3]
#----------------------------------------------------------------------------------
--------------------------------------
print(a, id(a))
a = a * 3
print(a, id(a)) # a'nın adresi değişiyor, a artık a * 3 ile yaratılan listeyi
gösteriyor
print(a, id(a))
a *= 3
print(a, id(a)) # a'nın adresi değişmiyor, ekleme a'nın sonuna yapılıyor
#----------------------------------------------------------------------------------
--------------------------------------
Demetler (tuples) listelere benzeyen önemli diğer bir veri yapısıdır. Bir demet
normal parantezler kullanılarak yaratılabilir.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Bir demet tuple sınıfının tür fonksiyonu olan tuple fonksiyonu ile de
yaratılabilir. tuple fonksiyonuna argüman girilmezse
boş bir demet yaratılır. Eğer tuple fonksiyonuna dolaşılabilir bir nesne
argüman olarak verilirse tuple fonksiyonu bu nesneyi dolaşarak
onun elemanlarından demet oluşturmaktadır. Örneğin:
>>> t = tuple()
>>> t
()
>>> a = [1, 2, 3, 4, 5]
>>> t = tuple(a)
>>> t
(1, 2, 3, 4, 5)
>>> t = tuple('ankara')
>>> t
('a', 'n', 'k', 'a', 'r', 'a')
#----------------------------------------------------------------------------------
--------------------------------------
t = tuple()
print(t) # ()
t = tuple('ankara')
print(t) # ('a', 'n', 'k', 'a', 'r', 'a')
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
t = tuple(a)
print(t) # (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
#----------------------------------------------------------------------------------
--------------------------------------
Demetler listelere pek çok bakımdan benzemektedir. Örneğin:
t = (10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
val = t[3]
print(val) # 40
val = t[-3]
print(val) # 80
result = t[1:6]
print(result) # (20, 30, 40, 50, 60)
result = t[1:6:2]
print(result) # (20, 40, 60)
result = t[::-1]
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Demetler dolaşılabilir nesnelerdir. Demetler dolaşıldığında sırasıyla demetin
elemanları elde edilir. Örneğin:
>>> t = (1, 2, 3, 4, 5)
>>> a = list(t)
>>> a
[1, 2, 3, 4, 5]
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Demetlerde de eleman uzunluğu built-in len fonksiyonuyla elde edilebilir.
#----------------------------------------------------------------------------------
--------------------------------------
t = (10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
n = len(t)
print(n) # 10
#----------------------------------------------------------------------------------
--------------------------------------
Boş bir demet içi boş bir parantezle oluşturulmaktadır. Örneğin:
>>> t = ()
>>> type(t)
<class 'tuple'>
>>> len(t)
0
t = (10, )
Tabii buradaki son ',' atomunun hiçbir işlevi yoktur. Ancak sentaks bakımından
bu son ',' geçerlidir. Yani buradaki son ','
boş bir eleman oluşturmamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
t = (10)
print(t, type(t)) # 10 <class 'int'>
t = (10, )
print(t, type(t)) # (10,) <class 'tuple'>
#----------------------------------------------------------------------------------
--------------------------------------
Demetler de tamamen listelerde oluduğu gibi heterojen yani farklı türlerden
elemanlara sahip olabilir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Bir listenin elemanı bir demet, bir demetin elemanı da bir liste olabilir.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Demet elemanları da tıpkı listelerde olduğu gibi adreslerden oluşmaktadır. Yani
bir demet elemanların kendisini değil
onların adreslerini tutmaktadır. Örneğin:
t = ('ali', 12)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Listelerle demetler arasındaki en önemli farklılı (aslında tek farklılık, diğer
farklılıklar bu farklılığın bir sonucu)
listelerin "değiştirilebilir (mutable)", demetlerin ise "değiştirilemez
(immuatable)" olmasıdır. Bir demet yaratıldıktan
sonra artık onun elemanları bir daha değiştirilemez. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
result = [Link]('veli')
print(result) # 5
result = [Link](20)
print(result) # 2
#----------------------------------------------------------------------------------
--------------------------------------
Tabii demetlerin elemanları da aslında nesnelerin adreslerini tutmaktadır. Bu
konuda listelerle bir farklı yoktur.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir grup bilgiden elde edilen onu temsil eden kısa bir bilgiye hash
denilmektedir. Bir grup bilgiden bir hash
değerinin elde edilmesi çeşitli amaçlarla kullanılabilmektedir. Örneğin hash
değerleri bazı veri yapıları için
anahtar olarak kullanılırlar. Yine hash değerleri bozulmayı tespit etmek için
de bazı bilgi güvenliği ve şifreleme gibi
bazı aalanlarda kullanılmaktadır.
Bir nesnenin içerisindeki değerin hash değeri hash isimli built-in fonksiyonla
elde edilebilir. Örneğin:
>>> a = 'ali'
>>> hash(a)
3186741938407899844
>>> t = (1, 2, 3, 4)
>>> hash(t)
590899387183067792
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Demetin bir elemanı bir liste olsun:
t[1][1] = 100
t[1][1] = 100
print(t) # (1, [2, 100], 4)
#----------------------------------------------------------------------------------
--------------------------------------
15. Ders - 30/05/2021-Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Demetler oluşturulurken bazı zorunlu durumlar dışında parantezler
kullanılmayabilir. Örneğin:
t = (1, 2, 3)
ile,
t = 1, 2, 3
t = 10,
ile,
t = (10, )
aynı anlamdadır. Demet oluştururken pek çok durumda parantez gerekmediğine göre
biz de örneklerimizde bazen parantezleri
kullanacağız bazen de kullanmayacağız.
#----------------------------------------------------------------------------------
--------------------------------------
t = 10, 20, 30
print(t)
#----------------------------------------------------------------------------------
--------------------------------------
Tabii bazı durumlarda demeti yaratırken parantezleri mecburen kullanırız.
Örneğin listenin ya da demetin bir elemanı demet olacaksa
mecburen demet parantezleri kullanılmak zorundadır.
Örneğin bir fonksiyona argüman olarak bir demet göndermek istiyorsak yine
mecburen demet parantezini kullanmak zorundayız:
print(10, 20)
print((10, 20))
t = ()
print(t)
t = tuple()
print(t)
t = ,
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Tek elemanlı bir demeti parantezsiz de oluşturabiliriz. Tabii bu durumda
değerden sonra bir ',' atomu gerekir. Örneğin:
t = 10,
print(t)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
İki demet + operatörü ile toplanabilir. Toplama işleminden yeni bir demet
nesnesi elde edilir. Bu yeni demet nesnesi
iki demet nesnesinin elemanlarının uç uca eklenmesinden oluşmaktadır. Yine
tıpkı listelerde olduğu gibi yeni yaratılan
demete bu demetlerin içerisindeki adresler kopyalanmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
c = a + b
print(c)
#----------------------------------------------------------------------------------
--------------------------------------
Listelerde a = a + b ile a += b farklı anlamlara geliyordu. Ancak demetlerde bu
iki ifade tamamen aynı anlama gelmektedir.
Çünkü demetlerde sona ekleme biçiminde bir işlem yoktur. Örneğin:
>>> a = 1, 2, 3
>>> id(a)
1486505308992
>>> b = 4, 5, 6
>>> a += b
>>> id(a)
1486504943872
>>> a
(1, 2, 3, 4, 5, 6)
#----------------------------------------------------------------------------------
--------------------------------------
a = 10, 20, 30
b = 40, 50
print(a, id(a))
a = a + b
print(a, id(a))
a = 10, 20, 30
print(a, id(a))
a += b
print(a, id(a)) # id farklı çıkacak, çünkü demetlerde sona ekleme mümkün değil
#----------------------------------------------------------------------------------
--------------------------------------
Demetlerde de yineleme (repitition) işlemi yapılabilmektedir. Yani bir demet
int bir değerle çarpılabilir. Bu durumda yine bir
demet nesnesi yaratılır. Bu demet nesnesi çarpılan demetin elemanlarının
yinelenmesinden oluşur. Tabii iki demeti çarpamayız,
çıkartamayız, bölemeyiz. Yine yineleme işleminde çarpılan değer 0 ya da negatif
bir değerse boş bir demet elde edilmektedir.
Operand'lar yine çarpma işlemimde yer değiştirebilir.
#----------------------------------------------------------------------------------
--------------------------------------
b = 3 * a # a * 3 de yazılabilirdi
print(b)
#----------------------------------------------------------------------------------
--------------------------------------
Listelerde a = a * n ile a *= n farklı anlamlara geliyordu. Ancak demetlerde
sona ekleme işlemi olmadığı için bu iki ifade
de tamamen aynı anlama gelmektedir. Örneğin:
>>> a = 1, 2, 3
>>> id(a)
1486505308992
>>> a *= 2
>>> id(a)
1486504943872
>>> a
(1, 2, 3, 1, 2, 3)
#----------------------------------------------------------------------------------
--------------------------------------
a = 10, 20, 30
print(a, id(a))
a = a * 3
print(a, id(a))
a = 10, 20, 30
print(a, id(a))
a *= 3
print(a, id(a)) # id farklı çıkacak, çünkü demetlerde sona ekleme mümkün değil
#----------------------------------------------------------------------------------
--------------------------------------
Demetlerle listeler kullanım bakımından birbirine benzemektedir. Pekiyi ne
zaman listeleri ne zaman demetleri tercih
etmeliyiz?
1) Eğer veri yapısı üzerinde onu yarattıktan sonra ekleme, silme gibi
değişikler yapılacaksa mecburen listeleri kullanırız.
Çünkü demetlerde yaratıldıktan sonra değişiklik yapılamamaktadır.
2) Liste türünden nesneler demet türünden nesnelere göre daha fazla yer kaplama
eğilimindedir. Çünkü onların değiştirilebilir,
büyütülebilir ve küçültülebilir olmaları nedeniyle nesne içerisinde bu işlemler
için bazı bilgilerin bulundurulması gerekmektedir.
Bu nedenle eğer veri yapısı içerisindeki elemanlar üzerinde bir değişiklik
yapmayacaksak, veri yapısına eleman ekleyip ondan bir şeyler
silmeyeceksek demetleri tercih etmeliyiz.
#----------------------------------------------------------------------------------
--------------------------------------
Dolaşılabilir bir nesnenin elemanları "açım (unpacking)" denilen bir sentaksla
değişkenlere atanabilmektedir. Açım (unpacking)
işlemi Ruby' Python gibi dillerde eskiden beri vardı. Daha sonraları C# gibi,
Swift gibi dillere ve hatta C++'a bile bu özellik
eklenmiştir. Açım işlemi eskiden yalnızca demetler üzerinde yapılabiliyordu.
Ancak daha sonra dolaşılabilir her nesne açılabilir
yapıldı. Dolayısıyla açım sentaksı da genelleştirildi. Açım için aşağıdaki üç
sentaks tamamen aynı anlama gelmektedir:
t = 10, 20, 30
x, y, z = t
x = t[0]
y = t[1]
z = t[2]
Örneğin:
[x, y, z] = 'ali'
x = 'a'
y = 'l'
z = 'i'
Açım işleminde sol taraftaki sentaksın bir önemi yoktur. Üç sentaks biçimi de
tamamen birbirleriyle eşdeğerdir.
#----------------------------------------------------------------------------------
--------------------------------------
(x, y, z) = t
print(x, y, z) # 10 20 30
x, y, z = t
print(x, y, z) # 10 20 30
[x, y, z] = t
print(x, y, z) # 10 20 30
x, y, z = 'van'
print(x, y, z) # v a n
#----------------------------------------------------------------------------------
--------------------------------------
Açım (unpacking) yapılırken = operatörünün sağındaki dolaşılabilir nesnenin
eleman sayısının açımdaki değişken sayısı
ile tamamen aynı olması gerekir. Aksi takdirde exception oluşmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Açılacak dolaşılabilir nesnedeki elemanlardan bazıları liste, demet gibi başka
bir dolaşılabilir nesne olabilir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
x, y, z = a
#----------------------------------------------------------------------------------
--------------------------------------
Açım işlemi özyinelemeli (recursive) bir biçimde de yapılabilir. Yani açılan
bir eleman dolaşılabilir bir nesne ise o da açılabilir.
Tabii bu durumda ek parantezler kullanılmalıdır. Ancak bu parantezlerin demet
parantezi ya da liste parantezi olması arasında
bir farklılık yoktur. Örneğin:
Örneğin:
>>> a, (b, (c, d, e), f), g = [10, [20, (30, 40, 50), 60], 70]
>>> a, b, c, d, e, f, g
(10, 20, 30, 40, 50, 60, 70)
#----------------------------------------------------------------------------------
--------------------------------------
x, y, z = a
print(x, y, z)
#----------------------------------------------------------------------------------
--------------------------------------
Tabii açım işlemi aslında bir çeşit atama işlemidir. Yani açım sonucunda
değişkenlere aslında dolaşılabilir nesnenin elemanlarının
adresi yerleştirilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
x, y, z = a
#----------------------------------------------------------------------------------
--------------------------------------
Python'da iki değişkenin değerinin yer değiştirilmesi açım (unpacking) işlemi
ile kolay bir biçimde yapılabilmektedir.
Örneğin:
>>> a = 10
>>> b = 20
>>> a, b = b, a
>>> print(a, b)
20 10
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
print(a, b) # 10 20
a, b = b, a
print(a, b) # 20 10
#----------------------------------------------------------------------------------
--------------------------------------
range isimli fonksiyon bize range isimli dolaşılabilir bir sınıf nesnesi
vermektedir. range fonksiyonunun bize verdiği
dolaşılabilir nesne dolaşıldığında start değerinden başlayarak stop değerine
kadar (start dahil stop dahil değil)
step miktarı artırımlarla int değerler elde edilmektedir. range fonksiyonu tek
argümanlı, iki argümanlı ya da üç argümanlı
kullanılabilmektedir:
range(stop)
range(start, stop)
range(start, stop, step)
>>> r = range(10)
>>> a = list(r)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Yani tek argümanlı kullanımsa aslında start değeri 0, step değeri 1 gibiymiş
gibi işlem uygulanmaktadır. Bu durumda tek
argümanlı kullanımda biz aslında argüman olarak stop değerini vermiş oluruz.
Dolaşım sırasında start değeri her zaman dolaşıma
dahildir ancak stop değeri dahil değildir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
range fonksiyonunu iki argümanla çağırırsak birinci argüman start değerini
ikinci argüman stop değerini belirtir.
(start dahil stop dahil değildir.) Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
a = list(r)
print(a) # [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
#----------------------------------------------------------------------------------
--------------------------------------
range fonksiyonu üç argümanla kullanıldığında birinci argüman start değerini,
ikinci argüman stop değerini ve üçüncü
argüman da step değerini belirtir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
a = list(r)
print(a) # [10, 12, 14, 16, 18]
#----------------------------------------------------------------------------------
--------------------------------------
range fonksiyonunun üç parametresi de int türden olmak zorundadır. Böylece
range nesnesi dolaşıldığında nesne her zaman bize
int değerler verir. Bu durum range fonksiyonu ile noktalı artırımlar
yapamayacağımız anlamına da gelmektedir. Örneğin:
r = range(0, 1, 0.1)
Böyle range nesnesi yaratmak istersek step değeri float olduğu için exception
oluşacaktır. Yuvarlama hataları ile kararsız
durumların oluşmaması noktalı artırımlara, float türden start ve stop
değerlerine izin verilmemiştir. Örneğin eğer noktalı
artırımlara izin verilseydi şöyle sorunlar ortaya çıkabilirdi:
#----------------------------------------------------------------------------------
--------------------------------------
range fonksiyonunda step değeri negatif verilebilir. Bu durumda dolaşım
sırasında büyükten küçüğe değerler elde edilir.
Tabii eğer step değeri negatif ise start değerinin stop değerinden daha büyük
olması gerekir. Yine stop değeri dahil değildir.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
a = list(r)
print(a) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
a = list(r)
print(a) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
#----------------------------------------------------------------------------------
--------------------------------------
16. Ders - 01/06/2022-Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Tabii range fonksiyonunun step parametresi 0 olamaz. Eğer bu parametre 0
girilirse exception (ValueError) oluşacaktır.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
range sınıfı Python Standart Kütüphanesinde bir "sequence container" olarak ele
alınmıştır. "Sequence container" terimi Python'da
elemanlar arasında öncelik sonralık ilişkisi olan ve elemanlarına [...]
operatörü ile erişilen veri yapılarına betimlemektedir.
>>> r = range(100)
>>> k = r[30:60:3]
>>> k
range(30, 60, 3)
Burada biz r range nesnesini dilimleyerek ondan başka bir range nesnesi elde
ettik. range nesnelerinin dilimlenmesine seyrek
bir biçimde gereksinim duyulmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
r = range(100)
k = r[30:60:3]
a = list(k)
print(a) # [30, 33, 36, 39, 42, 45, 48, 51, 54, 57]
#----------------------------------------------------------------------------------
--------------------------------------
range nesnesinin belli bir elemanına [...] operatör ile erişebiliriz. Ancak
range nesnesinin elemanları değiştirilememektedir.
Örneğin:
>>> r = range(100)
>>> r[23]
23
>>> r[23] = 100
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'range' object does not support item assignment
#----------------------------------------------------------------------------------
--------------------------------------
r = range(10, 20)
val = r[3]
print(val) # 13
#----------------------------------------------------------------------------------
--------------------------------------
Listelerde ve demetlerde kullandığımız in ve not in operatörleri range
nesnelerinde de kullanılabilmektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
r = range(10, 20)
result = 15 in r
print(result) # True
result = 25 in r
print(result) # False
#----------------------------------------------------------------------------------
--------------------------------------
Teknik olarak range sınıfı "değiştirilemez (immutable)" bir sınıftır.
Dolayısıyla bu sınıfta append, extend, remove gibi
metotlar bulunmaz. Ancak index ve count metotları sınıfta bulunmaktadır.
Örneğin:
>>> r = range(100)
>>> [Link](67)
1
>>> r = range(30, 60, 3)
>>> [Link](39)
3
#----------------------------------------------------------------------------------
--------------------------------------
r = range(10, 20)
result = [Link](15)
print(result) # 5
result = [Link](12)
print(result) # 1
#----------------------------------------------------------------------------------
--------------------------------------
range nesnelerinin dolaşıldıkları takdirde kaç eleman verecekleri yine built-in
len fonksiyonuyla elde edilebilir.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
r = range(10, 20)
result = len(r)
print(result) # 10
#----------------------------------------------------------------------------------
--------------------------------------
range nesnesinde belirtilen start, stop ve step değerleri sınıfın "start",
"stop" ve "step" örnek öznitelikleri (instance attibutes)
ile elde edilebilmektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
r = range(10, 20, 3)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi "dolaşılabilir (iterable)" nesne her dolaşıldığında yeniden
aynı değerleri verebilen nesnelerdi.
Oysa "dolaşım (iterator)" nesneleri bir kez dolaşıldığında biten, yani ikinci
kez dolaşıldığında bittiği için artık
bir değer vermeyen nesnelerdir. Teknik olarak her dolaşım nesnesi dolaşılabilir
bir nesne gibidir. Ancak dolaşım bitince
artık bir daha dolaşılsa da değer vermez. Özetle bir dolaşılabilir nesne
dolaşıldığında bitiyorsa o nesneye özel olarak
"dolaşım (iterator)" nesnesi denilmektedir.
>>> r = range(10)
>>> a = list(r)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> b = list(r)
>>> b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir değeri doğrudan fonksiyona argüman olarak vermekle önce onu bir değişkene
atayıp değişkeni argüman olarak vermek arasında
işlevsel bir farklılk yoktur. Örneğin:
r = range(10)
a = list(r)
a = list(range(10))
Bu durumda yine bir range nesnesi yaratılacak ve o nesne (yani onun adresi)
list fonksiyonuna geçirilecektir. Örneğin:
a = list((1, 2, 3, 5))
Burada da önce bir demet nesnesi yaratılacak o demet nesnesi list fonksiyonuna
geçirilecektir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Matematikte farklı (distinct) elemanların oluşturduğu topluluğa küme (set)
denilmektedir. Python'da da bu matematiksel anlamı
destekleyecek biçimde bir küme veri yapısı bulunmaktadır.
Kümeler set isimli sınıfla temsil edilmiştir. Kğme türünden bir değişkene type
fonksiyonunu uyguladığımızda onun set
isimli bir sınıf türünden olduğunu görürüz. Örneğin:
>>> type(s)
<class 'set'>
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Kümelerde elemanlar arasında öncelik sonralık ilişkisi yoktur. Yani kümelerde
bir sıra yoktur. (Matematikteki kümelerde de
bir sıra kavramı yktur). Dolayısıyla biz kümenin elemanlarına [...] operatörü
ile erişemeyiz. Küme elemanlarını [...] operatörü ile
dilimleyemeyiz. Bir küme print fonksiyonu ile yazdırıldığında onun
elemanlarının hangi sırada yazdırıldığının bir önemi yoktur.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Kümelerde [...] operatörü kullanılmadığı için kümeler üzerinde dilimleme
(slicing) işlemleri de yapılamamaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Kümelerin eleman sayısı built-in len fonksiyonuyla elde edilebilmektedir.
Örneğin:
result = len(s)
print(result) # 5
#----------------------------------------------------------------------------------
--------------------------------------
Bir kümeye zaten onda var olan bir elemanı eklemeye çalışmak exception
oluşturmaz. Eleman ekleneceği zaman elaman zaten varsa
eklenmez. Dolayısıyla aşağıdaki küme oluşturma işlemi tamamen geçerlidir:
a = {10, 20, 10, 30, 20}
>>> s = {1, 2, 1, 1, 3, 4, 1, 5, 2, 3}
>>> print(s)
{1, 2, 3, 4, 5}
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Boş bir küme söz konusu olabilir. Ancak boş bir küme {} biçiminde oluşturulmaz.
Buradaki {} ifadesi boş bir küme değil
boş bir sözlük (dicitonary) oluşturmaktadır. Sözlükler konusu izleyen bölümde
ele alınacaktır.
>>> d = {}
>>> type(d)
<class 'dict'>
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Kümeler set isimli sınıfla temsil edilmektedir. Boş bir küme set fonksiyonunun
argümansız çağrılmasıyla oluşturulabilmektedir.
Örneğin:
>>> s = set()
>>> type(s)
<class 'set'>
>>> s
set()
>>> len(s)
0
#----------------------------------------------------------------------------------
--------------------------------------
a = {}
s = set()
print(s, type(s)) # set() <class 'set'>
#----------------------------------------------------------------------------------
--------------------------------------
set fonksiyonu da tıpkı list ve tuple fonksiyonlarında olduğu gibi argüman
olarak bizden dolaşılabilir bir nesne alıp
o nesneyi dolaşarak o değerlerden bir küme oluşturabilmektedir. Örneğin:
>>> a = [1, 2, 1, 4, 8, 2, 5, 8, 1]
>>> s = set(a)
>>> s
{1, 2, 4, 5, 8}
Örneğin:
>>> s = set('ankara')
>>> s
{'n', 'a', 'k', 'r'}
#----------------------------------------------------------------------------------
--------------------------------------
s = set(range(10))
print(s) # {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s = set('ankara')
print(s) # {'r', 'k', 'a', 'n'}
#----------------------------------------------------------------------------------
--------------------------------------
Kümeler yinelenenn elemanları atmak için iyi bir araç olabilmektedir.
result = len(set(s))
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Kümeler de "dolaşılabilir (iterable)" nesnelerdir. Bir kümeyi dolaştığımızda
onun elemanlarını elde ederiz. Ancak elemanların
hangi sırada elde edileceği hakkında bir belirleme yapılmamıştır. Yani biz
kümeyi dolaştığımızda onun tüm elemanlarını elde ederiz
ancak sırası konusunda bir garanti verilmemektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Bir elemanın küme içerisinde olup olmadığının belirlenmesi için yine "in" ve
"not in" operatörleri kullanılmaktadır.
Genel olarak kümeler gibi veri yapıları arka planda "hash tabloları (hash
tables)" ya da "ikili ağaçlarla (binary trees)" denilen
algoritmik veri yapılarıyla gerçekleştirilmektedir. Örneğin CPython
yorumlayıcısında kümeler algoritmik olarak "hash tabloları"
ile gerçekleştirilmiştir. Bu veri yapıları özellikle algoritmik aramalarda en
çok tercih edilen veri yapılarındandır.
Bu nedenle kümelerde "var mı yok mu" testi listeler ve demetlere göre çok daha
hızlı yapılabilmektedir. Çok sayıda elemandan
oluşan toplulukta in ve not in operatörleriyle "var mı yok mu" testi yapacaksak
listeleri ve demetleri değil kümeleri tercih
etmeliyiz. Az sayıda eleman için böyle bir hız farkı anlamlı değildir. Listeler
ve demetlerde "var mı yok mu" testi ancak
"sıralı arama (sequential search)" denilen yöntemle yapılabilmektedir. Bunun
ise zaman maliyeti yüksektir.
#----------------------------------------------------------------------------------
--------------------------------------
result = 10 in s
print(result) # True
result = 10 not in s
print(result) # False
#----------------------------------------------------------------------------------
--------------------------------------
set nesnesindne hash tablosu oluşturabilmek için set elemanlarının
"hashlenebilir (hashable)" olması gerekmektedir.
Temel türlerin hash'lenabilir olduğunu, demetlerin hash'lenebilir olduğunu
ancak listelerin hash'lenebilir olmadığını
anımsayınız. Bu durumda bir liste bir kümenin elemanı yapılamamaktadır.
Kümelerin kendisi de hash'lenebilir değildir.
Bu nedenle kümenin bir elemanı bir küme de olamaz. Eğer biz bir kümeye eleman
olarak bir liste ya da küme verirsek bu durumda
exception (TypeError) oluşur. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi bir liste (ve küme) hiçbir zaman hash'lenebilir değildir.
Ancak demetler "eğer onların elemanları
hash'lenebilir ise" hash'lenebilir. olmaktadır. Yani örneğin bir demetin
elemanı bir liste ise artık demet hash'lenebilir
olmaz:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da bool değerlerden hash değeri elde edilirken True için 1, False için 0
değeri elde edilmektedir. Örneğin:
>>> hash(b)
1
>>> b = False
>>> hash(b)
0
Dolayısıyla bool bir değer bir küme elemanı yapılırken bu hash değeri
kullanılacağı için dikkat etmek gerekir. Örneğin:
Aynı durum float ve int nesneler için de benzer biçimde söz konusudur:
Aslında hash tablolarında aynı hash değerine ilişkin elemanlar tabloda bir
arada bulunabilmektedir. Yukarıdaki problem aslında
hash değeri ile karşılaştırma sırasında ortaya çıkmaktadır. Çünkü aşağıdaki
karşılaştırmalar True değerini vermektedir:
>>> 1 == True
True
>>> 1.0 == 1
True
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Kümeler değiştirilebilir türlerdir. Yani biz bir set nesnesi içerisindeki
değeri kümeden çıkartabiliriz. Yeni bir değeri kümeye
ekleyebiliriz.
set sınıfının add isimli metodu tek bir elemanı kümeye eklemek için
kullanılmaktadır. Örneğin :
Anımsanacağı gibi listelerde tek bir elemanın eklenmesi append isimli metotla
yapılıyordu. "append" sözcüğü sona ekleme
anlamında listeler için daha uygun olabilir. Kümelerde "sona ekleme" diye bir
kavram olmadığı için "append" ismi yerine
"add" ismi tercih edilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
[Link](60)
print(s) # {50, 20, 40, 10, 60, 30}
print(s) # {50, 20, (100, 200, 300), 40, 10, 60, 30}
#----------------------------------------------------------------------------------
--------------------------------------
Bir grup elemanı kümeye tek hamlede eklemek için update metodu
kullanılmaktadır. (bu metodu mantıksal olarak listelerdeki extend
metoduna benzetebiliriz. Tabii extend sona ekleme yapmaktadır. Kümelerede sıra
olmadığına göre update metodu sona eklemez.)
update metodu "dolaşılabilir bir nesneyi parametre olarak alır. Nesneyi
dolaşarak elde edilen değerleri kümeye ekler.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
[Link]('ankara')
print(s) # {200, 10, 'a', 'k', 20, 30, 'n', 100, 'r', 40, 300, 50}
#----------------------------------------------------------------------------------
--------------------------------------
Tabii daha önceden de belirttiğimiz gibi zaten var olan bir elemanın yeniden
kümeye eklenmeye çalışılması bir exception'a yol
açmayacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Kümedeki bir elemanı silmek için remove metodu kullanılmaktadır. remove ile
silinecek eleman listede yoksa exception (KeyError)
oluşmaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Kümelerden eleman silmek için discard isimli bir metot da bulunmaktadır.
discard metodu silinmek istenen eleman yoksa
exception oluşturmaz. Sadece silme işlemini yapmaz. remove metodundan tek
farkı budur. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
set sınıfında eleman silme ile ilgili pop isimli bir metot da vardır. Ancak set
sınıfının pop isimli metodu parametresizdir.
Bu metot her çağrıldığında kümeden gelişigüzel bir elemanı siler ve sildiği
elemanı bize geri dönüş değeri olarak verir.
Ancak küme boşsa exception (KeyError) oluşmaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
val = [Link]()
print(val)
val = [Link]()
print(val)
val = [Link]()
print(val)
val = [Link]()
print(val)
print(s) # set()
#----------------------------------------------------------------------------------
--------------------------------------
Kümelerde eleman silme işlemi listelere göre çok daha hızlı yapılmaktadır.
Listelerde bir eleman silinmesi sırasında listenin
elemanlarının kaydırılması gerekmektedir. Halbuki kümelerde böyle bir kaydırma
yapılmadan çok hızlı bir biçimde eleman
silinebilmektedir. Hash tablolarında eleman silme işlemi çok hızlı
yapılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
set sınıfının clear metodu kümedeki tüm elemanları silmek için
kullanılmaktadır. Metot parametreye sahip değildir. Örneğin:
>>> s = set(range(10))
>>> s
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> [Link]()
>>> s
set()
#----------------------------------------------------------------------------------
--------------------------------------
[Link]()
print(s) # set()
#----------------------------------------------------------------------------------
--------------------------------------
set sınıfının copy isimli metodu kümenin bir kopyasını oluşturmak için
kullanılmaktadır. Tabii buradaki kopyalama da aslında
yine "sığ kopyalama (shallow copy)" biçiminde yapılmaktadır. Yani aslında küme
elemanları da asıl nesnelerin adreslerini tutmaktadır.
Dolayısıyla kopyalamada aslında adresler kopyalanmaktadır. Asıl nesneler
kopyalanmamaktadır. Örneğin:
>>> s = set(range(10))
>>> s
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> [Link]()
>>> s
set()
>>> s = set(range(10))
>>> s
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> k = [Link]()
>>> k
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> id(s)
2567818069376
>>> id(k)
2567817420576
#----------------------------------------------------------------------------------
--------------------------------------
[Link](100)
>>> s = set(range(10))
>>> s
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> k = s
>>> k
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> id(s)
2567818070048
>>> id(k)
2567818070048
>>> [Link](100)
>>> s
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100}
>>> k
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100}
#----------------------------------------------------------------------------------
--------------------------------------
[Link](100)
#----------------------------------------------------------------------------------
--------------------------------------
17. Ders - 06/06/2022-Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
İki kümenin ortak elemanlarının bulunması işlemine "kesişim (intersection)"
denilmektedir. Kesişim işlemi set sınıfının intersection
metoduyla ya da & operatörüyle yapılabilmektedir. a ve b iki küme olmak üzere:
c = [Link](b)
işlemi ile
c = a & b
işlemi aynı sonucu verecektir. Arada şöyle bir farklılık vardır: & operatöründe
sağ taraftaki operand yalnızca set ya da ileride görecek olduğumuz
frozenset türünden olmak zorundadır. Ancak intersection metodunun parametresi
herhangi bir dolaşılabilir nesne olabilir.
Örneğin:
>>> s = {'ali', 10, 'veli', 'eskişehir'}
>>> k = {10, 'eskişehir', 'adana', 20}
>>> result = [Link](k)
>>> result
{10, 'eskişehir'}
>>> result = [Link](['ali', 'adana', 'eskişehir'])
>>> result
{'ali', 'eskişehir'}
>>> result = s & k
>>> result
{10, 'eskişehir'}
#----------------------------------------------------------------------------------
--------------------------------------
c = a & b
print(c) # {'veli', 20, 30}
c = [Link](b)
print(c) # {'veli', 20, 30}
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte klavyeden (stdin dosyasından) iki sözcük okunmuştur. Bu iki
sözcüğün ortak karakterleri ekrana yazdırılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
s = set(word1)
result = [Link](word2)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Aslında intersection metodunda biz birden fazla dolaşılabilir nesnesyi de
agüman olarak verebiliriz. Bu durumda sol
taraftaki nesneyle argüman olarak verilmiş dolaşılabilir nesnelerin kesişimleri
bulunmaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
İki kümenin birleşimi '|' operatörü ile ya da set sınıfının union metodu ile
elde edilebilmektedir. Burada da yine '|'
operatörünün sağ tarafındaki operand set ya da frozenset türünden olabilir.
Ancak union metodunun parametresi herhangi bir dolaşılabilir
nesne olabilir. Yine union metoduna birden fazla dolaşılabilir nesneyi argüman
olarak verebiliriz. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
c = a | b
print(c) # {'ayşe', 'veli', 40, 10, 'selami', 20, 'fatma', 30,
'ali'}
c = [Link](b)
print(c) # {'ayşe', 'veli', 40, 10, 'selami', 20, 'fatma', 30,
'ali'}
c = [Link]('ankara')
print(c) # {'k', 10, 20, 'a', 'ali', 30, 'n', 'veli', 'r',
'selami'}
#----------------------------------------------------------------------------------
--------------------------------------
İki kümenin farkı '-' operatörü ile ya da difference metodu ile elde
edilebilir. Yine diğerlerinde olduğu gibi '-' operatörünün
sağ tarafındaki operand set ya da frozenset türünden olmak zorundadır. Ancak
difference metodunun parametresi herhangi bir dolaşılabilir
nesne türünden olabilir. Örneğin:
c = a - b
print(c) # {'selami', 10, 'ali'}
c = [Link](b)
print(c) # {'selami', 10, 'ali'}
#----------------------------------------------------------------------------------
--------------------------------------
İki kümenin ortak olmayan elemanlarının elde edilmesine "exor" işlemi
denilmektedir. Exor işlemi '^' operatörü ile ya da
symmetric_diffrence isimli metotla yapılmaktadır. Yine '^' operatörünün sağ
tafındaki operand set ya da frozenset türünden
olabilir. Ancak symmetric_difference metodunun parametresi herhangi bir
dolaşılabilir nesne türünden olabilmektedir.
Örneğin:
c = a ^ b
print(c) # {'ayşe', 40, 10, 'selami', 'fatma', 'ali'}
c = a.symmetric_difference(b)
print(c) # {'ayşe', 40, 10, 'selami', 'fatma', 'ali'}
c = a.symmetric_difference(x)
print(c) # {'ayşe', 'veli', 'selami', 30}
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda görmüş olduğumuz temel küme işlemlerinin update'li versyonları da
vardır. Bunlar aşağıda listelenmiştir:
a &= b ya da a.intersection_update(b)
a |= b ya da [Link](b)
a -= b ya da a.diffrence_update(b)
a ^= b ya da a.symmetric_difference_update(b)
Yukarıdaki işlemlerde sonuç başka bir nesne olarak elde edilmemektedir. Sonuç
soldaki nesne değiştirilerek elde edilmektedir.
Yani bu işlemler sonucunda yeni bir nesne yaratılmamaktadır. Örneğin:
c = [Link](b)
#----------------------------------------------------------------------------------
--------------------------------------
Bir a kümesinin tüm elemanları b kümesinde varsa "a kümesi b kümesinin bir alt
kümesidir (subset)". Alt küme işleminin tersine
"üst küme (superset)" denilmektedir. Öz alt küme (proper subset) kendisi dahil
olmayan alt kümedir. Her küme kendisinin
bir alt kümesidir ancak öz alt kümesi değildir. Benzer biçimde her küme
kendisinin bir üst kümesidir ancak öz üst kümesi değildir.
Bir kümenin diğer kümenin alt kümesi olup olmadığı "<=" operatörü ile ya da
issubset operatörü ile belirlenir. Yine "<="
operatöründe sağ taraftaki operand set ya da frozenset türünden olabilir. Ancak
issubset metodunda parametre herhangi bir dolaşılabilir
sınıf türünden olabilir. Benzer biçimde üst küme kontrolü de ">=" ya da
issuperset metoduyla yapılabilmektedir. Benzer biçimde >= operatörünün
sol tarafındaki ve sağ tarafındaki nesneler set ya da frozenset türünden olmak
zorundadır. Ancak issuperset metodunun parametresi herhangi
bir dolaşılabilir nesne olabilir.
Öz alt küme için bir metot yoktur. Bu işlem '<' operatörü ile yapılır. Öz üst
küme kontrolü için de bir metot yoktur. Bu işlem '>' operatörü ile yapılır.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
result = b < a
print(result) # True
result = a < a
print(result) # False
result = a <= a
print(result) # True
result = a > b
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
İki kümenin hiçbir ortak elemanı yoksa bu iki kümeye "ayrık kümeler (disjoint
sets)" denilmektedir. Ayrıklık kontrolü
set sınıfının isdisjoint metodu ile yapılmaktadır. Bu metodun parametresi
herhangi bir dolaşılabilir nesne olabilir.
Disjoint işleminin bir operatör karşılığı yoktur. Tabii işlem s & k == set()
biçiminde de yapılabilir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
result = [Link](b)
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
Python'da "seyrek kullanılan" set sınıfının değiştirilemez (immutable) biçimi
olan "frozenset" isimli bir sınıf da vardır.
set sınıfı ile frozenset sınıfı arasındaki farklılıklar şunlardır:
1) frozenset sınıfı değiştirilebilir olmadığı için onun add gibi, update gibi,
intersection_update gibi, remove gibi
metotları yoktur. Ancak değiştirme işlemi yapmayan metotları set sınıf ile
aynıdır.
#----------------------------------------------------------------------------------
--------------------------------------
Bir frozenset nesnesi küme parantezleriyle yaratılamaz. Ancak frozenset
sınıfının tür fonksiyonu olan frozenset fonksiyonu
ile yaratılabilir. Bu fonksiyon herhangi bir dolaşılabilir nesneyi parametre
olarak alabilmektedir. Örneğin:
>>> fs = frozenset(['ali', 'veli', 'selami', 'ayşe', 'fatma'])
>>> fs
frozenset({'veli', 'fatma', 'ayşe', 'ali', 'selami'})
#----------------------------------------------------------------------------------
--------------------------------------
print(len(fs))
#----------------------------------------------------------------------------------
--------------------------------------
set ile frozenset nesneleri '|', '&', '-', '^' işlemlerine sokulabilmektedir.
Bu durumda işlemin sonucu soldaki operandın
türüne bağlıdır. Eğer soldaki operand set türündense sonuç set türünden,
frozenset türündense sonuç frozenset türünden
olur. Tabii bunların metot karşılıklarında her zaman sonuç metodun çağrıldığı
nesnenin türünden olmaktadır. Başka bir deyişle
yukarıdaki operatör işlemlerinde sol taraftaki ya da sağ taraftaki operand set
ya da frozenset olabilmektedir. Ancak işlemin sonucu
her zaman sol taraftaki operand türünden olur. Örneğin (sembolik biçimde
yazıyoruz):
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
result = fs & s
print(result, type(result)) # frozenset({1}) <class 'frozenset'>
result = s & fs
print(result, type(result)) # {1} <class 'set'>
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda da belirtildiği gibi frozenset sınıfında &=, |=, ^=, -= gibi
işlemlerde bir update yapılmamaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Kümenin bir elemanı küme olamaz. Çünkü kümler hash'lenebilir değildir. Ancak
kümenin bir elemanı frozenset olabilir.
Çünkü frozenset hash'lenebilir biçimdedir. Örneğin:
Anımsanacağı gibi bir demetin elemanı bir liste olabiliyordu. Ancak bir
frozenset'in elemanlarının hash'lenebilir
olması gerekmektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sözlükler (dictionaries) "anahtar-değer" çiftlerini tutan, anahtar verildiğinde
değerin hızlı bir biçimde bulunmasını sağlayan
veri yapılarıdır. Sözlükler hızlı aramayı mümkün hale getirmek için özel
algoritmik yöntemlerden faydalanmaktadır. Programlama
dillerinde sözlükler "hash tabloları (hash tables)", "dengelenmiş ikili ağaçlar
(balanced binary tree)", "sıralı diziler (sorted arrays)"
biçiminde gerçekleştirilebilmektedir. CPython gerçekleştiriminde sözlükler
"hash tabloları" yoluyla oluşturulmuştur.
Burada sözlklerin küme partantezleri içerisinde anahtardan sonra ':' ile değer
belirtilerek yaratıldığını görüyorsunuz.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 100, 'veli': 200, 'selami': 300, 'ayşe': 1000, 'fatma': 400}
print(d) # {'ali': 100, 'veli': 200, 'selami': 300, 'ayşe': 1000, 'fatma':
400}
print(type(d)) # <class 'dict'>
#----------------------------------------------------------------------------------
--------------------------------------
Sözlükler "dict" isimli sınıfla temsil edilmiştir. Örneğin:
>>> d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> d
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> type(d)
<class 'dict'>
>>> print(d)
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sözlüklerde anahtarların hash'lenebilir olması gerekmektedir. Ancak değerler
için böyle bir koşul yoktur. Dolayısıyla anahtarlar
int, float, str gibi türlerden olabilir. Ancak liste ve küme olamaz. Demetlerin
eğer elemanları hash'lenebilir ise hash'lenebilir
olduğunu anımsayınız. Benzer biçimde frozenset nesneleri de hash'lenebilir
durumdadır. Örneğin:
Görüldüğü gibi biz sözüğün anahtarını hash'able olmayan bir türden yapmak
istersek xception (TypeError) oluşmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sözlüklerde anahtarlar ve değerler herhangi bir türden olabilirler (anahtarlar
hash'lenebilir olmak zorundadır). Anahtarların ve
değerlerin aslında aynı sözlük içerisinde aynı türden olması gerekmemektedir.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Sözlüklerin kendisi de hash'lenebilir değildir. Dolayısıyla bir sözlük başka
bir sözlüğe anahtar yapılamaz. Ancak bir szölüğün
değeri bir sözlük olabilir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Listelerin, demetlerin, kümelerin ve sözlklerin elemanları sabit yerine
değişken de olabilir. Python'da bu bağlamda
sabitle değişken arasında bir farklılık yoktur. Biz bir sabit oluşturduğumuzda
zaten o sabit için önce bir nesne yaratılmakta sonra o nesnenin
adresi kullanılmaktadır. Bunun doğrudan yapılmasıyla dolaylı yapılması arasında
bir farklılık yoktur. Örneğin:
x = 10
y = 20
z = 30
a = [x, y, z]
Burada da liste elemanlarında yine 10, 20, 30 değerlerinin bulunduğu int türden
nesnelerin adresleri tutulmaktadır.
x = 26
d = {x: 'eskişehir'}
Biz her ne kadar atama işlemini bir operatör gibi ele almış olsak da aslında
Python'da atama işlemi bir operatör değil
deyim statüsündedir. Dolayısıyla aşağıdaki bir durum geçerli değildir:
Tabii walrus gerçekten atama işlemi yapıp değer üreten bir operatördür.
Dolayısıyla aşağıdaki gibi bir durum geçerlidir:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda da belirtildiği gibi sözlükler dict isimli sınıfla temsil edilmiştir.
dict sınıfının tür fonksiyonu olan dict fonksiyonu
ile de sözlük yaratabiliriz. Örneğin boş bir sözlük dict() biçiminde
yaratılabilir:
>>> d = dict()
>>> d
{}
>>> d = {}
>>> d
{}
Boş küme parantezlerinin boş bir küme yaratmadığına boş bir sözlük yarattığına
dikkat ediniz. Boş bir kme yaratmak için
mecburen set() çağrısı kullanılmaktadır:
>>> s = set()
>>> s
set()
#----------------------------------------------------------------------------------
--------------------------------------
d = dict()
print(d) # {}
d = {}
print(d) # {}
#----------------------------------------------------------------------------------
--------------------------------------
dict fonksiyonuna biz iki elemanlı dolaşılabilir nesnelerden oluşan
dolaşılabilir bir nesneyi argüman olarak geçirirsek,
dict fonksiyonu bu nesneyi dolaşır. Her dolaşımda iki elemanlı bir
dolaşılabilir nesne elde eder. O iki elemanlı dolaşılabilir
nesnenin ilk elemanı anahtar ikinci elemanı değer olacak biçimde bir sözlük
nesnesi oluşturur. Örneğin:
>>> a = [('ali', 10), ('veli', 20), ('selami', 30), ('ayşe', 40), ('fatma',
50)]
>>> a
[('ali', 10), ('veli', 20), ('selami', 30), ('ayşe', 40), ('fatma', 50)]
>>> d = dict(a)
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
Burada a nesnesini dolaştığımızda biz iki elemanlı dolaşılabilir nesneler elde
edilmektedir. İşte dict fonksiyonu bunlardan
sözlük yapmaktadır. Bu örnekte listenin elemanları birer demettir. Ancak tabii
bunun tersi de söz konusu olabilirdi. Örneğin:
>>> a = (['ali', 10], ['veli', 20], ['selami', 30], ['ayşe', 40], ['fatma',
50])
>>> a
(['ali', 10], ['veli', 20], ['selami', 30], ['ayşe', 40], ['fatma', 50])
>>> d = dict(a)
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
Ya da örneğin:
>>> a = (('ali', 10), ('veli', 20), ('selami', 30), ('ayşe', 40), ('fatma',
50))
>>> d = dict(a)
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
Örneğin:
>>> a = (['ali', 10], ['veli', 20], ['selami', 30], ['ayşe', 40], ['fatma',
50], 'ak', 'tk', 'xy')
>>> d = dict(a)
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50, 'a': 'k', 't':
'k', 'x': 'y'}
#----------------------------------------------------------------------------------
--------------------------------------
a = [('ali', 10), ('veli', 20), ('selami', 30), ('ayşe', 40), ('fatma', 50)]
d = dict(a)
print(d) # {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
a = [['ali', 10], ['veli', 20], ['selami', 30], ['ayşe', 40], ['fatma', 50]]
d = dict(a)
print(d) # {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
a = (['ali', 10], ['veli', 20], ['selami', 30], ['ayşe', 40], ['fatma', 50])
d = dict(a)
print(d) # {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
#----------------------------------------------------------------------------------
--------------------------------------
18. Ders - 08/06/2022-Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
dict fonksiyonunda "değişken=değer" biçiminde argümanlar girdiğimizde dict bize
o değişkenlerin string halini anahtar,
'=' operatörünün sağındakini de değer yaparak bir sözlük nesnesi oluşturur.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
print(d) # {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
#----------------------------------------------------------------------------------
--------------------------------------
Burada değişken ismi yerine başka bir şey getirilemez. Örneğin aşağıdaki
geçerli değildir:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir sözlükte anahtarı verip değeri elde etmek için [...] operatörü kullanılır.
Köşeli parantezin içerisine anahtar yazılır.
Operatör de bize o anahtarın değerini verir. [...] operatöründe verdiğimiz
anahtar sözlükte yoksa exception (KeyError) oluşur.
Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> val = d['ayşe']
>>> val
40"
>>> val = d['fatma']
>>> val
50
>>> val = d['sacit']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'sacit'
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
print(d)
val = d['veli']
print(val) # 20
val = d['fatma']
print(val) # 50
#----------------------------------------------------------------------------------
--------------------------------------
Tabii bir sözlükte anahtar ve değerlerin türleri hep aynı olmak zorunda
değildir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
print(d)
val = d['veli']
print(val) # 20
val = d[10]
print(val) # ali
#----------------------------------------------------------------------------------
--------------------------------------
dict sınıfının get isimli metodu da anahtar verildiğinde değerin alınması için
kullanılır. Ancak get anahtar sözlükte yoksa
exception oluşturmaz. İkinci parametresiyle girdiğimiz değere geri döner. Bu
ikinci parametre için argüman girmeyebiliriz. Bu durumda
get anahtar bulunamazsa None değeri ile geri dönmektedir. Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> val = [Link]('ayşe')
>>> val
40
>>> val = [Link]('sacit')
>>> print(val)
None
>>> val = [Link]('ayşe', 'bulunamadı')
>>> val
40
>>> val = [Link]('sacit', 'bulunamadı')
>>> val
'bulunamadı'
>>> val = [Link]('sacit', 0)
>>> val
0
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
print(d)
val = [Link]('sacit')
print(val) # None
#----------------------------------------------------------------------------------
--------------------------------------
Sözlüklerde "in" ve "not in" operatörleri belli bir anahtarın sözlükte olup
olmadığını anlamak için kullanılabilir.
Sözlüklerde de tıpkı kümelerde olduğu gibi "in" ve "not in" operatörleri çok
hızlı çalışmaktadır. Bu operatörler anahtarın
varlığını sorgulamaktadır, değerlerin değil. Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> 'ayşe' in d
True
>>> 20 in d
False
>>> 20 not in d
True
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
print(d)
result = 'selami' in d
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
Built-in len fonksiyonuna biz bir sözlük nesnesini verirsek fonksiyon bize
sözlükte kaç tane anahtar-değer çifti olduğunu verir.
Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> len(d)
5
>>> d = {}
>>> len(d)
0
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
result = len(d)
print(result) # 5
d = {}
result = len(d)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Sözlükler de dolaşılabilir (iterable) nesnelerdir. Bir sözlük nesnesi
dolaşıldığında yalnızca "anahtarlar" elde edilir.
Ancak bu anahtarların elde edilmesi Python 3.6'ya kadar herhangi bir sırada
olabiliyordu. Ancak Python 3.6 ve sonrasında
artık sözlük nesneleri dolaşıldığında anahtarlar eklenme sırasıyla elde edilir
hale getirildi. Yani mevcut Python süsümlerinde
bir sözlük nesnesi dolaşıldığında her zaman anahtarlar sözlükteki sıraya göre
elde edilmektedir. Bu durumun Python 3.6'da
garanti edilmediğine dikkat ediniz. Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> a = list(d)
File "<stdin>", line 1
a = list(d)
IndentationError: unexpected indent
>>> a = list(d)
>>> a
['ali', 'veli', 'selami', 'ayşe', 'fatma']
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
a = list(d)
print(a) # ['ali', 'veli', 'selami', 'ayşe', 'fatma']
#----------------------------------------------------------------------------------
--------------------------------------
Sözlükler değiştirilebilir türlerdir. Biz sözlüğe yeni bir anahtar-değer çifti
ekleyebiliriz, sözlükten bir anahtar-değer çiftini
silebiliriz. Mevcut bir anahtarın değerini değiştirebiliriz.
Bir sözlükte anahtarlar "tektir (unique)". Ancak daha önce var olan bir
anahtara ilişkin anahtar-değer çiftini sözlüğe eklemek
istersek bu durum herhangi bir soruna yol açmaz. Artık daha önceki anahtarın
değeri değiştirilmiş olur. Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ali': 40, 'selami': 50}
>>> d
{'ali': 40, 'veli': 20, 'selami': 50}
Tabii sözlükteki bir anahtarın değiştirilmesi biçiminde bir işlem yoktur. Zaten
bu işlem bir anahtar-değer çiftinin silinip yeni bir anahtar-değer
çiftinin eklenmesi ile aynı anlamdadır.
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50, 'ali': 100}
print(d) # {'ali': 100, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
#----------------------------------------------------------------------------------
--------------------------------------
Sözlüğe yeni bir anahtar değer çifti eklemenin en basit yolu köşeli parantezli
atama yapmaktır. Yani:
d[key] = value
ataması sözlüğe yeni bir anahtar-değer çifti eklemektedir. Ancak burada eğer
anahtar zaten varsa anahtarın değeri değiştirilir.
Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d['sacit'] = 60
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50, 'sacit': 60}
>>> d['selami'] = 100
>>> d
{'ali': 10, 'veli': 20, 'selami': 100, 'ayşe': 40, 'fatma': 50, 'sacit': 60}
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
print(d) # {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma':
50}
d['sacit'] = 60
print(d) # {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma':
50, 'sacit': 60}
d['veli'] = 100
print(d) # {'ali': 10, 'veli': 100, 'selami': 30, 'ayşe': 40, 'fatma':
50, 'sacit': 60}
#----------------------------------------------------------------------------------
--------------------------------------
Bir sözlüğe tek hamlede birden fazla anahtar-değer çifti eklemek için update
metodu kullanılmaktadır. update metodunun parametresinin
dolaşılabilir bir nesne olması gerekir. Bu durumda bu nesne dolaşıldıkça iki
elemanlı dolaşılabilir nesneler elde edilmelidir.
update metodu da onların birinci elemanlarını anahtar, ikinci elemanlarını
değer yaparak bunları sözlüğe ekler. Yani update metodunun
parametresi dict fonksiyonundaki gibi olmalıdır. Tabii zaten anahtar sözlükte
bulunuyorsa onun yine değeri değiştirilecektir. Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> [Link]([('sacit', 100), ('mehmet', 200), ('sibel', 300)])
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50, 'sacit': 100,
'mehmet': 200, 'sibel': 300}
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
print(d) # {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma':
50}
print(d) # {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50,
'sibel': 60, 'kazım': 70, 'hasan': 90}
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda da belirttiğimiz gibi bir sözlük nesnesi dolaşıldığında anahtarlar her
zaman Python'ın 3.6 ve sonrasında sözlüğe
eklenme sırası dikkate alınarak elde edilmektedir. Yani biz bir sözlüğe köşeli
paramtezlerle ya da update metodu ile ekleme
yaptığımızda bu eklenenlerin anahtarları sözlük dolaşıldığında bizim
eklediğimiz sırada elde edilecektir. Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d['ahmet'] = 100
>>> [Link]([('sacit', 200), ('mehmet', 300), ('sibel', 400)])
>>> a = list(d)
>>> a
['ali', 'veli', 'selami', 'ayşe', 'fatma', 'ahmet', 'sacit', 'mehmet', 'sibel']
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir sözlükten bir anahtar-değer çiftini silmek için pop isimli metot
kullanılır. pop metodu bizden anahtarı alır. Anahtar-değer
çiftini sözlükten siler. Ancak silinen değeri bize geri dönüş değeri olarak
verir. pop metodu anahtarı bulamazsa ve tek argüman ile
çağrılmışsa exception (KeyError) oluşturmaktadır. Ancak pop iki argümanla da
çağrılabilir. Bu durumda anahtar bulunamzsa
ikinci argümandaki değer geri döndürülmektedir. Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> val = [Link]('selami')
>>> val
30
>>> d
{'ali': 10, 'veli': 20, 'ayşe': 40, 'fatma': 50}
>>> val = [Link]('sibel')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'sibel'
>>> val = [Link]('sibel', 'anahtar yok')
>>> val
'anahtar yok'
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
print(d) # {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma':
50}
value = [Link]('ayşe')
print(value) # 40
#----------------------------------------------------------------------------------
--------------------------------------
Bir sözlüğün tüm anahtarlarını keys isimli metotla, tüm değerlerini values
isimli metotla elde edebiliriz. Bu metotlar bize
dolaşılabilir nesne verir. O nesne dolaşıldığında anahtarlar ve değerler elde
edilecektir. Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> a = list([Link]())
>>> a
['ali', 'veli', 'selami', 'ayşe', 'fatma']
>>> b = list([Link]())
>>> b
[10, 20, 30, 40, 50]
Yine keys metodu Python 3.6 ve sonrasında bize anahtarları onların listeye
eklenme sırasına göre vermektedir. Benzer biçimde
yine Python 3.6 ve sonrasında values metodu da bize değerleri anahtarların
eklenme sırasına göre vermektedir. Ancak Python'un
önceki sürümleri bunu garanti etmemektedir.
Burada keys metodunun aslında çok da gerekmediği ancak values metodunun
gerektiği gibi bir sonuç çıkartabilirsiniz. Çünkü zaten
biz sözlük nesnesini dolaştığımızda onunanahtarlarını elde edebiliyorduk.
#----------------------------------------------------------------------------------
--------------------------------------
d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
result = [Link]()
a = list(result)
print(a) # [10, 20, 30, 40, 50]
result = [Link]()
a = list(result)
print(a) # ['ali', 'veli', 'selami', 'ayşe', 'fatma']
#----------------------------------------------------------------------------------
--------------------------------------
Python'da çeşitli konularda karşımıza "view" nesnesi kavramı çıkabilmektedir.
View nesnesi ana bir nesnenin bir bölümünü
ya da tamamını temsil eden bir nesnedir. Ancak ana nesne üzerinde değişiklik
yapıldığında bu view nesnesi bu değişikliği görür.
Bazı view nesneleri read only, bazıları read/write olabilmektedir. Eğer bir
view nesnesi read/write biçimdeyse o view nesnesi
üzerinde değişiklilkler yapıldığında bundan ana nesne etkilenecektir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
keys ve values metotlarının bize verdiği dolaşılabilir nesneler "view"
nesneleridir. Örneğin:
>>> d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> d
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> result = [Link]()
>>> a = list(result)
>>> a
[10, 20, 30, 40, 50]
>>> d[60] = 'sacit'
>>> d
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma', 60: 'sacit'}
>>> a = list(result)
>>> a
[10, 20, 30, 40, 50, 60]
Burada biz keys() metodunu çağırarak view nesnesini elde ettik. Daha sonra
sözlüğe eleman ekledik. Sonra bu view nesnesini
dolaştığımızda elemanın ekli olduğunu gördük.
#----------------------------------------------------------------------------------
--------------------------------------
d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
keys = [Link]()
a = list(keys)
print(a) # [10, 20, 30, 40, 50]
values = [Link]()
a = list(values)
print(a) # ['ali', 'veli', 'selami', 'ayşe', 'fatma']
d[60] = 'nurettin'
a = list(keys)
print(a) # [10, 20, 30, 40, 50, 60]
a = list(values)
print(a) # ['ali', 'veli', 'selami', 'ayşe', 'fatma', 'nurettin']
#----------------------------------------------------------------------------------
--------------------------------------
dict sınıfının items isimli metodu dolaşılabilir bir view nesnesi verir. Bu
nesne dolaşıldığında anahtar-değer çiftleri
iki elemanlı demetler biçiminde elde edilmektedir. Örneğin:
>>> d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> d
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> items = [Link]()
>>> items
dict_items([(10, 'ali'), (20, 'veli'), (30, 'selami'), (40, 'ayşe'), (50,
'fatma')])
>>> a = list(items)
>>> a
[(10, 'ali'), (20, 'veli'), (30, 'selami'), (40, 'ayşe'), (50, 'fatma')]
>>> t = tuple(items)
>>> t
((10, 'ali'), (20, 'veli'), (30, 'selami'), (40, 'ayşe'), (50, 'fatma'))
>>> s = set(items)
>>> s
{(30, 'selami'), (40, 'ayşe'), (20, 'veli'), (50, 'fatma'), (10, 'ali')}
>>> d[60] = 'sacit'
>>> d
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma', 60: 'sacit'}
>>> a = list(items)
>>> a
[(10, 'ali'), (20, 'veli'), (30, 'selami'), (40, 'ayşe'), (50, 'fatma'), (60,
'sacit')]
Yine Python 3.6'ya kadar items metodundan elde edilen dolaşılabilir nesne
dolaşıldığında elemanların hangi sırada elde
edileceğinin bir garantisi yoktu. Ancak Python 3.6 ile birlikte artık
dolaşımdan elde edilen değerler onların sözlüğe eklenme
sırasına göre olmaktdır.
#----------------------------------------------------------------------------------
--------------------------------------
d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
result = [Link]()
a = list(result)
print(a) # [(10, 'ali'), (20, 'veli'), (30, 'selami'), (40, 'ayşe'), (50,
'fatma')]
d[60] = 'sacit'
a = list(result)
print(a) # [(10, 'ali'), (20, 'veli'), (30, 'selami'), (40, 'ayşe'), (50,
'fatma'), (60, 'sacit')]
#----------------------------------------------------------------------------------
--------------------------------------
dict sınıfının clear isimli metodu sözlük içerisindeki anahtar-değer
çiftlerinin hepsini siler. Örneğin:
>>> d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> id(d)
2500835703104
>>> [Link]()
>>> d
{}
>>> id(d)
2500835703104
#----------------------------------------------------------------------------------
--------------------------------------
d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
print(d, id(d)) # {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50:
'fatma'} 2449660834816
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
dict sınıfının copy isimli metodu sözlüğün bir kopyasını bize verir. Tabii
kopya çıkartma işlemi yine "sığ kopyalama (shallow copy)"
biçiminde yapılmaktadır. Örneğin:
>>> d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> id(d)
2500835703104
>>> [Link]()
>>> d
{}
>>> id(d)
2500835703104
>>> d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> id(d)
2500835738496
>>> k = [Link]()
>>> k
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> id(k)
2500835703104
>>> id(d[10])
2500835729264
>>> id(k[10])
2500835729264
Burada copy metodu ile çıkartılan kopya farklı bir sçzlük nesnesidir. Ancak iki
sözlük nesnesindeki değerlerin (ve anahtarlarınd da)
aynı olduğu görülmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
k = [Link]()
print(d, id(d))
print(k, id(k))
#----------------------------------------------------------------------------------
--------------------------------------
Daha önce sözlükten anahtarı verip değer elde etmenin iki yolunu görmüştük:
[...] operatörü ve get metodu. Aslında bu iş için
üçüncü bir metot daha vardır. O da setdefault isimli metottur. setdefault
metodu bize her zaman anahtarın değerini verir. Eğer anahtarı bulamazsa
setdefault o anahtarı bizim verdiğimiz değerler sözlüğe ekler eklediği
anahtarın değerini bize verir. Yani setdefault anahtarı bulnazsa
ikinci parametresiyle belirtilen değerle anahtarı eklemekteve bu değeri bize
vermektedir. Metotta ikinci parametre için argüman
girilmeyebilir. Eğer ikinci argüman girilmezse ve setdefault anahtarı bulamazsa
anahtar için değer olarak sözlüğe None eklemektedir.
setdefault anahtarı bulursa ikinci argümanı hiç kullanmaz. Bu ikinci argüman
"eğer anahtar bulamazsa" kullanılmaktadır.
Örneğin:
>>> d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> d
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> val = [Link](20, 'kaya')
>>> val
'veli'
>>> val = [Link](60, 'kaya')
>>> val
'kaya'
>>> d
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma', 60: 'kaya'}
>>> val = [Link](30)
>>> val
'selami'
>>> val = [Link](70)
>>> print(val)
None
>>> d
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma', 60: 'kaya', 70:
None}
#----------------------------------------------------------------------------------
--------------------------------------
d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
val = [Link](20)
print(val) # veli
#----------------------------------------------------------------------------------
--------------------------------------
Biz daha önce sözlükten pop metodu ile anahtar-değer çiftini silmiştik. Ancak
del deyimi ile de sözlükten bir anahtar-değer
çiftini silebiliriz. Bunu sağlamak için del deyiminde anahtar yine köşeli
parantez içerisinde verilir. Örneğin:
del d[10]
>>> d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> d
{10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
>>> del d[40]
>>> d
{10: 'ali', 20: 'veli', 30: 'selami', 50: 'fatma'}
>>> del d[100]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 100
#----------------------------------------------------------------------------------
--------------------------------------
d = {10: 'ali', 20: 'veli', 30: 'selami', 40: 'ayşe', 50: 'fatma'}
del d[40]
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi string'ler Python'da str isimli bir sınıfla temsil
edilmektedir. Yani biz string'leri tek tırnak, iki tırnak,
üç tek tırnak ya da üç çift tırnak ile yarattığımızda aslında str sınıfı
türünden bir nesne yaratmış olmaktayız. Yine biz str
sınıfının "değiştirilemez (immutable)" bir sınıf olduğunu görmüştük. Biz string
üzerinde onu yarattıktan sonra bir değişiklik
yapamamaktayız.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Tıpkı listeler gibi, demetler gibi string'ler de Python'da "sequence type"
grubundadır. Yani adreta string'ler onları oluşturan
larakterden oluşan bir dizilim gibi düşünülebilir.
>>> s = 'ankara'
>>> result = s[2]
>>> result
'k'
>>> type(result)
<class 'str'>
Tabii Python'da Java, C# gibi bazı dillerde olduğu gibi tek bir karakteri
temsil eden bir tür yoktur. Dolayısıyla biz Python'da
bir string'in belli bir indeksteki karakterini yine bir string olarak elde
ederiz.
>>> s = 'ankara'
>>> c = s[2]
>>> c
'k'
>>> s[2] = 'x'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir stirng'in karakter uzunluğu built-in len fonksiyonu ile elde edilebilir.
Örneğin:
>>> s = 'ankara'
>>> result = len(s)
>>> result
6
>>> k = ''
>>> result = len(k)
>>> result
0
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankara'
result = len(s)
print(result) # 6
#----------------------------------------------------------------------------------
--------------------------------------
Elemana erişimde yine negatif indeksler liste ve demetlerle aynı biçimde
kullanılabilmektedir. Örneğin:
>>> s = 'ankara'
>>> s[-1]
'a'
>>> s[-2]
'r'
>>> s = 'ankara'
>>> s
'ankara'
>>> result = s[2:6]
>>> result
'kara'
>>> result = s[1:3]
>>> result
'nk'
>>> result = s[2:-2]
>>> result
'ka'
Örneğin bir string'i ters çevirmek için yine aynı s[::-1] işlemini
uygulayabiliriz:
>>> s = 'ankara'
>>> k = s[::-1]
>>> s
'ankara'
>>> k
'arakna'
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankara'
k = s[2:4]
print(k) # ka
k = s[::-1]
print(k) # arakna
k = s[-1]
print(k) # a
#----------------------------------------------------------------------------------
--------------------------------------
İki string '+' operatörü ile toplanabilir. Bu durumda yeni bir string nesnesi
yaratılır. Bu yeni string nesnesi iki string'in
birleştirilmesinden oluşur. Örneğin:
>>> s = 'ankara'
>>> k = 'izmir'
>>> result = s + k
>>> result
'ankaraizmir'
Java, C# gibi bazı dillerde bir string ile başka bir türden nesne
toplanabilmektedir. Bu durumda o dillerde string olmayan tür
otomatik olarak string türüne dönüştürülüp string toplamı yapılmaktadır. Ancak
Python'da bir özellik yoktur. Yani biz Python'da
örneğin bir string ile int bir değeri toplayamayız. (Halbuki bu işlem Java ve
C# gibi bazı dillerde yapılabilmektedir.)
Bu tür durumlarda bizim açıkta diğer operand'ı str türüne dönüştürmemiz
gerekir. Örneğin:
>>> a = 10
>>> s = 'a = ' + a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
>>> s = 'a = ' + str(a)
>>> s
'a = 10'
#----------------------------------------------------------------------------------
--------------------------------------
a = 'ankara'
b = 'izmir'
c = a + b
print(c) # ankaraizmir
#----------------------------------------------------------------------------------
--------------------------------------
iki string toplandığında yeni bir string elde edilmektedir. Bu yeni string iki
string'in uç uca eklenmesinden elde edilen yazıdan
oluşur. Ancak s += k gibi bir işlem mevcut s yazısının sonuna ekleme yapma
anlamına gelmez. Çünkü string'ler değiştirilemez (immutable)
nesnelerdir. Dolayısıyla s += k tamamen s = s + k anlamına gelir. Anımsanacağı
gibi bu durum değiştirilemez olan demetlerde de
böyleydi. Ancak listeler değiştirilebilir olduğu için orada += işlemi sona
ekleme anlamına geliyordu. Örneğin:
>>> s = 'ali'
>>> k = 'veli'
>>> id(s)
1986218231280
>>> id(k)
1986218230640
>>> s += k
>>> s
'aliveli'
>>> id(s)
1986218223088
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankara'
k = 'izmir'
result = s + k
print(result) # ankaraizmir
print(id(s))
s += k # s = s + k
print(id(s))
print(s) # ankaraizmir
#----------------------------------------------------------------------------------
--------------------------------------
Bir string tıpkı listelerde ve demetler olduğu gibi "yineleme (repitition)"
işlemine sokulabilir. Bu durumda string n defa
kendisiyle toplanmaktadır. Yine yineleme işleminde "çarpılan değer" 0 ya da
negatif bir değerse boş string elde edilmektedir.
Örneğin:
>>> s = '*' * 20
>>> s
'********************'
>>> s = 'ali' * 5
>>> s
'alialialialiali'
>>> s = 'veli' * 0
>>> s
''
#----------------------------------------------------------------------------------
--------------------------------------
s = '*' * 10
print(s) # **********
s = 'ali' * 3
print(s) # alialiali
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte klavyeden bir yazı girilmektedir. Sonra o yazı üstte ve altta
tireler olacak biçimde afiş gibi ekrana
yazdırılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
print('-' * len(s))
print(s)
print('-' * len(s))
#----------------------------------------------------------------------------------
--------------------------------------
Şimdi de str sınıfının metotları üzerinde duracağız. Ancak bu konuda bir
noktaya dikkat çekmek istiyoruz. str sınıfının
"değiştirilemez" olduğunu yukarıda belirtmiştik. Bu durumda str sınıfının sanki
yazıyı değiştirecekmiş gibi işlemler yapan
metotları aslında mevcut yazıyı değiştirmezler. Bize yeni bir yazı verirler.
Zaten yaratılmış bir string nesnesi üzerinde
değişiklik yapmanın bir yolu yoktur. Biz aşağıda metotların sanki mevcut yazı
üzerinde değişiklik yapıyormuş gibi bir
anlatım tarzı uygulayacağız. Aslında burada söylemek istediğimiz şey metodun
değiştirilmiş yeni yazı ile geri döndüğüdür.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının capitalize isimli metodu parametresizdir. Bu metot bize aynı
yazıdan yalnızca ilk harfi büyük olan yeni bir
yazı oluşturup onu vermektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
k = [Link]()
print(k) # Bugün hava çok güzel
print(s) # bugün hava çok güzelçok güzel
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının title isimli metodu yazının tüm sözcüklerinin ilk harfini büyük
harf yapar (yani büyük harf yapılmış yazıyla
geri döner.) Bu metot da parametresizdir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
k = [Link]()
print(k) # Bugün Hava Çok Güzel
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
19. Ders - 13/06/2022-Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının center isimli metodu bir yazıyı belli bir genişlikte ortalar.
Metodun birinci parametresi geri döndürülecek yazının
uzunluğunu belirtir. Yani yazı bu uzunluktaki yazının içerisinde
ortalanacaktır. Eğer metot tek argümanla çağrılırsa yazının iki tarafındaki
boş alan boşluk karakteriyle doldurulur. Ancak metot iki argümanla da
çağrılabilir. Bu durumda ikinci argüman tek karakterli bir string
olarak girilmek zorundadır. Bu ikinci argümanda belirtilen karakter iki tarafın
doldurulacağı karakteri belirtir. Ancak eğer birinci
parametre ile belirtilen uzunluktan yazının uzunluğu çıkarıldığında tek sayı
kalıyorsa bu durumda fazlalığın ne tarafa verileceği
konusunda "Python Standard Library Reference" içerisinde bir şey
söylenmemiştir. Bu durum herhangi bir garantinin verilmediği
anlamına gelmektedir. Örneğin:
>>> s = 'ankara'
>>> k = [Link](20)
>>> print(':' + k + ':')
: ankara :
>>> s = 'ali'
>>> k = [Link](7)
>>> print(':' + k + ':')
: ali :
>>> k = [Link](6)
>>> print(':' + k + ':')
: ali :
>>> k = [Link](20, '.')
>>> print(':' + k + ':')
:........ali.........:
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankara'
k = [Link](20);
print(':' + k + ':')
k = [Link](20, 'x');
print(':' + k + ':')
k = [Link](11, 'x');
print(':' + k + ':')
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının find metodu bir yazı içerisinde bir yazıyı aramak ve yerini
bulmak için kullanılmaktadır. Metot tek argümanla
çağrılırsa arama baştan sona kadar yapılmaktadır. Eğer yazı bulunursa find
metodu bize yazının "ilk bulunduğu yerin" asıl yazıdaki
indeks numarasıyla geri döner. Eğer yazı bulunamazsa find -1 değeri ile geri
dönmektdir. Örneğin:
>>> s = 'ankara'
>>> result = [Link]('k')
>>> result
2
>>> result = [Link]('ar')
>>> result
3
>>> result = [Link]('a')
>>> result
0
>>> result = [Link]('x')
>>> result
-1
#----------------------------------------------------------------------------------
--------------------------------------
index = [Link]('yarın')
print(index) # -1
#----------------------------------------------------------------------------------
--------------------------------------
find metodu iki argümanlı da çağrılabilir. Bu durumda arama ikinci argümanla
belirtilen indeksten başlatılır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
find metodu üç argümanlı da çağrılabilir. Bu durumda üçüncü argüman aramanın
bitirileceği indeksi belirtir. Ancak bu indeks
aramaya dahil değildir. (Yani sanki önce dilimleme yapılıp bu dilimlemenin
içerisinde find uygulanıyor gibi düşünebilirsiniz.)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
rfind isimli metot tamamen find metodu metodu gibidir. Ancak son bulunan yerin
indeks numarasını verir. Başka bir deyişle
aramayı sondan başa doğru yapar. Yine metot iki ve üç argümanlı çağrılabilir.
Argümanlar yine yazının başından itibaren indeks belirtir.
Yani bu duurmda ikinci ve üçüncü argümanlar sanki aranacak kısmın başını ve
sonunu belirtiyor gibidir. (Yani sanki önce dilimleme
yapılıp bu dilimlemenin içerisinde rfind uygulanıyor gibi düşünebilirsiniz.)
Örneğin:
>>> s = 'adıyaman'
>>> pos = [Link]('a', 1, 6)
>>> pos
4
>>> pos = [Link]('a', 1)
>>> pos
6
>>> pos = [Link]('a')
>>> pos
6
Aşağıdaki örnekte Windows'tai bir yol ifadesinin hedefindeki dosya ismi elde
edilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
path = r'c:\windows\system\[Link]'
index = [Link]('\\')
fname = path[index + 1:]
print(fname) # [Link]
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki programı girişi klavyeden isteyecek biçimde de değiştirebiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
index = [Link]('\\')
fname = path[index + 1:]
print(fname)
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının find metodu ile aynı işlemleri yapan index isimli bir metodu,
rfind metodu ile ile aynı işlemleri yapan
rindex isimli bir metodu da vardır. Yine index ve rindex de tek argümanla, iki
argümanla ya da üç argümanla çağrılabilmektedir.
Bu bakımdan bunların find metotlarından bir farkı yoktur. index ve rindex
metotlarının find ve rfind metotlarından tek farkları
başarısızlık durumunda exception index ve rindex metotlarının -1 ile geri
dönmek yerine exception (ValueEroor) oluşturmasıdır.
Halbuki anımsanacağı üzere find ve rfind başarısızlık durumunda -1 değerine
geri dönüyordu. Örneğin:
>>> s = 'ankara'
>>> pos = [Link]('a')
>>> pos
0
>>> pos = [Link]('a')
>>> pos
5
>>> pos = [Link]('x')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: substring not found
#----------------------------------------------------------------------------------
--------------------------------------
result = [Link]('çok')
print(result) # 11
result = [Link]('a')
k = s[result:]
print(k) # a çok güzel
#----------------------------------------------------------------------------------
--------------------------------------
count metodu belli bir karakterin ya da yazının asıl yazı içerisinde kaç tane
olduğu bilgisini bize verir. Örneğin:
>>> s = 'ankara'
>>> result = [Link]('a')
>>> result
3
>>> s = 'bugün hava çok güzel, evet hava çok güzel'
>>> result = [Link]('hava')
>>> result
2
#----------------------------------------------------------------------------------
--------------------------------------
s = 'istanbul\'da iş buldum'
result = [Link]('bul')
print(result) # 2
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının isxxx isimli bir grup metodu vardır. Bu metotlar yazının tüm
karakterlerinin belirtilen koşulu sağlayıp sağlamadığına
bakmaktadır. Örneğin isalpha metodu yazının tüm karakterlerinin alfabetik
karakter olup olmadığına bakar. Python 3'lü versüyonlardan
sonra tamamen UNICODE sisteme geçmiştir. Yani bu metotlar UNICODE tablodaki
bütün dillerin harflerini bu testte dikkate alırlar.
Önemli isxxx metotları şunlardır:
Örneğin:
>>> s = 'ankara'
>>> [Link]()
True
>>> [Link]()
False
>>> s = '1234'
>>> [Link]()
True
>>> s = 'ali Ankara'
>>> [Link]()
False
>>> s = '1abc'
>>> [Link]()
False
>>> s = 'abc1'
>>> [Link]()
True
>>> s = ' \t \n '
>>> [Link]()
True
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ağrı-04'
k = 'AĞRIDağı'
m = '1293456789'
r = ' '
result = [Link]()
print(result) # True
result = [Link]()
print(result) # False
result = [Link]()
print(result) # True
result = [Link]()
print(result) # True
result = [Link]()
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının join isimli metodu argüman olarak dolaşılabilir bir nesne alır.
Ancak bu dolaşılabilir nesne dolaşıldıkça
string'ler elde edilmelidir. join metodu bu dolaşılabilir nesnenin elemanlarını
aralarına metodun çağrılmasında belirtilen yazıyı
ayraç yaparak bir yazı biçiminde birleştirir ve bize böyle bir yazı verir.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
k = [Link](names)
print(k) # alixxxvelixxxselamixxxayşexxxfatma
s = ', '
k = [Link](names)
print(k) # ali, veli, selami, ayşe, fatma
k = ' '.join(names)
print(k) # ali veli selami ayşe fatma
k = ''.join(names)
print(k) # aliveliselamiayşefatma
k = ' '.join('ankara')
print(k) # a n k a r a
k = '\n'.join(names)
print(k)
"""
ali
veli
selami
ayşe
fatma
"""
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının split metodu adeta join metodunun tersi gibidir. split yazının
ayrıştırılacağı yazıyı parametre olarak alır.
Bu parametre yazıyı ayraç kabul ederek yazıyı parçalara ayırır ve bu
parçalardan bir string listesi yapar bize o string listesini
verir. split parametresiz kullanılırsa tüm boşluk karakterlerini ayıraç kabul
eder. Yani bir yazıyı tüm boşluklardan ayrıştırmak
için split parametresiz kullanılabilir. Örneğin:
join ile split metotlarının adresta ters işlemler yaptıklarına dikkat ediniz:
#----------------------------------------------------------------------------------
--------------------------------------
a = [Link](',')
print(a) # ['ali', ' veli', ' selami', ' ayşe', ' fatma']
a = [Link](', ')
print(a) # ['ali', 'veli', 'selami', 'ayşe', 'fatma']
a = [Link](' ')
print(a) # ['ali,', 'veli,', 'selami,', 'ayşe,', 'fatma']
s = 'ali,,,veli,,,selami'
a = [Link](',')
print(a) # ['ali', '', '', 'veli', '', '', 'selami']
d = '13/06/2022'
a = [Link]('/')
print(a) # ['13', '06', '2022']
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının strip isimli metodu argümansız çağrılırsa metot yazının başındaki
ve sonundaki boşluk karakterlerini atar.
Bu işlev pek çok programama dilinde "trim" isimli fonksiyonlarla yapılmaktadır.
strip fonksiyonu parametreli kullanılırsa
strip edilecek karakteri de belirlememize olanak sağlar. Burada argüman birden
fazla karakter olarak girilirse tüm bu karakterler
bireysel olarak strip karakterleri olarak ele alınmaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
k = [Link]()
print(':' + k + ':') # :ankara:
s = 'xxxyyyyyyyyxxxankaraxxxxxxxxxxxxxyyyyyy'
k = [Link]('xy')
print(':' + k + ':') # :ankara:
#----------------------------------------------------------------------------------
--------------------------------------
strip metodunun lstrip ve rstrip isimli benzerleri vardır. lstrip yalnızca sol
taraftaki strip karakterlerini atar, rstrip
ise yalnızca sağ taraftaki strip karakterlerini atar. (strip her iki taraftaki
strip karakterlerini atmaktadır).Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
s = ' bugün hava çok güzel '
result = [Link]()
print(':' + result + ':') # :bugün hava çok güzel :
result = [Link]()
print(':' + result + ':') # : bugün hava çok güzel:
#----------------------------------------------------------------------------------
--------------------------------------
20. Ders - 15/06/2022-ÇArşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının partition isimli metodu yazı içinde aranacak bir yazıyı parametre
olarak alır. Yazıyı bulursa üçlü bir
demete geri döner. Demetin ilk elemanı yazıda bulunan yerin sol tarafındaki
yazıdan, sonraki elemanı bulunan yazının kendisinden ve
sonraki elemanı da bulunan yazının sağ tarafındaki yazıdan oluşacaktır.
Örneğin:
>>> s = 'aliveliselami'
>>> s
'aliveliselami'
>>> result = [Link]('veli')
>>> result
('ali', 'veli', 'selami')
>>> s = 'ali veli selami'
>>> s = ' ali veli selami '
>>> result = [Link]('veli')
>>> result
(' ali ', 'veli', ' selami ')
>>> s = 'aliveliselami'
>>> result = [Link]('selami')
>>> result
('aliveli', 'selami', '')
>>> result = [Link]('ali')
>>> result
('', 'ali', 'veliselami')
>>> result = [Link]('xxx')
>>> result
('aliveliselami', '', '')
Burada eğer yazı asıl yazının başında bulunursa demetin ilk elemanının boş
string olduğuna, sonunda bulunursa demetin son elemanın boş string
olduğuna dikkat ediniz. Eğer Yazı asıl yazıda bulunamazsa demetin ilk elemanı
asıl yazıdan ikinci ve üçüncü elemanları boş
string'ten oluşacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankaraizmiristanbul'
t = [Link]('izmir')
print(t) # ('ankara', 'izmir', 'istanbul')
left, center, right = [Link]('izmir')
print(left) # ankara
print(center) # izmir
print(right) # istanbul
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda da belirtitğimiz gibi eğer partition parametresiyle belirtilen yazıyı
bulamazsa bu durumda yine üçlü demete
geri döner. Demetin birinci elemanı tüm yazıdan, ikinci ve üçüncü elemanları
boş string'lerden oluşur.
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankaraizmiristanbul'
t = [Link]('xxxx')
print(t) # ('ankaraizmiristanbul', '', '')
t = [Link]('ankara')
print(t) # ('', 'ankara', 'izmiristanbul')
t = [Link]('istanbul')
print(t) # ('ankaraizmir', 'istanbul', '')
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının replace isimli metodu bir yazı içerisinde belli bir yazıyı başka
yazıyla yer değiştirir. Metodun iki parametresi vardır.
Birinci parametre aranacak yazıyı, ikinci parametre yer değiştirilecek yazıyı
belirtir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
k = [Link]('istanbul', 'ankara')
print(k) # ankara ankara güzel ankara
#----------------------------------------------------------------------------------
--------------------------------------
replace metodunun isteğe bağlı bir üçüncü parametresi de vardır. Bu parametre
girilecekse int bir sayı olarak girilmelidir.
Bu durumda yalnızca replace burada belirtilen sayıda değiştirme yapar. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının startswith isimli metodu yazının parametresiyle belirtilen yazı
ile başlayıp başlamadığını belirlemek için
kullanılmaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankara'
result = [Link]('an')
print(result) # True
result = [Link]('anka')
print(result) # True
result = [Link]('anki')
print(result) # False
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının endswith isimli metodu yazının parametresiyle belirtilen yazı ile
bitip bitmediğini belirlemek için kullanılmaktadır.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankara'
result = [Link]('ra')
print(result) # True
result = [Link]('kara')
print(result) # True
result = [Link]('an')
print(result) # False
#----------------------------------------------------------------------------------
--------------------------------------
iki string toplandığında yeni bir string elde edilmektedir. Bu yeni string iki
string'in uç uca eklenmesinden elde edilen yazıdan
oluşur. Ancak s += k gibi bir işlem mevcut s yazısının sonuna ekleme yapma
anlamına gelmez. Çünkü string'ler değiştirilemez (immutable)
nesnelerdir. Dolayısıyla s += k tamamen s = s + k anlamına gelir.
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankara'
k = 'izmir'
result = s + k
print(result) # ankaraizmir
print(id(s))
s += k # s = s + k
print(id(s))
print(s) # ankaraizmir
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının upper metodu yazıdaki küçük harfleri büyük harflere lower metodu
da büük harfleri küçük harflere dönüştürür.
Ancak upper ve lower büyük ya da küçük harf olamayan karakterleri olduğu gibi
bırakır. Örneğin:
>>> s = 'AnKaRa-06'
>>> k = [Link]()
>>> k
'ANKARA-06'
>>> k = [Link]()
>>> k
'ankara-06'
Türkçe'deki küçük harf 'i'nin UNICODE büyük harf karşılığı 'I' biçimindedir.
Benzer biçimde büyük harf 'I' karakterinin de
küçük harf karşılığı 'i' biçimindedir. Bu da bazen Türkçe yazıla riçin
istediğimiz sonucun elde edilmesini engeller. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
s = 'AğRı dAğI-04'
k = [Link]()
print(k) # AĞRI DAĞI-04
k = [Link]()
print(k) # ağrı daği-04
#----------------------------------------------------------------------------------
--------------------------------------
İki string >, >=, <, <=, == ve != operatörleriyle karşılaştırılabilir.
Karşılaştırma leksikografik olarak yapılmaktadır.
Leksikografik karşılaştırma sözlükteki sıraya göre karşılaştırmadır. Yani iki
yazıda karşılıklı karakterler aynı olduğu sürece
ilerlenir. İlk aynı olmayan karakterlerin UNICODE sıra numaralarına bakılır.
Hangi karakterin UNICODE sıra numarası büyükse
o yazı diğerinden büyüktür. Tabii daha çok iki string'in eşit olması ya da eşit
olmaması biçiminde karşılaştırmalar yapılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
password = 'maviay'
s = input('Enter password:')
result = s == password
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Karşılaştırmanın Türkçe'ye göre değil UNICODE tabloya göre yapıldığına dikkat
ediniz. Örneğin:
>>> s = 'aysel'
>>> k = 'ayçe'
>>> result = s > k
>>> result
False
s = 'aysel'
k = 'ayçe'
result = s > k
print(result) # False 's'nin UNICODE numarası 'ç'nin UNICODE numarasından
küçük
#----------------------------------------------------------------------------------
--------------------------------------
Tabii bir yazı diğer yazının ilk bölümü ile aynı ise bu durumda uzun olan yazı
daha büyük olur. Yani örneğin 'aliye' yazısı
'ali' yazısından daha büyüktür.
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ali'
k = 'aliye'
result = k > s
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
UNICODE tabloda önce büyük harfler sonra küçük harfler gelmektedir. Zaten
UNICODE toblonun ilk 128 karakteri standart ASCII
tablosu ile aynıdır. Sonraki 128 karakteri ASCII Latin-1 Code Page'i ile
aynıdır. Büyük harflerin tabloda önce gelmesi ASCII
tablosundan kaynaklanmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ali'
k = 'Ali'
result = s > k
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir karakterin UNOCODE tablodaki sıra numarası ord isimli built-in fonksiyonla
elde edilebilmektedir. Örneğin:
ord fonksiyonuna biz tek karakterli bir string'i argüman olarak verebiliriz.
Aksi takdirde exception (TypeError) oluşur. Örneğin:
(C, C++, Java ve C# gibi dillerde bir karakteri tek tırnak içerisine
aldığımızda zaten bu ifade o karakterin ilgili tablodaki
sıra numarasını belirtmektedir. Bu dillerde tek karakterden oluşan yazılar
"char" isimli bir türdendir. Bu char türü de zaten
bu dillerde aritmektik işlemlere sokulabilmektedir. Dolayısıyla bu dillerde ord
gibi bir fonksiyona gereksinim duyulmamaktadır. )
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
ord fonksiyonunun yaptığı şeyin terci chr fonksiyonuyla yapılabilmektedir. chr
fonksiyonu bizden int bir değeri parametre
olarak alır. Onun UNICODE tablodaki karakter karşılığını tek elemanlı bir
string olarak verir.
#----------------------------------------------------------------------------------
--------------------------------------
k = ord(result)
print(k)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki gibi üç değşiken olsun:
a = 10
b = 20
c = 30
a = 10, b = 20, c = 30
a = 10
b = 20
c = 30
Ya da örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
str sınıfının format isimli metodu istenildiği kadar çok argüman
alabilmektedir. Bu metot yazı içerisindeki {n} kalıbını yer tutucu
olarak kabul eder ve bu {n} yer tutucusu yerine format metodunun n'inci
argümanın değerini yerleştirir. format metodunun ilk argümanı 0'ıncı
argümanıdır. Örneğin:
Format sentaksındaki sayıların peşi sıra gelme gibi bir zorunluluğu yoktur.
Aörneğin:
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
c = 30
#----------------------------------------------------------------------------------
--------------------------------------
Aynı numaralı yer tutucu yazı içerisinde birden fazla kez kullanılabilir.
Örneğin:
>>> a = 10
>>> b = 20
>>> print('{0}, {1}, {0}'.format(a, b))
10, 20, 10
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
c = 30
#----------------------------------------------------------------------------------
--------------------------------------
format metodundaki argüman türleri herhangi bir türden olabilir.
#----------------------------------------------------------------------------------
--------------------------------------
city = 'Eskişehir'
plate = 26
region = 'İç Anadolu'
#----------------------------------------------------------------------------------
--------------------------------------
Yer turucularda küme parantezinin içi boş bırakılabilir. Bu durumda her boş
küme parantezi argümanlarla sırasıyla eşleştirilmektedir.
Ancak yazıdaki bir yer tutucu numaralı diğeri numarasız olamaz. Ya tüm yer
tutucular numaralı olmalı ya da hiçbiri numaralı olmamalıdır.
Örneğin:
>>> a = 10
>>> b = 20
>>> c = 30
>>> print('a = {}, b = {}, c = {}'.format(a, b, c))
a = 10, b = 20, c = 30
>>> print('a = {}, b = {}, c = {}'.format(a, b, c))
Numarısız yer tutucu kullanımı genellikle tercih edilmektedir. Ancak tabii
biçimde aynı argüman birden fazla kez kullanılamamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
c = 20
#----------------------------------------------------------------------------------
--------------------------------------
fromat metodunda aslında daha ayrıntılı belirlemeler yapılabilmektedir. Biz
şimdilik format metodunun bu ayrıntıları üzerinde
durmayacağız. Bunun için Internet'te çeşitli kaynaklara ya da Python Library
Reference içerisindeki aşağıdaki kısma göz gezdirebilirsiniz:
[Link]
day = 8
month = 5
year = 2007
x = 100
print('{:X}'.format(x)) # 64
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
21. Ders - 20/06/2022 -
Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'a 3.6 ile birlikte "string enterpolasyonu" denilen bir özellik de
eklenmiştir. Aslında string enterpolasyonları
bazı programlama dillerinde uzun süredir bulunmaktaydı. Artık yavaş yavaş pek
çok programlama diline bu özellik sokulmuştur.
String enterpolasyonu bir string'e ona yapışık bir 'f' ya da 'F' harfi önek
getirilerek oluşturulmaktadır. String enterpolasyonunda
yapılanlar string sınıfının format metoduna benzerdir. Ancak string
enterpolasyonunda küme parantezleri içerisinde bir
ifade bulunmak zorundadır. Yorumlayıcı akış sırasında string enterpolasyonu ile
karşılaştığında bizzat kendisi bu ifadenin
değerini o anda hesaplayarak yer tutucu yerine yerleştirir. Örneğin:
a = 10
b = 20
str sınıfının format metodu ismi üzerinde bir metottur. Yani bu metot yoluyla
formatlama yapılacağı zaman formatlama
program çalışırken metot tarafından yapılmaktadır. Oysa string
enterpolasyonları doğrudan yorumlayıcı tarafından program
çalıştırılırken işleme sokulur. Bu nedenle string enterpolasyonları hem daha
kolay bir yazım sunmakta hem de göreli olarak daha hızlı
sonuç vermektedir. Dolayısıyla artık Python programcılları bu tarz
formatlamalar için her zaman string enterpolasyonlarını tercih etmelidir.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
#----------------------------------------------------------------------------------
--------------------------------------
Tabii string enterpolasyonlarında küme parantezlerinin içerisinde aslında
herhangi bir ifade olabilir. Örneğin:
import math
x = 10
print(f"karekök {x} = {[Link](x)}") # karekök 10 =
3.1622776601683795
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
#----------------------------------------------------------------------------------
--------------------------------------
string enterpolasyonunda küme parantezlerinin içerisnnde gerçek tırnaklar
kullanılacaksa asıl string'in tırnağının bu tırnaklarla
karışması engellenmelidir. Örneğin:
s = f'{\', \'.join(a)}'
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda da belirtitğimiz gibi artık (3.6 ve sonrasında) string enterpolasyonu
str sınıfının format metoduna göre tercih
edilmelidir. Ancak yine de seyrek bazı durumlarda str sınıfının format metodu
daha kolay bir kullanım sunabilmektedir. Örneğin
string içerisinde aynı ifadenin değerinin birden fazla kez kullanılması
durumunda string enterpolasyonunda küme parantezleri
içerisinde bu ifadenin tekrar tekrar yazılması gerekir. Halbuki str sınıfının
format metodunda bu işlem daha az tuşa basılarka
yapılabilir. Örneğin:
x = 10
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
String enterpolasyonundaki format kuralları str sınıfının format metodunndaki
gibidir. Örneğin:
day = 8
month = 7
year = 2009
print(f'{day:02d}/{month:02d}/{year:04d}')
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
print(f'{a:<20}{b:>20}') # 10 20
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'ın 2'li versiyonlarında string formatlama string sınıfının % operatör
metodu ile yapılıyordu. O zamanlar C Programlama
Dili bütün programlama dillerini etkisi altına almıştı ve bu biçimdeki
formatlama aslında C Programlama Dilindeki "printf" gibi,
"scanf" fonksiyonlardan esinlenerek oluşturulmuştu. Bu çeşit formatlamada yer
tutucular C'nin printf fonksiyonunda olduğu gibi
% karakterleriyle oluşturulmaktadır. Örneğin %d "int türünü 10'luk sistemde
yazdır", %f "float türünü 10'luk sistemde yazdır anlamına gelmektedir.
Bu yontemde string içerisindeki % karakterleri % operatörünün sağındaki demetin
elemanlarıyla eşleştirilmektedir.
Bu formatlama biçiminde yine printf fonksiyonundaki formatlama biçimleri
("%8.3f" gibi) burada da kullanılabilmektedir. Ancak artık bu yöntem Python'da
eski bir yöntem olarak değerlendirilmektedir. Yeni programlarda artık bu tarzda
formatlama tercih edilmemektedir. Örneğin:
>>> x = 10
>>> y = 20
>>> print('x = %d, y = %d' % (x, y))
x = 10, y = 20
>>> a = 10
>>> print('a = %d' % a)
a = 10
Burada string içerisinde tek bir yer tutucu olduğu için % operatörünün sağında
demet yerine doğrudan bir ifade kullanılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
b = 20
c = 12.3456
print('%-10.3f' % c) # 12.346
#----------------------------------------------------------------------------------
--------------------------------------
Daha önce de belirttiğimiz gibi Python'da genel bir silme semantiği için del
isimli bir deyim bulundurulmuştur.
del deyiminin genel biçimi şöyledi:
>>> a = 10
>>> print(a)
10
>>> del a
>>> print(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
Tabii del deyiminde ',' atomu ile tek hamlede birden fazla değişkeni
silebiliriz. Örneğin:
>>> a = 10; b = 20
>>> print(a, b)
10 20
>>> del a, b
>>> print(a, b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
s = 'ankara'
s = 'izmir'
s = 'ankara'
del s
Burada del deyimi ile s değişkeni silindiği için artık "ankara" yazısına
ilişkin nesneyi gösteren bir değişken kalmayacaktır. Dolayısıyla del deyimi
dolaylı da olsa bu nesnenin silinmesine de önayak olacaktır.
del deyimi ile köşeli parantez ile erişebildiğimiz veri yapılarının elemanları
da silinebilmektedir. Örneğin:
Tabii del ile biz ancak değiştirilebilir veri yapılarında silme yapabiliriz.
Örneğin bir demet değiştirilemez olduğuna göre
demetin bir elemanını del deyimi ile silemeyiz.
del deyimi ile bu biçimde silme yapılırken silme işleminin soldan sağa
yürütüldüğüne dikkat ediniz. Örneğin:
del a[2]
del a[5]
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Python'da farklı türler her zaman == ve != operatöryle karşılaştırılabilirler.
int, float ve bool türlerinin dışındaki
farklı türler == ve != operatörleriyle karşılaştırıldığında == operatörü ile
karşılaştırma her zaman False, != operatör
ile karşılaştırma her zaman True değerini verir. Örneğin:
>>> a = [1, 2, 3]
>>> b = (1, 2, 3)
>>> a == b
False
>>> a != b
True
>>> a == 10
False
>>> b != 10
True
Tabii daha önceden de belirttiğimiz gibi int, float ve bool türleri kendi
aralarında tüm karşılaştırma operatörleriyle karşılaştırılabilir.
Bu durumda sayıların değerleri karşılaştırılmaktadır. Örneğin:
>>> a = 10
>>> b = 10.0
>>> a == b
True
>>> a = 1.0
>>> b = True
>>> a == b
True
#----------------------------------------------------------------------------------
--------------------------------------
a = [10, 20]
b = (10, 20)
result = a == b
print(result) # False
result = a != b
print(result) # True
#----------------------------------------------------------------------------------
--------------------------------------
Ancak int, float ve bool türlerinin dışındaki türlerin >, <, >= ve <=
operatörleriyle karşılaştırılması geçersiz bir durumdur ve exception'a yol açar.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda da belirttiğimiz gibi int, float ve bool türleri farklı tür olsalar da
birbirleriyle karşılaştırılabilmektedir. Örneğin:
>>> a = 10
>>> b = 3.7
>>> a > b
True
>>> c = True
>>> a > c
True
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
İki listeyi ve iki demeti kendi aralarında tüm karşılaştırma operatörleriyle
karşılaştırabiliriz. Bu durumda karşılaştırma
leksikografik biçimde yapılır. Yani karşılıklı elemanlar eşit olduğu sürece
ilerlenir. İlk eşit olmayan elemanların durumlarına
bakılarak karar verilir. Örneğin:
>>> a = [1, 2, 3, 4, 5]
>>> b = [1, 2, 3, 5, 1]
>>> a > b
False
>>> a < b
True
>>> a = [1, 2, 3, 4, 5]
>>> b = [1, 2, 3, 4, 5]
>>> a == b
True
>>> a = [1, 2, 3, 4, 5]
>>> b = [1, 2, 3, 4, 5, 6]
>>> a == b
False
>>> a > b
False
>>> a < b
True
#----------------------------------------------------------------------------------
--------------------------------------
result = a > b
print(result) # False
result = b > a
print(result) # True
result = a == b
print(result) # False
#----------------------------------------------------------------------------------
--------------------------------------
Tabii listeler ve demetler heterojen türlere sahip olabildiğine göre karşılıklı
elemanların karşılaştırılabilir olması
gerekmektedir. Eğer karşılıklı elemanlar karşılaştırılabilir değilse exception
oluşur. Örneğin:
Örneğin
Kümelerde >, >=, <, <= operatörlerinin küme işlemi yaptığını anımsayınız.
(Kümelerde '<' öz alt küme, '>' öz üst küme, '<=' alt küme ve
'>=' üst küme işlemlerini yapmaktadır.) Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
22. Ders - 22/06/2022-Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir satırın başından itibaren ilk boşluk olmayan karaktere kadarki SPACE
sayısına "girinti düzeyi (indent level)" denilmektedir.
Bir satırın girinti düzeyi eğer satırın başında hep SPACE karakteri varsa SPACE
karakterlerinin toplamı olarak hesaplanır.
Ancak satırın başına SPACE ve TAB karakterleri varsa hesap şöyle yapılmaktadır:
Her TAB karakteri görüldüğünde bu TAB karakterlerinin
o zamana kadarki SPACE sayısını 8'in katlarına tamamlamak için n tane SPACE
anlamına geldiği kabul edilir. Bu biçimdeki SPACE'lerin sayısına
bakılır. Örneğin:
Örneğin:
Örneğin aşağıdaki iki satırın girinti düzeyleri editörde bu iki satır alt alta
gözükmüyor olsa bile Python yorumlayıcısına göre aynıdır:
#----------------------------------------------------------------------------------
--------------------------------------
Python programlarında girinti düzeyi 0 ile başlamak zorundadır. Yani Python
programları en soldaki sütuna dayalı bir biçimde yazılır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir programlama dilinde çalıştırma birimlerine "deyim (statement)"
denilmektedir. Yani "imperative dillerde" programın çalışması
deyimlerin çalıştırılmasıyla sağlanmaktadır. Python yorumlayıcısı yukarıdan
aşağıya doğru deyimleri tek tek sırasıyla çalıştırır.
C, C++, Java ve C# gibi pek çok dilde program main ya da Main isimli özel bir
fonksiyondan çalışmaya başlamaktadır. Ancak Python gibi
bazı dillerde program kaynak kodun tepesinden çalışmaya başlar.
Python'da basit deyimler tek parçadan oluşan deyimlerdir. Bu deyimler tek satır
üzerine yazılabilmektedir. Ancak bileşik deyimler
birden fazla parçadan oluşan ve tek satır üzerinde yazılamayan ya da yazılmak
zorunda olmayan deyimlerdir. Basit deyimlerin en önemli
özellikleri birden fazla basit deyimin aynı satıra aralarına ';' atomu
getirilerek yazılabilmesidir.
Python'da basit deyimlerin de bileşik deyimlerin de çeşitli biçimler vardır.
Örneğin if gibi for gibi deyimler Python'da
bileşik deyimler grubundandır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'daki en yalın basit deyim "ifadesel deyimdir (expression statment)". Bir
ifade programın parçası halinde
bulundurulduğunda bu artık deyim olur. Bu tür deyimlere ifadesel deyim denir.
Örneğin:
print(z)
input('Bir yazı giriniz:')
Biz bir ifadeyi bir satıra yazdığımızda artık o ifadenin artık bir deyim heline
geldiğine dikkat ediniz. Örneğin:
print(a + b)
Python'da atama işlemi aslında bir operatör değil bir deyim statüsündedir. Buna
"atama deyimi (assignment statement)" denilmektedir. Örneğin:
x = 10
y = 20
z = x + y
print(z)
Burada ilk üç deyim atama deyimidir. Son deyim ifadesel deyimdir. Ancak
bunların hepsi kategorik olarak basit deyim statüsündedir.
Biz basit deyimleri istersek tek satır üzerinde onların aralarına ';' getirerek
de yazabiliriz:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Programalama dillerinde bileşik deyimlerin parçalarını oluşturan deyimlerin
nasıl yazılması gerektiğine yönelik çeşitli biçimler
bulunmaktadır. Örneğin C, C++, Java ve C# gibi diller bloklama tekniğini
kullanırlar. Bu dillerde bileşik deyimin parçalarını oluşturan
deyimler bloklanarak belirlenir. Bloklama bu dillerde küme parantezleri ile
yapılmaktadır. Örneğin:
if (koşul) {
ifade1;
ifade2;
ifade3;
}
ifade4;
if koşul:
ifade1
ifade2
ifade3
ifade4
Burada ifade1, ifade2 ve ifade3 aynı girinti düzeyine sahip olduğu için bileşik
deyimin parçalarını oluşturmaktadır.
Bir bileşik deyimin içindeki deyimlerin aynı girinti düzeyine sahip olması
gerekir. Örneğin:
if koşul:
ifade1;
ifade2
ifade3
ifade4
Python'da bileşik deyimler genel olarak bir anahtar sözcükle başlatılır, sonra
bunu bir ya da birden fazla ifade izler sonra da bir ':'
atomu bulundurulur.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da bileşik deyimin anahtar sözcüğü ile aynı satıra yazılan birden fazla
basit deyime ya da farklı satırlara
aynı girinti düzeyiyle yazılan birden fazla deyime "suit" denilmektedir. Pek
çok deyim bir suit içermek durumundadır.
Örneğin:
while ifade:
ifade1
ifade2
ifade3
while ifade:
ifade1
ifade2; ifade3
while ifade:
ifade1
ifade2
ifade3
Burada ifade1, ifade2 ve ifade3 bir suit belirtmez. Çünkü aynı girinti düzeyine
sahip değildir.
Suit'i oluşturan "farklı satırlardaki aynı girinti düzeyine sahip deyimler"
arasında boş satırlar olabilir. Bu suit
kuralını bozmaz. Örneğin:
while ifade:
ifade1
ifade2
ifade3
ifade4
#----------------------------------------------------------------------------------
--------------------------------------
if deyimi bir ifadenin doğru ya da yanlış olması durumuna göre farklı
işlemlerini yapılmasını sağlayan en temel bileşik deyimdir.
Genel biçimi şöyledir:
if <ifade>: <suite>
[else: <suite>]
if ifade:
ifade1
ifade2
else:
ifade3
ifade4
else anahtar sözcüğü ile if anahtar sözüğünün aynı girinti düzeyine saip olması
gerekmektedir. Aşağıdaki if deyimi de geçerlidir:
if ifade:
ifade1; ifade2
else:
ifade3
ifade4
if ifade :
ifade1; ifade2
else : ifade3; ifade4
if ifade:
ifade1
ifade2
else:
ifade3
ifade4
if ifade:
ifade1
ifade2
else:
ifade3
ifade4
Suit "aynı satır üzerine yazılmış birden fazla basit deyim ya da farklı
staırlara yazılmış olan aynı girinti düzeyine sahip
birden fazla deyim" anlamına geldiğine göre aşağıdaki if sentaksı geçersizdir:
if ifade: ifade1
ifade2
else: ifade3; ifade4
if ifade:
ifade1
ifade2
else: ifade3
if ifade:
ifade1:
ifade2:
else:
ifade3
ifade4
ifade5
Örneğin:
x = 10
if x > 0:
print('ankara')
print('izmir')
else:
print('adana')
print('eskişehir')
Burada toplam üç deyim vardır. İlk deyim atama deyimidir ve basit bir deyimdir.
İkinci deyim if deyimidir. Üçüncü deyim 'eskişehir'i ekrana yazdıran
basit deyimdir. x > 0 ifadesi zaten bool türdendir. Bu ifade True ise 'ankara'
ve 'izmir' yazıları False ise 'adana' yazısı ekrana çıkacaktır.
'eskişehir' yazısını basan basit deyim if içerisinde değildir. Çünkü bu deyim
if ile aynı girinti düzeyinde yazılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
if x % 2 == 0:
print('çift')
else:
print('tek')
print('program sonlanıyor')
#----------------------------------------------------------------------------------
--------------------------------------
Boş bir listenin, demetin ve strin'in bool türüne False olarak, dolu bir
listenin, string'in ve demetin True olarak
dönüştürüldüğünü anımsayınız. Örneğin:
a = []
if a:
ifade1
ifade2
else:
ifade
ifade4
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıda ikinci derece bir denklemin köklerini bulan program verilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
import math
a = float(input('a:'))
b = float(input('b:'))
c = float(input('c:'))
delta = b ** 2 - 4 * a * c
if delta < 0:
print('kök yok')
else:
x1 = (-b + [Link](delta)) / (2 * a)
x2 = (-b - [Link](delta)) / (2 * a)
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki genel biçimden de görüldüğü gibi if deyiminin else kısmı
olmayabilir. if deyiminin doğruysa kısmındaki
suite'ten sonra else anahtar sözcüğü gelmezse derleyici bunun "else kısmı
olmayan bir if" olduğunu kabul eder. Örneğin:
if ifade:
ifade1
ifade2
ifade3
Burada toplamda iki deyim vardır. if deyimi ve ifade3'ten oluşan basit deyim.
Burada if deyimin else kısmı bulunmamaktadır.
ifade3 if deyimin dışında olan başka bir basit deyimdir. Örneğin:
if a > 0:
print('Pozitif')
print('Program sonlanıyor')
Burada if deyiminin else kısmı bulundurulmamıştır.
#----------------------------------------------------------------------------------
--------------------------------------
if a > 0:
print('Pozitif')
print('Program sonlanıyor')
#----------------------------------------------------------------------------------
--------------------------------------
C, C++, Java ve C# gibi dillerde if anahtar sözcüğündne sonra if ifadeesinin
parantezler içerisinde olması zorunludur.
Örneğin:
if (a > 0)
ifade1;
else
ifade2;
if a > 10 b = 20;
Artık her şey çok açıktır. Python'da zaten if deyiminin kontrol ifadesinden
sonra ':' atomu gelmek zorunda olduğu için
bu ayrıştırma parantezlere gereksinim duyulmadan da yapılabilmektedir. Örneğin:
if a > b: b = 20
Swift gibi Kotlin gibi yeni bazı dillerde küme parantezleri dozunlu turulduğu
için o dillerde de kontrol ifadesinin
paranteze alınması gerekmemiştir. Örneğin:
if a > 10 {
b = 10;
}
#----------------------------------------------------------------------------------
--------------------------------------
if deyiminin doğruysa kısmında başka bir if deyimi olabilir. Örneğin:
ifade1
if ifade2:
ifade3
if ifade4:
ifade5
ifade6
else:
ifade7
ifade8
else:
ifade9
ifade10
if ifade1:
if ifade2:
ifade3
ifade4
else:
ifade5
if ifade1:
if ifade2:
ifade3
ifade4
else:
ifade5
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte üç sayının en büyüğü bulunmuştur.
#----------------------------------------------------------------------------------
--------------------------------------
a = int(input('Sayı giriniz:'))
b = int(input('Sayı giriniz:'))
c = int(input('Sayı giriniz:'))
if a > b:
if a > c:
print(a)
else:
print(c)
else:
if b > c:
print(b)
else:
print(c)
#----------------------------------------------------------------------------------
--------------------------------------
Bir grup koşuldan bir tanesi doğru iken diğerlerinin doğru olma olasılığı yoksa
bu koşullara "ayrık (discrete) koşullar"
denilmektedir. Örneğin:
a > 0
a < 0
a > 0
a < 0
a == 0
a > 0
a > 5
a == 1
a == 2
a == 3
#----------------------------------------------------------------------------------
--------------------------------------
Programalamda ayrık koşulların ayrı if'lerle ifade edilmesi kötü bir tekniktir.
Örneğin:
if a > 0:
ifade1
if a < 0:
ifade2
if a == 0:
ifade3
if a == 1:
ifade1
if a == 2:
iafde2
if a == 3:
ifade3
if a == 1:
ifade1
else:
if a == 2:
ifade2
else:
if a == 3:
ifade3
#----------------------------------------------------------------------------------
--------------------------------------
if a == 1:
print('bir')
else:
if a == 2:
print('iki')
else:
if a == 3:
print('üç')
else:
if a == 4:
print('dört')
else:
if a == 5:
print('beş')
else:
print('hiçbiri')
print('program sonlanıyor')
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki örnekte görüldüğü gibi else-if merdivenleri kaydırmalı bir biçimde
yazılması gerektiği için görüntüyü bozmaktadır.
Bu görüntünün bozulmaması ve bu tür else-if merdivenlerinin daha kolay
yazılması için Python'da if deyimin bir parçası olarak
elif kısmı da bulundurulmuştur. elif (else if'ten kısaltma) tamamen else if
anlamına gelmektedir. Ancak yazımda if ile aynı
girinti düzeyine sahip olmak zorundadır. elif bir ifadeyle birlikte
bulundurulur. elif kısımlarından sonra son bir else kısmı da
bulundurulabilmektedir. Örneğin:
if a == 1:
print('bir')
elif a == 2:
print('iki)
elif a == 3:
print('üç')
elif a == 4:
print('dört')
elif a == 5:
print('beş')
else:
print('hiçbiri')
#----------------------------------------------------------------------------------
--------------------------------------
a = int(input('Bir değer giriniz:'))
if a == 1:
print('bir')
elif a == 2:
print('iki')
elif a == 3:
print('üç')
elif a == 4:
print('dört')
elif a == 5:
print('beş')
else:
print('program sonlanıyor')
<BURADA KALDIK>
#----------------------------------------------------------------------------------
--------------------------------------
Bir program parçasının yinelemeli olarak çalıştırılmasını sağlayan deyimlere
döngü deyimleri denilmektedir. Python
döngü deyimleri bakımından minimalist tasarlanmıştır. Python'da iki döngü
deyimi vardır:
1) while Döngüleri
2) for Döngüleri
#----------------------------------------------------------------------------------
--------------------------------------
23. Ders - 27/06/2022-Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
while döngüsünün genel biçimi şöyledir:
i = 0
while i < 10:
print(i)
i += 1
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte bir listenin elemanları while döngüsü ile yazdırılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
i = 0
while i < len(a):
print(a[i])
i += 1
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte 1'den klavyeden girilen sayıya kadar olan tamsayıların
toplamı yazdırılmıştır. (Tabii aslında bu toplam
(n * (n + 1)) / 2 biçimindedir.)
#----------------------------------------------------------------------------------
--------------------------------------
i = 1
total = 0
while i <= n:
total += i
i += 1
print(total)
#----------------------------------------------------------------------------------
--------------------------------------
while ifadesi bool türünden değilse bool türüne dönüştürülmektedir. Boş bir
string'in ya da listenin bool türüne False olarak,
dolu bir string'in ya da listenin True olarak dönüştürüldüğünü anımsayınız.
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankara'
while s:
print(s)
s = s[:-1]
#----------------------------------------------------------------------------------
--------------------------------------
int ya da float bir değerin bool türüne sıfır dışı ise True olarak sıfır ise
False olarak dönüştürüldüğünü anımsayınız.
#----------------------------------------------------------------------------------
--------------------------------------
i = 10
while i:
print(i)
i -= 1
#----------------------------------------------------------------------------------
--------------------------------------
Python'da atama operatörünün değer üretmediğini bunun için dile Walrus
operatörünün eklendiğini anımsayınız. Aşağıdaki
while döngüsünde girilen sayının karesi ekrana yazdırılmaktadır. Ancak 0
girildiğinde döngü sonlandırılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
İç içe döngüler söz konusu olabilir. Yani bir suit içerisinde başka bir döngü
de olabilir.
#----------------------------------------------------------------------------------
--------------------------------------
i = 1
while i <= n:
k = 0
while k < i:
print('*', end='')
k += 1
print()
i += 1
#----------------------------------------------------------------------------------
--------------------------------------
Tabii yukarıdaki örneği aslında "yineleme (repitition)" ile çok daha kolay
aşağıdaki gibi yapabilirdik.
#----------------------------------------------------------------------------------
--------------------------------------
i = 1
while i <= n:
print('*' * i)
i += 1
#----------------------------------------------------------------------------------
--------------------------------------
Sayıyı asal çarpanlarına ayıran örnek bir kod aşağıdaki gibi olabilir.
#----------------------------------------------------------------------------------
--------------------------------------
divider = 2
while n != 1:
if n % divider == 0:
print(divider, end=' ')
n //= divider
else:
divider += 1
print()
#----------------------------------------------------------------------------------
--------------------------------------
Python'da sonsuz döngü oluşturmak için while anahtar sözcüğünün yanındaki
ifadeyi True yapabiliriz. Örneğin:
while True:
...
i = 0
while True:
print(i)
i += 1
#----------------------------------------------------------------------------------
--------------------------------------
for döngülerinin genel biçimi şöyledir:
Python'da for döngüleri C/C++, Java ve C#'taki belli bir miktar yinelemeye yol
açan tarzda for döngüleri değildir. Python'daki
for döngüleri diğer bazı dillerdeki "foreach" döngüleri gibidir. Yani Python'da
for döngüleri aslıunda dolaşılabilir nesneleri
dolaşan bir döngüdür. for döngüleri şöyle çalışır: Döngünün her yinelşenmesinde
dolaşılabilir nesnenin sıraki elemanı döngü değişkenine
atanır. Sonra suit çalıştırılır. Böylece elemanlar sırasıyla tek tek
değişkenlere atanmış olur. Dolaşılabilir nesne bittiğinde dolaşım da bitmiş olur.
Örneğin:
Tabii aslında döngü değişkenine atama bir adres atamasıdır. Yani yukarıdaki
örnekte x sırasıyla 10, 20, 30, 40, 50
nesnelerinin adreslerini tutacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
for x in a:
print(x, end=' ')
print()
i = 0
while i < len(a):
print(id(a[i]))
i += 1
print()
for x in a:
print(id(x))
#----------------------------------------------------------------------------------
--------------------------------------
Biz for döngüsü ile dolaşılabilir olan her nesneyi dolaşabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
s = 'ankara'
for c in s:
print(c)
#----------------------------------------------------------------------------------
--------------------------------------
Biz Python'da C/C++, Java ve C# gibi dillerdeki for döngüsünü range fonksiyonu
ile emüle edebiliriz. Örneğin:
for i in range(n):
...
for i in range(10):
print(i, end=' ')
#----------------------------------------------------------------------------------
--------------------------------------
Bir listeyi dolaşmak isteyelim. İki alternatif yöntem kullanılabilir. Birincisi
indeks yoluyla dolaşma olabilir. Örneğin:
for i in range(len(a)):
print(a[i], end=' ')
İkincisi liste dolaşılabilir bir nesne olduğuna göre onu doğrudan dolaşmak.
Örneğin:
for x in a:
print(x, end=' ')
#----------------------------------------------------------------------------------
--------------------------------------
for i in range(len(a)):
print(a[i], end=' ')
print()
for x in a:
print(x, end=' ')
#----------------------------------------------------------------------------------
--------------------------------------
for döngüsünde biz döngü değişkenini değiştirmekle dolaşılabilir nesneuyi
değiştirmiş olmayız. Örneğin bir listenin
elemanlarını onların kareleriyle değiştirmek isteyelim. Bunu şöyle yapamayız:
for x in a:
x = x ** 2
Çünkü biz burada x'i değiştiriyoruz, a'da bir değişiklik yapmıyoruz. Bu tür
durumlarda indeks yoluyla listeye erişmek gerekir. Örneğin:
for i in range(len(a)):
a[i] = a[i] ** 2
#----------------------------------------------------------------------------------
--------------------------------------
a = [1, 2, 3, 4, 5]
for x in a:
x = x ** 2
print(a)
for i in range(len(a)):
a[i] = a[i] ** 2
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
Tabii dolaşılabilir bir nesne de dolaşılabilir nesnelerden oluşyor olabilir.
Örneğin bir demet listesi söz konusu olabilir.
Bu durumda biz listeyi dolaştığımızda demetleri elde ederiz:
a = [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
for t in a:
print(t)
for t in a:
for k in t:
print(k, end=' ')
print()
#----------------------------------------------------------------------------------
--------------------------------------
a = [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
for t in a:
print(t)
print()
for t in a:
for k in t:
print(k, end=' ')
print()
#----------------------------------------------------------------------------------
--------------------------------------
Eğer for döngüsü ile dolaşımda dolaştıkça elde edilen nesneler de dolaşılabilir
ise biz "açım (unpacking)" for dönüsünün içerisinde
yapabiliriz. Örneğin:
a = [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
for t in a:
x, y = t
...
for x, y in a:
...
for [x, y] in a:
print(x, y)
for (x, y) in a:
print(x, y)
#----------------------------------------------------------------------------------
--------------------------------------
a = [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
for t in a:
x, y = t
print(x, y)
print()
for x, y in a:
print(x, y)
#----------------------------------------------------------------------------------
--------------------------------------
Python'da _ aslında normal bir değişken ismi olarak kullanılabilmektedir. Ancak
genel olarak programcılare _ ismini
"ben bu değişkenle ilgilenmiyorumi yalnızca bu değişkeni yer turucu olarak
kullanıyorum" anlamında kullanmaktadır. Örneğin biz
1000 kere dönen bir döngüde bir şeyler yapacak olalım. Ancak döngü değişkenini
hiç döngü içerisinde kullanmayacak olalım. Bu durumu
vurgulamak için döngü değişkeni için _ ismini kullanabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
for _ in range(1000):
pass
#----------------------------------------------------------------------------------
--------------------------------------
Sözlüklerin de dolaşılabilir olduğunu, sözlükleri dolaştıkça anahtarların elde
edildiğini anımsayınız. Tabii elimizde
bir anahtar varsa biz onun değerini de eld edebiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
for key in d:
print(key, d[key])
#----------------------------------------------------------------------------------
--------------------------------------
dict sınıfının items isimli metodunuın bize dolaşılabilir bir nesne verdiğini,
o nesne dolaşıldığında da anahtar değer
çiftlerindne oluşan demetlerin elde edildiğini anımsayınız. O zaman biz
sözlükteki anahtar değer çiftlerini şöyle de elde edebiliriz:
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
for t in [Link]():
print(t)
print()
#----------------------------------------------------------------------------------
--------------------------------------
Dolaşılabilir bir nesneyi tersten dolaşabilir miyiz? Bunun yanıtı "o nesneye
ilişkin sınıfı yazan kişi buna izin verdiyse
dolaşabiliriz" biçimindedir. Tabii str gibi, list gibi, tuple gibi nesneler
ters indekslemeyle ya da ters çeviren dilimleme
ile terten dolaşılabilirler. Bu türlere daha önceden de belirttiğimiz gibi
"sequence type" denilmektedir. Pekiyi ya
dolaşılabilir nesne bir "sequence type" değilse?
for x in a[::-1]:
print(x, end=' ')
#----------------------------------------------------------------------------------
--------------------------------------
Tersten dolaşmanın daha genel bir biçimi daha önce görmüş olduğumuz "reversed"
fonksiyonunu kullanmaktadır. Anımsanacağı gibi
reversed fonksiyonu bize tersten dolaşılabilir bir nesne vermekteydi. O zaman
tersten dolaşımı aşağıdaki gibi de yapabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
for x in reversed(a):
print(x, end=' ')
#----------------------------------------------------------------------------------
--------------------------------------
Tabii biz her dolaşılabilir nesneyi reversed fonksiyonuna sokamayız. Bir
dolaşılabilir nesnenin tersten dolaşılabilirliği o sınıfı
yazanlar tarafından sağlanmaktadır. Örneğin kümeler dolaşılabilir nesnelerdir.
Kümeler dolaşıldığında elemanların hangi sırada elde
edileceğinin bir garantisi yoktur. Ancak kümeler tersten dolaşılabilir
değildir. Daha önceden de belirtildiği gibi Python'da sonradan
s3.7 versiyonu ile birlikte özlükler dolaşılırken elemanların onların ekleme
sırasına göre dolaşılması garanti edilmiştir.
Bu eklemeden sonra artık sözlükler de reversed fonksiyonu ile tersten
dolaşılabilir hale gelmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
d[100] = 'sacit'
d[300] = 'fehmi'
for x in reversed(d):
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
24. Ders - 29-06/2022-Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
break deyimi yalnızca döngüler içerisinde kullanılabilir. Genel biçimi
şöyledir:
break
#----------------------------------------------------------------------------------
--------------------------------------
while True:
val = float(input('Bir sayı giriniz:'))
if val == 0:
break
print(val * val)
#----------------------------------------------------------------------------------
--------------------------------------
İç içe döngülerde break yalnızca kendi döngüsünden çıkmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
for i in range(10):
for k in range(10):
print(f'({i},{k})')
s = input()
if s == 'quit':
break
if s == 'quit':
break
#----------------------------------------------------------------------------------
--------------------------------------
continue deyimi break deyimine göre oldukça seyrek kullanılmaktadır. Programın
akışı continue anahtar sözcüğünü gördüğünde
döngü başa sararak yeni bir yinelemeye geçilir. break deyimi döngünün kendisini
sonlandırırken continue deyimi döngü
içerisindeki suite'i sonlandırmaktadır. Yani akış continue deyimini gördüğünde
sanki suit bitmiş de yeni bir yineleme yapılıyormuş
gibi bir etki oluşur.
Aşağıdaki örnekte çift sayılarda programın akışı continue deyimini gördüğü için
döngü başa sarmaktadır. Dolayısıyla ekrana
yalnızca çift sayılar basılacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
for i in range(10):
if i % 2 == 0:
continue
print(i)
#----------------------------------------------------------------------------------
--------------------------------------
continue deyimi genellikle döngü içerisindeki uzun if bloklarını elimine etmek
için kullanılmaktadır. Örneğin:
while True:
a = int(input()):
if a % 7 == 0:
...
...
...
...
...
while True:
a = int(input()):
if a % 7 != 0:
continue
...
...
...
...
...
Aşağıdakiş örnekte bir REPL ortamı yaratılmak istenmiştir. Programda bir prompt
eşliğinde bir komut girilmesi istenir.
Eğer komut önceden belirlenen komutlardan bir tanesi ise ilgili işlem yapılır.
Değilse ekrana "invalid command!" yazısı çıkartılır.
Eğer kişi yalnızca boşluklar girerek ENTER tuşuna basarsa bu durumda continue
ile akış başa sardırılmtır.
#----------------------------------------------------------------------------------
--------------------------------------
while True:
print('CSD>', end='')
cmd = input().strip()
if cmd == '':
continue
if cmd == 'quit':
break
if cmd == 'copy':
print('copy command executes...')
elif cmd == 'rename':
print('rename command executes...')
elif cmd == 'dir':
print('dir command executes...')
else:
print('invalid command!')
#----------------------------------------------------------------------------------
--------------------------------------
Bir sayının basamak sayısını bulmak için bir döngü içerisinde sayı sürekli 10'a
bölünebilir ve bu işlemin kaç kere yapıldığı
hesaplanabilir.
#----------------------------------------------------------------------------------
--------------------------------------
count = 0
while a:
count += 1
a //= 10
print(count)
#----------------------------------------------------------------------------------
--------------------------------------
Tabii aslında sayının basamak sayısı hiç döngü kullanmadan 10 tabanına göre
logaritmasının 1 fazlası olarak elde edilebilir.
10 tabanına göre logaritma hesabı yapan standart math modülünde log10 isimli
bir fonksiyon vardır. Bu fonksiyon sonucu bize
float olarak verir. float sayıyı noktadan kurtarmak için int dönüştürmesi
yapılabilir.
#----------------------------------------------------------------------------------
--------------------------------------
import math
#----------------------------------------------------------------------------------
--------------------------------------
pass deyimi "boş deyim" anlamına gelmektedir. Normalde if gibi while gibi for
gibi deyimlerde bir suite bulundurmak zorunludur.
Ancak bazen biz içi boş bir suit oluşturmak isteyebiliriz. İşte o zaman pass
deyimi kullanılır. Örneğin:
for i in range(10000000):
pass
Burada programcı for döngüsünün içerisinde bir şey yapmak istememiştir. C/C++,
Java ve C# gibi dillerde ';' boş deyim anlamına
gelir. Ancak Python.2da böyle bir kullanım yoktur..
#----------------------------------------------------------------------------------
--------------------------------------
for i in range(10):
print(i)
for k in range(10000000):
pass
#----------------------------------------------------------------------------------
--------------------------------------
Tabii aslında pass suite içerisinde kullanılmak zorunda değildir. Herhangi bir
yerde kullanılabilir. pass hiçbir işleme yol açmaz.
Şüphesiz herhangi bir yerde gereksiz pass kullanımı da kötü bir tekniktir.
Örneğin:
x = 10
pass
print(x)
pass
print(x * x)
#----------------------------------------------------------------------------------
--------------------------------------
pass deyimi ileride görecek olduğumuz fonksiyonlar, sınıflar gibi yapılarda da
"içi boş" olmayı belirtmek amacıylşa kullanılmaktadır. Örneğin:
def foo(): # içi boş fonksiyon
pass
#----------------------------------------------------------------------------------
--------------------------------------
25. Ders 04/07/2022 -
Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da diğer bazı dillerdeki gibi bir switch deyimi yoktu. Ancak Python'un
310 versiyonuyla birlikte dile "match"
adı altında switch benzeri bir deyim eklenmiştir. Kursun yapıldığı sırada
Python'un son versiyonu 3.10.5'tir. Dolayısıyla
aşağıda açıklanacak olan match deyiminin çalıştırılması için yprumlayıcınızın
versiyonuna dikkat ediniz. Eğer Anaconda dağıtımında
çalışıyorsanız. "Envrionment" sekmesinden yeni bir "Virtual Envirionment"
yaratıp Python'un en güncel versiyonunu yükleyebilirsiniz.
#----------------------------------------------------------------------------------
--------------------------------------
match deyiminin genel biçimi şöyledir:
match <ifade>:
case <kalıp>:
<suit>
case <kalıp>:
<suit>
case <kalıp>:
<suit>
....
match deyimi şöyle çalışmaktadır: Önce match anahtar sözcüğünün yanındaki ifade
işleme sokulur. Sonra case bölümleri sırasıyla gözden geçirilir.
match ifadesi ile case bölümlerinde kalıp uyuşursa ilk uyuçan case bölümündeki
suite çalıştırılmaktadır. Diğer bazı dillerde olduğu gibi case bölümleri
break bir deyimle sonlandırılmaz. match deyiminde zaten bir case kalıbı ile
uyuşum sağlandığında yalnızca o case bölümündeki deyimler çalıştırılmaktadır.
natch deyiminde diğer bazı dillerde olduğu gibi "aşağıya düşme (fall through)"
mekazizması yoktur. Bir case bölümü uyuşumu sağlarsa o case bölümü
çalıştırılır. Sonra match deyimi biter ve programın akışı sonraki deyim ile
devam eder.
Python'un match deyiminde acase bölmlerinde birden fazla uyuşum söz konusu
olabilir. Bu durumda yukarıdan aşağıya doğru ilk uyuşan case bölümü
çalıştırılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
match a:
case 1:
print('bir')
case 2:
print('iki')
case 3:
print('üç')
case 4:
print('dört')
case 5:
print('beş')
#----------------------------------------------------------------------------------
--------------------------------------
Diğer bazı dillerde switch deyiminin eğer hiçbir case bölümü uyuşum sağlamazsa
çalıştırılan bir "default" bölümü vardır.
Python'un match deyiminde "default" bölüm yoktur. Ancak onunla aynı anlama
gelen _ kalıbı vardır. Bu kalıp eğer yukarıdaki
kalıpların hiçbiri uyum sağlamazsa uyum sağlar. _ kalıbı case bölümlerinin
sonuna yerleştirilmek zorundadır.
#----------------------------------------------------------------------------------
--------------------------------------
match a:
case 1:
print('bir')
case 2:
print('iki')
case 3:
print('üç')
case 4:
print('dört')
case 5:
print('beş')
case _:
print('hiçbiri')
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda belirttiğimiz gibi Python'a eklenen match deyimi oldukça detaylı ve
yetenekli bir deyimdir. case bölümlerindeki
kalıplar değişikj biçimde oluşturulabilmektedir. Yukarıdaki örneklerde biz case
anahtar sözcüğünün yanına birer sabit yazdık.
(Buraya diğer dillerdeki gibi sabit ifadeleri yazamayız. Tek bir sabit yazmak
zorundayız). Bu tür kalıplara "sabit kalıpları (iteral patterns)"
denilmektedir. Sabit kalıpları tek bir sabittten oluşur. Bu sabitler string de
olabilir.
#----------------------------------------------------------------------------------
--------------------------------------
s = input('Bir şehir giriniz:')
match s:
case 'ankara':
print('06')
case 'eskişehir':
print('26')
case 'kocaeli':
print('41')
case 'adana':
print('01')
case 'izmir':
print('35')
case _:
print('hiçbiri')
#----------------------------------------------------------------------------------
--------------------------------------
Bir case bölümünde "veya" biçiminde birden fazla kalıp "|" atomu ile
oluşturulabilmektedir. Bu kalıba "veya kalıbı (or pattern)" denilmektedir.
Örneğin:
case 1 | 2 | 3:
pass
while True:
cmd = input('CSD>').strip()
if cmd =='':
continue
match cmd:
case 'copy':
print('copy executes')
case 'rename':
print('rename executes')
case 'del' | 'erase' | 'remove':
print('delete executes...')
case 'quit' | 'exit':
break
case _:
print(f'invalid command: {cmd}')
#----------------------------------------------------------------------------------
--------------------------------------
Veya kalıbında veya kalıbını parantez içerisine alarak bir "as" cümleceği
ekleyebiliriz. as cümleceğini bir değişken izler
bu değişken hangi veya kalıbı uyuşum sağladıysa onun değerini barındırır.
Örneğin:
while True:
cmd = input('CSD>').strip()
if cmd =='':
continue
match cmd:
case 'copy':
print('copy executes')
case 'rename':
print('rename executes')
case ('del' | 'erase' | 'remove') as as_cmd:
print(f'{as_cmd} executes...')
case 'quit' | 'exit':
break
case _:
print(f'invalid command: {cmd}')
#----------------------------------------------------------------------------------
--------------------------------------
case bölümlerinde kullanılabilen diğer bir önemli kalıp da "dizilim (sequence)"
kalıbıdır. eğer match anahtar sözcüğünün
yanındaki ifade bir "dizilim türünden (sequence type)" ise (burada string
dizilim türünden kabul edilmemektedir) bu durumda case bölümleri
dizilim kalıbına sahip olabilir. PEP 634'te dizilim türlerinin neler olduğu
açıklanmıştır. Ancak biz kursta geldiğimiz noktaya kadar
yalnızca dizilim türü için "list" ve "tuple" kullanabiliriz. O halde özetle
birizm bir dizilim kalıbını
kullanabilmemiz için match yanındkai ifadenin bir liste ya da demet olması
gerekir.
#----------------------------------------------------------------------------------
--------------------------------------
while True:
cmd = input('CSD>').split()
if len(cmd) == 0:
continue
match cmd:
case 'copy', :
print('copy executes')
case ['rename']:
print('rename executes')
case (['del'] | ['erase'] | ['remove']) as as_cmd:
print(f'{as_cmd[0]} executes...')
case ['quit'] | ['exit']:
break
case _:
print(f'invalid command: {cmd[0]}')
#----------------------------------------------------------------------------------
--------------------------------------
Dizilim kalıbında dizilimin elemanları yerine değişken isimleri konulabilir. Bu
duurmda uyuşum her zaman sağlanır ve
dizilimin ilgili elemanı o değişkene atanır. Örneğin:
['del', '[Link]']
('del', '[Link]')
['del', 123]
...
Burada biz hem del eşleşmesini sağlayıyoruz hem de del komutunun yanındaki
yazıyı elde etmiş oluyoruz. Tabii yukarıdaki kalıp aşağıdaki gibi
bir dizilimle uyuşmaz:
Çünkü kalıptaki path tek bir eleman anlamına gelmektedir. Yukarıdaki kalıp
aşağıdakiyle de eşleşmez:
['del']
#----------------------------------------------------------------------------------
--------------------------------------
while True:
cmd = input('CSD>').split()
if len(cmd) == 0:
continue
match cmd:
case 'copy', source_path, dest_path:
print(f'copy {source_path} {dest_path} executes')
case 'rename', source_path, dest_path:
print(f'rename {source_path} {dest_path} executes')
case (['del', path] | ['erase', path] | ['remove', path]) as as_cmd:
print(f'{as_cmd[0]} {path} executes')
case ['quit'] | ['exit']:
break
case _:
print(f'invalid command: {cmd[0]}')
#----------------------------------------------------------------------------------
--------------------------------------
26. Ders 04/07/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Dizilim kalıbında *'lı eleman yalnızca bir tane olmak koşuluyla kullanılabilir.
Örneğin:
match a:
case 10, 20, *others:
print(others) # [30, 40, 50]
#....
match a:
case 10, *others, 50:
print(others) # [20, 30, 40]
match a:
case 10, *others, 40:
print(others)
match a:
case 10, *others, 40, 50:
print(others) # [20, 30]
match a:
case 10, x, y, 40, 50:
print(x, y) # 20 30
match a:
case 10, x, y, 40, 50:
print(x, y) # 20 30
match a:
case 10, _, _, 40, 50:
print('matched') # Artık _ değişkenini burada kullanamayız, özel
anlamı var
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
case yanındaki ifade bir değişken olursa buna "capture pattern" denilmektedir.
Bu durumda bu değişkenin içerisindeki değere bakılmaz.
Uyuşumun her zaman sağlandığı kabul edilir ve match ifadesi bu değişkene
atanır. Tabii "capture patten" her şeye uyum sağladığı için case
bölümlerinin sonuna yerleştirilmelidir. Eğer case bölümlerinin sonuna
yerleştirilmezse "exception" oluşur.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sözlük case ifadelerindeki sözlüğün doğrudan küme parantezleriyle yazılması
gerekir. (Aksi halde "capture pattern" olarak ele alınır.)
Bu durumda case bölümüne yazılan sözlük ifadesinde anahtar-değer çiftleri
uyuşursa case uyuşumu sağlanmış kabul edilir. Dizilim kalıbına
benzemeyen biçimde sözlük kalıbında match ifadesindeki sözlüğün her elemanının
case bölümündeki sözlükle uyuşması gerekmemektedir. Önemli olan
case bölümündeki sözlüğün yazılan elemanlarının uyuşumudur. Uyuşum hem anahtar
hem değer ile yapılır.
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
match d:
case {'ali': 10, 'veli': 60}:
print('match etmeyecek')
case {'ayşe': 40, 'selami': 30}:
print('match edecek') # match edecek
#----------------------------------------------------------------------------------
--------------------------------------
Sözlük kalıbında değerde _ kullanılırsa değerin her zaman uyuştuğu kabul
edilir. Örneğin:
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
match d:
case {'ali': 10, 'veli': _}:
print('match edecek') # match edecek
#----------------------------------------------------------------------------------
--------------------------------------
Aslında sözlüğün değer kısmında bir değişken ismi de getirilebilir. Bu durumda
anahtar uygunsa değer her zaman uyuşma sağlar ve değer de
aynı zamanda buradaki değişkene yerleştirilir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
match d:
case {'ali': 10, 'veli': x}:
print(x) # 20
#----------------------------------------------------------------------------------
--------------------------------------
Sözlük kalıbında eleman olarak **'lı bir değişken kullanılabilir. Bu **'lı
değişkene geri kalan tüm sözlük elemanlarına
uyuşum sağlayan bir sözlük yerleştirilir. Ancak **'lı değişken sözlüğün sonunda
bulunmak zorundadır.
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
match d:
case {'ali': 10, 'veli': 20, **others}:
print(others) # {'selami': 30, 'ayşe': 40, 'fatma': 50}
#----------------------------------------------------------------------------------
--------------------------------------
Aslında match deyiminde "sınıf kalıbı (class paterrn)" da kullanılabilmektedir.
Ancak henüz sınıf konusunu ele almadığımızdan dolayı
bu kalıp üzerinde burada durmayacağız. Aşağıda basit bir örnek verilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self, a, b):
self.a = a
self.b = b
s = Sample(10, 20)
match s:
case Sample(a = 10, b = 20):
print('match edecek') # match edecek
#----------------------------------------------------------------------------------
--------------------------------------
27. Ders 18/07/2022-Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
match deyiminde case bölümlerinde "koruma (gurad)" da oluşturulabilmektedir.
case bölümlerinde if anahtar söcüğüyle
ayrı bir koşul belirtilebilir. Bu durumda bu case bölümünün uyuşum sağlaması
için o if koşulunun da sağlanması gerekir. Örneğin:
match a:
case 10 if x > 0:
pass
Burada case bölümünün uyuşum sağlaması için a'nın 10 olmasının yanı sıra aynı
zamanda x'in de 0'dan büyük olması
gerekmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
match a:
case 10 if x > 0:
print('Ok')
case _:
print('cannot match...')
#----------------------------------------------------------------------------------
--------------------------------------
case bölümündeki koruma kısmı bazı durumlarda pratik kullanımlara yol
açabilmektedir. Örneğin uyuşum dışında ek başka
koşulların kontrolü bu sayede yapılabilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
match cmds:
case ['delete', *args] if len(cmds) == 2:
print('delete command')
case _:
print('cannot match...')
#----------------------------------------------------------------------------------
--------------------------------------
Koşul operatörü (conditional operator) üç operandlı bir operatördür. Operatör
if deyimine benzemekle birlikte bir değer üretir.
Bu bağlamda ifadelerin içerisinde kullanılabilir. C, C++, Java ve C# gibi
dillerde bu operatörün bzeri ?: biçiminde bulunmaktadır.
Koşul operatörü -ismi üzerinde- bir deyim değildir. Bir operatördür. Yani bir
değer üretmektedir. Genel biçimi şöyledir:
print(b)
#----------------------------------------------------------------------------------
--------------------------------------
Şüphesiz koşul operatör ile yapılan şeyler aslında if deyimiyle de yapılabilir.
Ancak koşul operatörü bazı durumlarda
okunabilirliği artırmakta ve pratik bazı kullanımlara olanak sağlamaktadır.
Yukarıdaki örnekte şöyle yapılmıştır:
if a % 2 == 0:
b = 100
else:
b = 200
#----------------------------------------------------------------------------------
--------------------------------------
Koşul operatörünü if deyimi gibi kullanmak kötü bir tekniktir. Örneğin:
1) Bir karşılaştıma sonıcında elde edilen değerin bir değişkene atandığı durum.
Örneğin:
if a % 2 == 0:
print('çift')
else:
print('tek')
if a %+ 2 == 0:
return 100
else:
return 200
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte 0'dan 100'e kadar sayılar beşer beşer aralarına boşluk
bırakılarak yazdırılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
for i in range(100):
print(i, end='\n' if i % 5 == 4 else ' ')
#----------------------------------------------------------------------------------
--------------------------------------
Koşul operatörü düşük öncelikli bir operatördür. Eğer ifade içerisindeki belli
bir kısmın koşulu operatörünün operandı
olmaktan çıkartılması isteniyorsa parantezler kullanılmalıdır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Örneğin iki değerden büyük olanınıo bir değişkene atamak istediğimizde koşul
operatörünü tercih edebiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Tabii yukarıdaki örnekte aslında biz büyük olan değeri doğrudan da print ile
yazdırabilirdik.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Koşul operatörü de iç içe kullanılabilir. İç içe kullanımda parantez kullanmak
zorunlu değildir. Ancak parantezsiz
ifadenin okunurluğu düşük olmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Her ne kadar iç içe kullanımda parantez gerekmiyorsa da okunabilirliği artırmak
için parantezler kullanılmalıdırç
#----------------------------------------------------------------------------------
--------------------------------------
a = int(input('Bir değer giriniz:'))
b = int(input('Bir değer giriniz:'))
c = int(input('Bir değer giriniz:'))
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfta şöyle bir soruldu: Aşağıdaki kodda neden ben matrisin tek bir elemanını
değiştirdiğim halde matrisin tüm
satırlarının elemanı değişmiş oluyor?
a = [[0] * 5]
b = a * 5
print(b)
b[0][0] = 100
print(b)
b = []
for _ in range(5):
a = [0] * 5
[Link](a)
print(b)
b[0][0] = 10
print(b)
print(b)
b[0][0] = 100
print(b)
#----------------------------------------------------------------------------------
--------------------------------------
Şimdiye kadar biz var olan fonksiyonları çağırdık. Şimdi kendimiz fonksiyonlar
yazacağız. Bir fonksiyonun yazılmasına
"fonksiyonun tanımlanması (function definition)" da denilmektedir. Python'da
fonksiyon tanımlamanın genel biçimi şöyledir:
def foo():
print('foo')
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
print('foo')
foo()
bar()
#----------------------------------------------------------------------------------
--------------------------------------
Aslında Python'da fonksiyon tanımlama işlemi bir deyim statüsündedir.
Yorumlayıcı bir fonksiyonun tanımlanadığını gördüğünde
önce bir fonksiyon nesnesi yaratır. Sonra o fonksiyon nesnesinin içerisine
fonksiyonun kodlarını (yani suit deyimlerini)
yerleştirir. Fonksiyon nesnesinin adresini de fonksiyon ismine atar. Böylece
aslında fonksiyon isimleri sıradan bir değişkendir.
Fonksiyon isimleri fonksiyon nesnesini göstermektedir. Bir fonksiyonu (...)
operatörü ile çağırdığımızda aslında biz
bu operatörün operandı olan değişkenin içerisindeki adreste bulunan fonksiyonun
nesnesinin içerisindeki kodları çalıştırmış oluruz.
Örneğin:
def foo():
print('foo')
foo()
#----------------------------------------------------------------------------------
--------------------------------------
[Link] 20/7/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir fonksiyon çağrıldığında onu çağıran fonksiyona iletilen değere "geri dönüş
değeri (return value)" denilmektedir.
Fonksiyonların geri dönüş değerleri return deyimiyle oluşturulmaktadır. return
deyiminin genel biçimi şöyledir:
return [ifade]
Programın akıuşı return deyimini gördüğünde fonksiyon sonlanır ve geri dönüş
değeri return anahtar sözcüğünün yanındaki ifade
olacak biçimde oluşturulur.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
print('foo')
return 100
a = foo()
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonun geri dönüş değeri kullanılmak zorunda değildir. Yani isteğe bağlı
(optional) bir bilgidir. Biz fonksiyonu
çağırıp geri dönüş değerini hiç kullanmayabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
print('foo')
return 100
foo()
#----------------------------------------------------------------------------------
--------------------------------------
C, C++, Java ve C# gibi statik tür sistemine sahip dillerde fonksiyonların geri
dönüş değerlerinin türleri vardır.
Fonksiyonlar tek birle geri dönebilirler. Ancak Python dinamik tür sistemine
sahip bir programlama dili olduğu için
Python'da bir fonksiyon duruma göre farklı türlerle geri dönebilir. Python'da
fonksiyonun geri dönüş değerinin türü diye bir
kavram yoktur.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
a = int(input('Bir değer giriniz:'))
if a > 0:
return a * a
return 'error'
result = foo()
print(result, type(result))
#----------------------------------------------------------------------------------
--------------------------------------
Akış retun deyimini görmeden fonksiyonu bitirirse geri dönüiş değeri olarak
None değeri elde edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
print('foo')
result = foo()
print(result) # None
#----------------------------------------------------------------------------------
--------------------------------------
return anahtar sözcüğünün yanına bir ifade yazılmayabilir. Bu durumda
fonksiyonun sonlandırılması istenmiştir ancak bir geri
dönüş değeri belirtilmemiştir. Bu durumda da geri dönüş değeri olarak None elde
edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
print('foo')
return
result = foo()
print(result) # None
#----------------------------------------------------------------------------------
--------------------------------------
Bir fonksiyonun tek bir geri dönüş değeri vardır. Yani fonksiyon tek bir
değerle geri dönebilir. Eğer fonksiyonun birden fazla
değer iletmesi isteniyorsa geri dönüş değeri demet, liste gibi bileşik bir
nesne yapılmalıdır. Demetler bu bağlamda tercih edilmelidir.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
print('foo')
return 100, 200
a, b = foo()
print(a, b)
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonların parametreleri söz konusu olabilir. Parametrelerin isimler
parametre parantezinin içerisinde belirtilmektedir.
Diğer statik tür sistemine sahip programlama dillerinde olduğu gibi bir
parametre bildirimi Python'da yoktur. Parametre parantezinin
içerisine yalnızca parametre değişkenlerinin isimleri yazılır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte argümanlara ilişkin nesnenin adresinin aslında parametre
değişkenine yerleştirildiğini göreceksiniz.
#----------------------------------------------------------------------------------
--------------------------------------
def foo(a):
print(a, id(a))
x = 10
print(id(x))
foo(x)
#----------------------------------------------------------------------------------
--------------------------------------
Tabii aynı durum return işlemi için de geçerlidir. Yan,i return işleminde de
aslında çağıran fonksiyona iletilen return ifadesindeki
nesnenin adresidir.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
a = 10
print(id(a))
return a
result = foo()
print(id(result))
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonun parametre değişkenleri yalnızca o fonksiyonun içerisinde
kullanılabilir. Dışarıdaki aynı isimli başka bir
değişken ile karışmaz. Örneğin:
def foo(a):
pass
a = 10
foo(a)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonların parametre değişkenleri onlar hangi tüden argümanla çağrılmışsa o
türden olurlar. Yani onların belli bir
türleri yoktur.
#----------------------------------------------------------------------------------
--------------------------------------
def foo(a):
print(a, type(a))
foo(10)
foo('ankara')
foo(1.2)
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi biz neden fonksiyon yazmak isteriz? İşte fonksiyonlar oluşturmanın
gerekçeleri şunlardır:
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonlar listelerin, demetlerin sözlüklkerin elemanları olabilir.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
print('foo')
def bar():
print('bar')
def tar():
print('tar')
for i in range(len(a)):
a[i]()
for f in a:
f()
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyon nesneleri hash'lenebilir olmadığı için sözlüklerde anahtar yapılamaz.
Ancak fonksiyonlar sözlüklerde
değer biçiminde bulunabilir.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
print('foo')
def bar():
print('bar')
def tar():
print('tar')
d['ali']()
d['veli']()
d['selami']()
#----------------------------------------------------------------------------------
--------------------------------------
Tabii aşağıdaki iki listede yapılan şeyler tamamen farklıdır:
def foo():
print('foo')
return 10
def bar():
print('bar')
return 20
def tar():
print('tar')
return 30
for x in a:
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte ikinci derece bir denklemin köklerini buluna bir fonksiyon
yazılmıştır. Fonksiyon eğer kök yokse None
değerine geri döner, kök varsa x1 ve x2 köklerini bir demet olarak geri
döndürmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
import math
x1 = (-b + [Link](delta)) / (2 * a)
x2 = (-b - [Link](delta)) / (2 * a)
return x1, x2
a = float(input('a:'))
b = float(input('b:'))
c = float(input('c:'))
result = get_roots(a, b, c)
if result != None:
x1, x2 = result
print(f'x1 = {x1}, x2 = {x2}')
else:
print('Kök yok!')
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdkai örnekte banner fonksiyonu tireler arasında parametresiyle aldığı
yazıyı ekrana basmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
def banner(s):
print('-' * len(s))
print(s)
print('-' * len(s))
banner('ankara')
banner('izmir')
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte bir sayının asal olup olmadığını test eden isprime isimli bir
fonksiyon yazılmıştır. Sonra bu fonksiyon
çağrılarak 100'e kadar tüm asal sayılar yazdırılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
def isprime(a):
for i in range(2, a):
if a % i == 0:
return False
return True
#----------------------------------------------------------------------------------
--------------------------------------
Aslında asallık testi daha hızlı bir biçimde aşağıdaki gibi yapılabilir.
Aşağıdaki programda iki önemli matematiksel
özellikten faydalanılmıştır:
1) Sayı çift ise ama 2'ye eşit değilse asal değildir. Başka çift sayıların
kontrol edilmesine gerek yoktur.
2) Asal olmayan her sayının kareköküne kadar bir asal çarpanı vardır. (Öklit
teoremi). Dolayısıyla sayının kareköküne
kadar kontrol yapılması yueterlidir.
#----------------------------------------------------------------------------------
--------------------------------------
import math
def isprime(a):
if a % 2 == 0:
return a == 2
return True
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıda parametresiyle girilen değerin faktöriyeline geri dönen fonksiyon
örneği verilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
def factorial(n):
total = 1
for i in range(2, n + 1):
total *= i
return total
result = factorial(5)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Aslında math modülü içerisinde factorial fonksiyonu hazır olarak bulunmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
>>> import math
>>> [Link](5)
120
#----------------------------------------------------------------------------------
--------------------------------------
29. Ders 25/07/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da diğer bazı dillerde olduğu gibi default argüman kullanımı vardır.
Yani parametre değişkenleri Python'da default
değer alabilmektedir. Bir parametre değişkeni default değer almışsa çağrı
sırasında o parametre değişkeni için argüman girilmezse
sanki o parametre için o default değerilmiş gibi işlem yapılır. Örneğin:
Biz burada foo fonksiyonunu çağırırken c için default değer girmezsek sanki 10
değerini girmiş gibi oluruz. Örneğin:
foo(100, 200)
Ancak default değer almış olan parametre için argüman girilirse artık o default
değerin bir önemi kalmaz. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Tabii fonksiyonun birden fazla parametresi default değer alabilir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Parametrelere verilen default değerlerin çok kullanılan değerler olması
gerekir. Yoksa öylesine değerleri default değer olarak
vermek kötü bir tekniktir. Fonksiyonu inceleyen kişiler default değerlerin
yaygın değerler olduğunu varsaymaktadır.
Örneğin farklı türden bir değeir int türüne dönüştürmek için kullandığımız int
fonksiyonun aslında ikinci bir parametresi vardır.
Bu parametre birinci parametredeki yazının kaçlık sistemde yazılmış olduğunu
belirten base parametresidir. Biz günllük yaşamımızda
10'luk sistemi kullandığımıza göre bu parametrenin default değerinin 10 olması
oldukça makuldür. Böylece kişiler fonksiyonu
çağırırken boşuna bu ikinci parametre için her defasında 10 değrini girmezler.
Default argümanlar bu örnekte olduğu gibi
fonksiyonu çağıracak kişilere kolaylık sağlamaktadır. Hatta bazen bir
fonksiyonun çok parametresi olabilir. Ancak bunların
önemli bir bölümü default değer almış olabilir. Meşgul bir programcı da
yalnızca default değer almamış parametrelrin anlamlarını
öğrenip onların için argüman girebilir.
#----------------------------------------------------------------------------------
--------------------------------------
s = '123'
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonda default değer alan parametre değişkenlerinin parametre listesinin
sonunda toplanmış olması gerekir. Başka bir
deyişle eğer bir parametre değişkeni default değer almışsa onun sağındakilerin
hepsinin default değer almış olması gerekir.
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
banner('ankara')
banner('ankara', '*')
#----------------------------------------------------------------------------------
--------------------------------------
Python'da fonksiyon parametrelerine verilen default değerler fonksiyonun bir
deyim olarak çalıştırılması sırasında yalnızca bir kez
işleme sokulmaktadır. Bu davranış C++', Java ve C# gibi dillerdeki davranıştan
farklıdır. Örneğin:
Burada [1, 2, 3] elemanlarına sahip olan liste nesnesi yalnızca bir kez
yaratılacaktır. Yani fonksiyon her çağrıldığında yaratılmayacaktır.
Bunu basit bir biçimde şöyle test edebilirsiniz:
foo()
foo()
foo()
def foo():
print('foo')
return 10
bar()
bar()
bar()
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da C++, Java ve C# gibi dillerdeki "function/method overloading"
özelliği yoktur. Dolayısıyla bu tür bir etki
default argüman alan parametre değişkenleriyle sağlanmaktadır. Örneğin biz
range fonksiyonunu (aslında bu bir sınıftır)
tek parametreyle, iki parametreyle ve üç parametreyle kullanabiliyorduk.
Aslında üç range fonksiyonu yoktur. Tek bir range
fonksiyonu vardır. range fonksiyonu aşağıdaki gibi bir mantıkla yazılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
disp_range(10)
disp_range(10, 20)
disp_range(10, 20, 2)
#----------------------------------------------------------------------------------
--------------------------------------
Python gibi dinamik tür sistemine sahip programlama dillerinde fonksiyonların
parametre değişkenlerinin belli bir türü
olmadığı için fonksiyonlar yanlış türden argümanlarla çağrılırsa programın
çalışma zamanı sırasında exception nedeniyle program
çökebilir. Örneğin yukarıdaki banner fonksiyonunun normal olarak bir string ile
çağrılması gerekir. Ancak biz bu fonksiyonu
bir string ile çağırmazsak programımız exception ile çökecektir. İşte
Python.2da fonksiyon çağrılarında argümanların uygun bir biçimde
girilmesi programcının sorumlulupundadır.
#----------------------------------------------------------------------------------
--------------------------------------
banner('ankara')
banner(10) # burada exception oluşacak, çünkü len fonksiyonu int türüne
uygulanamaz!
#----------------------------------------------------------------------------------
--------------------------------------
Bir fonksiyon yazarken programcı yine de fonksiyon parametreleri üzerinde
programın çalışma zamanı sırasında tür kontrolü
yapabilir. Bunun için isinstance isimli built-in fonksiyon kullanılmaktadır.
isinstance fonksiyonunun birinci parametresi
bir ifade, ikinci parametresi ise bir tür ismi olarak girilir. Fonksiyon
birinci parametresiyle belirtilen ifadenin ikinci
parametresiyle belirtilen türden (ya da o türden türemiş bir sınıf türünden)
olup olmadığına ilişkin bool bir değer geri
döndürmektedir. Örneğin:
if isinstance(s, str):
pass
banner('ankara')
banner(10)
banner(12.4)
#----------------------------------------------------------------------------------
--------------------------------------
Bir fonksiyon çağrılırken arümanlara isim verilerek o argümanların hangi
parametreler için girildiği belirtilebilmektedir.
Bu tür argümanlara Python refernas kitabında "keyword arguments" denilmektedir.
Ancak biz kurusumuzda bunlara "isimli argümanlar"
diyeceğiz. İsimli argümanlar "parametre_ismi=ifade" sentaksıyla
kullanılmaktadır. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Pozisyonel argümanlarla isimli argümanları fonksiyon çağrısında bir arada
kullanabiliriz. Ancak çağrıda isimli argümanların
sonda toplaşmış olması gerekir. Başka bir deyişle bir argüman isimli olarak
girilmişse onun sağındakilerin hepsinin isimli olarak
girilmiş olması gerekmektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Bir fonksiyon çağrısında default argümanlar da dekkate alınarak tüm parametre
değişkenlerine bir ve yalnızca bir kez
değer verilmiş olmalıdır. Eğer bir parametre değişkenine hiç değer verilmemişse
ya da birden fazla kez değer verilmişse bu durum
error ile sonuçlanır. Örneğin:
Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi isimli argümanlara neden gereksinim duyulmaktadır. Aslında isimli
argümanlara iki nedenden dolayı gereksinim duyulmaktadır:
Eğer isimli argümanlar olmasaydı biz f'ye kadarki parametre değişkenleri için o
default değerleri argüman olarak belirtmek
zorunda kalırdık. Örneğin:
Burada aslında isimli argüman kullanmaya gerek yoktur. Ancak programcı buradaki
16'nın taban belirttiğini vurgulamak için
isimli argüman kullanımını tercih etmiş olabilir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Biz kursumuzda atama işleminde '=' atomunun iki tarafında birer boşluk
karakteri bırakıyoruz. Ancak Python programcıları
genel olarak (ancak hepsi değil) isimli argüman belirtirken '=' atomunun iki
yanına boşluk karakteri girmemektedir. Biz de
kursumuzda bu yazım biçimini tercih edeceğiz. Default argümanlarda bazı
programcılar boşluk bırakırken bazıları bırakmamaktadır.
Biz kursumuzda default argümanlarda '=' atomunun iki tarafına boşluk
bırakacağız.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonun parametre listesinde parametre değişkeni yerine '*' kullanılırsa
'*'ın sağındaki tüm parametreler çağrım sırasında
isimli argümanlarla çağrılmak zorundadır. Böylece fonksiyonu yazan kişi
okunabilirliği artırmak amacıyla çağıran kişiyi isimli argüman
kullanmaya zorlayabilmektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonun parametre listesinde bir parametre yalnızca '/' biçiminde
girilmişse bu durum "onun solundaki tüm parametrelerin
pozisyonel girilmesi gerektiği" anlamına gelmektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
* ile / parametre belirleyicileri aynı anda kullanılabilir. Ancak bu durumda
önce / sonra * parametre belirleyicilerinin
gelmesi gerekir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
def foo(a, b, /, c, *, d, e):
print(a, b, c, d, e)
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonların *'lı parametreleri olabilir. Fonksiyonun parametre listesinde
yalnızca bir parametre değişkeni önüne * getirilerek belirtilebilir. Örneğin:
Bunlara *'lı parametre diyeceğiz. *'lı parametreler en fazla bir tane olabilir.
Örneğin:
foo(10, 20, 30, 40, 50, 60) # a = 10, b = 20, c = (30, 40, 50, 60)
foo(10, 20, 30) # a = 10, b = 20, c = (30,)
foo(10, 20) # a = 10, b = 20, c = ()
#----------------------------------------------------------------------------------
--------------------------------------
30. Ders 27/07/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
*'lı parametre parametre listesinin sonunda olmak zorunda değildir. Ancak her
parametre değişkeninin bir ve yalnızca
bir kez değer almış olması gerekir. Örneğin:
Örneğin:
def foo(a, *b, c = 100):
pass
foo(10, 20, 30, c=100, d=200) # geçerli, a = 10, b = (20, 30), c = 100, d
= 200
#----------------------------------------------------------------------------------
--------------------------------------
*'lı parametrenin her zaman demet belirttiğine dikkat ediniz. Demetler
dolaşılabilir nesneler olduğuna göre biz
*'lı parametreyi for döngüsi içerisinde dolaşılbiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
def add(*args):
total = 0
for x in args:
total += x
return total
result = add()
print(result) # 0
result = add(10)
print(result) # 10
#----------------------------------------------------------------------------------
--------------------------------------
Aslında print fonksiyonu da *'lı parametre almaktadır. Biz de print
fonksiyonunu myprint ismiyle yazabiliriz. Tabii
burada ekrana yazdırma için yine orijinal print fonksiyonunu kullanacağız.
Aşağıdaki örnek yalnızca print fonksiyonunun nasıl
yazılmış olabileceğini göstermek amacıyla verilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki myprint fonksiyonunu for döngüsüyle de yapabilirdik. Ancak for
döngüsü bu tür durumlarda daha zahmetli
bir çözüm sunabilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'un standart kütüphanesindeki min ve max isimli built-in fonksiyonlar bir
dolaşılabilir nesneyi alıp onun en küçük
ve en büyük elemanlarına geri dönmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
result = max(a)
print(result) # 76
result = min(a)
print(result) # 21
#----------------------------------------------------------------------------------
--------------------------------------
Aslında min ve max fonksiyonlarına birden fazla argüman girilebilmektedir. Bu
durumda bu argümanların en büyük ve
en küçük değerleri elde edilir.
#----------------------------------------------------------------------------------
--------------------------------------
def mymax(*args):
iterable = args[0] if len(args) == 1 else args
maxval = None
for x in iterable:
if maxval == None or x > maxval:
maxval = x
return maxval
a = [1, 6, 3, 2, 5]
result = mymax(a)
print(result) # 6
def mymin(*args):
iterable = args[0] if len(args) == 1 else args
minval = None
for x in iterable:
if minval == None or x < minval:
minval = x
return minval
a = [1, 6, 3, 2, 5]
result = mymin(a)
print(result) # 1
#----------------------------------------------------------------------------------
--------------------------------------
31. Ders 01/08/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonların *'lı parametrelerşnşn yanı sıra bir de **'lı parametreleri
bulunabilmektedir. Bir fonksiyonun **'lı parametresi
eğer bulundurulacaksa parametre listesinin sonunda bulundurulmak zorundadır.
(Halbuki *'li parametre parametres listesinin herhangi bir yerinde
bulundurulabilmektedir.) Fonksiyonun tek bir **'lı parametresi olabilir.
Geleneksel olarak pek çok programcı fonksiyonun *2lı parametresine
args ismini **'lı parametresine ise kwargs ya da kargs ismini vermektedir.
foo(10, 20, 30, 40, xx='ali', yy=100) # a = 10, args = (20, 30, 40), kwargs =
{'xx': 'ali', 'yy': 100}
foo(10, 20, 30, 40) # a = 10, args = (20, 30, 40), kwargs =
{}
foo(a=10) # a = 10, args = (), kwargs = {}
foo(10, args=(100, 200)) # a = 10, args = (), kwargs = {'args':
(100, 200)}
foo(10, 20, 30, kwargs={}) # a = 10, args = (20, 30), kwargs =
{'kwargs': {}}
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi fonksiyonların **'lı parametreleri neden kullanılmaktadır? Bunun iki
nedeni vardır. Birincisi bir fonksiyon çok fazla
parametreye sahipse parametre sayısını azaltmak için **'lı parametre
kullanılabilmektedir. Örneğin bir fonksiyonun 50 tane parametresi
söz konusu olsun. Bu 50 parametrenin parametre listesinde belirtilmesi
okunabilirliği bozar. Üstelik bu parametreler başa fonksiyonlarda da
söz konusu ise hem programcının bunları yazması zorlaşır hem de okunabilirlik
düşer. İşte bu durumda **'lı parametreler kolaylık sağlamaktadır.
Şimdi foo fonksiyonun a, b, c, d, e, f, g, h, i, j, k, l, m parametrelerinin
olduğunu düşünelim. Bu parametrelerin ilk ikisi
dışındakiler default değer alıyor olsun. Foksiyonu aşağıdaki gibi tanımlamış
olalım:
print('Ok')
foo(10, 20, c=10, f=20, i=30, k=40) # c = 10, d = 200, e = 300, f = 20,
g = 500, h = 600, i = 30, j = 800, k = 40, l = 1000, m = 1100
foo(10, 20) # c = 100, d = 200, e = 300, f =
400, g = 500, h = 600, i = 700, j = 800, k = 900, l = 1000, m = 1100
Tabii yukarıdaki örnekte ilk döngü ile ikinci döngü de duruma göre aşağıdaki
gibi birleştirilebilir:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi biz bir fonksiyonda ** parametresi gördüğümüzde ne düşünmeliyiz? İşet
bizim şu çıkarımı yapmamız gerekir: "Bu fonksiyon
pek parametreye sahip olabilir. Bu parametreleri fonksiyonu yazan tek tek
belirtmek istememiş. Ben geçerli parametreleri
fonksiyonu çağırırken isim=değer biçiminde isimli argüman olarak geçebilirim.
Benim geçebileceğim parametrelerin neler
olduğunu anlamak için dokümantasyona bakmalıyım".
import math
import [Link] as plt
xpoints = []
ypoints = []
x = -6.
while x <= 6:
[Link](x)
[Link]([Link](x))
x += 0.01
print(xpoints)
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonun *'lı parametresinden sonraki parametreler eğer default değer
almamışsa zaten isimli argüman biçiminde kullanılabileceğine göre
şöyle bir ince kural dile eklenmiştir: Bir parametre değişkeni default değer
almışsa *'lı parametreye kadar onun sağındakilerin hepsinin
default değer alması gerekir. *'lı parametreden sonraki parametreler karşışık
sırada default değer alan ve almayan biçiminde bulunabilir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
**'lı parametre "forwarding (iletme)" amacıyla da kullanılabilmektedir. Buna
ilişkin örnek sonraki izleyen örneklerde verilecektir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiytonları çağırırkane argümanların önüne '*' atomu getirilebilir. Bu tür
argümanlara *'lı argümanlar diyeceğiz.
*'lı argümanlar argüman listesinde birden fazla bulunabilirler. Örneğin:
Örneğin:
foo(*'ali')
foo(*'ali') # geçerli
foo(*'ankara') # error! fonksiyondaki parametreler yetersiz!
Ancak örneğin:
def foo(*args):
pass
foo('ankara')
#----------------------------------------------------------------------------------
--------------------------------------
for i in range(len(a)):
if i != 0:
print(end=', ')
print(a[i], end='')
print()
Aslında *'lı argüman sayesinde bu işlem tek bir print ile yapılabilirdi:
#----------------------------------------------------------------------------------
--------------------------------------
*'lı argüman sarma fonksiyon yazarken "iletme (forwarding)" amaçlı da
kullanılabilmektedir. Örneğin biz myprint isimli
fonksiyon yazmak isteyelim. Ancak fonksiyonumuz aldığı parametreleri print
fonksiyonuna yollasın (forward etsin):
def myprint(*args):
print(*args)
#----------------------------------------------------------------------------------
--------------------------------------
Sözlüklerin de dolaşılabilir nesneler olduğunu unutmayınız.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
range nesnesi de dolaşılabilir bir nesnedir.
#----------------------------------------------------------------------------------
--------------------------------------
print(*range(100))
#----------------------------------------------------------------------------------
--------------------------------------
Fonksiyonların **'lı argümanları da olabilmektedir. Bu argümanlar argüman
listesinde birden fazla kez bulunabilirler.
**'lı argümanlar bir sözlük nesnesi olmak zorundadır. Bu argümanlara ilişkin
sözlük nesnelerinin anahtarlarının str tründen olması
gerekir. Ancak değerleri herhangi bir türden olabilir.
foo(**d)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
**'lı argümanlar sarma fonksiyonlarda "iletme (forwarding)" işlemlerinde
kullanılabilmektedir. Örneğin:
import math
import [Link] as plt
xpoints = []
ypoints = []
x = -6.
while x <= 6:
[Link](x)
[Link]([Link](x))
x += 0.01
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
32. Ders 03/08/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir listenin ya da demetin elemanı da *'lı bir dolaşılabilir nesne olabilir. Bu
durumda bu dolaşılabilir nesne dolaşılır.
Sanki onların değerleri liste ya da demete eleman yapılmış gibi olur. Örneğin:
a = 1, 2, 3, 4, 5
b = [10, 20, *a, 30, 40]
#----------------------------------------------------------------------------------
--------------------------------------
a = 1, 2, 3, 4, 5
b = [10, 20, *a, 30, 40]
c = 10, 20, *a, 30, 40
#----------------------------------------------------------------------------------
--------------------------------------
Benzer biçimde bir sözlükl oluştururken sözlüğün elemanı da **'lı bir nesne
olabilir. Bu duurmda **'ın yanındaki sözlüğün elemanları
o sözlüğün elemanlarına eklenmiş olur. Örneğin:
Tabii burada artık **'ın yanındaki sözlüğün anahtarlarının birer string olması
gerekmez.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir çağrı sırasında argümanların girilişi ile ilgili nihai kural şöyledir:
2) Ancak isimli argümanın sağında bir ya da birden fazla karışık sırada *'lı ve
**'lı argüman bulunabilir.
Burada önce isimsiz ve *'lı argümanlar eşleştirilir. 1 --> a ile, 2 --> b ile,
10 --> c ile ve 20 --> d ile eşleştirilecektir.
Sonra isimli argümanlar da eşleştirilir. Burada toplamda her parametreye
yalnızca bir kez ve en fazla bir kez eşleştirme yapılmıştır. Örneğin:
Burada f'ye iki kez eşleştirme yapıldığı için çağrı geçersizdir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Açağıdaki örnekte add isimli fonksiyon *'lı paramtresiyle aldığı değerlerin
toplamına geri dönmektedir. Ancak bu değerler eğer
bir demet ya da liste ise onlar da bu toplama katılmışlardır.
#----------------------------------------------------------------------------------
--------------------------------------
def add(*args):
total = 0
for x in args:
if isinstance(x, (tuple, list)):
for y in x:
total += y
else:
total += x
return total
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki örnekte biz add fonksiyonunu aşağıdaki gibi çağıramayız:
#----------------------------------------------------------------------------------
--------------------------------------
33. Ders 08/08/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da ".py" uzantılı kaynak dosyalara "modül (module)" de denilmektedir.
Büyük bir projenin tek bir kaynak dosya biçiminde
organize edilmesi iyi bir teknik değildir. Çünkü:
import <dosya ismi> [as <isim>] [, <dosya ismi> [as <isim>], ...]
import util
import işlemi yapılırken as anahtar sözcüğü ile modüle bir takma isim
verilebilir. Örneğin:
import util as ut
import math
import statistics
import math
result = [Link](10)
result = [Link](10)
import numpy as np
result = [Link](10)
import math as mt
import util
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
# [Link]
import util
[Link]()
# [Link]
def foo():
print('foo')
#----------------------------------------------------------------------------------
--------------------------------------
import deyimi kaynak dosyanın herhangi bir yerinde bulunabilir. Bazı
programcılar bütün import dosyalarını programın tepesinde import ederler.
Bazıları gerektiği zaman gerektiği yerde import ederler. Bir modül import
edildiğinde o modülün içerisindeki tüm kodlar (yani deyiimler)
çalıştırılmaktadır. Yani import etme aslında import edilen dosyanın aynı
zamanda çalıştırılması anlamına da gelmektedir. Bu nedenle
import edilen dosyada örneğin print gibi komutlar varsa bunlar da
işletilecektir.
# [Link]
import util
[Link]()
# [Link]
def foo():
print('foo')
#----------------------------------------------------------------------------------
--------------------------------------
Bir modül import edildiğinde yorumlayıcı modülün içindeki kodları çalıştırır.
Sonra module isimli bir sınıf türünden bir
nesne yaratır. Modülün içerisindeki değişkenleri bu sınıfa yerleştirir ve modül
ismine sw bu nesnenin adresini yerleştirir.
Yani modül isimleri aslında module türünden bir nesnenin adresini tutmaktadır.
Örneğin:
Burada biz bir modül değişkenini başka bir değişkene atadık. Artık iki değişken
de aynı module nesnesini gösterdiğine göre
fonksiyon çağrılırken hangi ismin kullanılacağının bir önemi kalmamıştır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir modül iki kez import edildiğinde yalnızca ilk kez import edildiğinde o
modülün kodları çalıştırılır. İkinci kez import
edildiğinde yine modül ismine atama yapılır ancak modülün içerisindeki kodlar
çalıştırılmaz.
# [Link]
import sample
print('sample')
# [Link]
import math
result = [Link](10)
print(result)
Burada programcı standart modül olan math modülü yerine kendi dosyasını import
etmiş olabilir. Bu durumda [Link] çağrımı
error oluşturacaktır. Tabii bu durum aslında standart modüllerin [Link]
listesinde aranmas sırasına da bağlıdır. Siz
kaynak dosyalarınıza Python'ın stndart modüllerinin simini vermemelisiniz.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Modül dosyası yerel düzeyde de import edilebilir. Bu durumda bu import ismine
yalnızca o fonksiyonda erişebiliriz.
Aslında aynı dosya nasıl import edilmiş olursa olsun her zaman tek bir modül
nesnesi yaratılmaktadır. Örneğin:
def foo():
import util
print(id(util)) # 1581194445344
foo()
import util
print(id(util)) # 1581194445344
del util
import util
print(id(util)) # 1581194445344
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi bir modül import edildiğinde ilgili .py dosyası yorumlayıcı tarafıdan
hangi dizinlerde aranmaktadır? İşte Python
yorumlayıcısı modülleri [Link] isimli bir listede belirtilen dizinlerde
surasıyla aramaktadır. Örneğin:
>>> [Link]
['', 'f:\\', 'C:\\Users\\CSD\\anaconda3\\[Link]', 'C:\\Users\\CSD\\
anaconda3\\DLLs', 'C:\\Users\\CSD\\anaconda3\\lib',
'C:\\Users\\CSD\\anaconda3', 'C:\\Users\\CSD\\anaconda3\\lib\\site-packages',
'C:\\Users\\CSD\\anaconda3\\lib\\site-packages\\win32',
'C:\\Users\\CSD\\anaconda3\\lib\\site-packages\\win32\\lib', 'C:\\Users\\CSD\\
anaconda3\\lib\\site-packages\\Pythonwin']
Burada boş string yani '' ifadesi "bulunulan dizin (current working directory)"
anlamına gelmektedir. Genellikle çalışma dizini
bizim çalıştırdığımız Python programının bulunduğu dizindir. Ancak çalışma
dizini program çalışırken de istenildiği gibi değiştirilebilir.
Default durumda çalışma dizini eğer programı komut satırından çalıştırıyorsak
içinde bulunduğumuz dizindir. PyCharm IDE'si
her zaman çalışma dizinini proje dizini olarak ayarlamaktadır. Spyder IDE'si
ise çalışma dizinini o anda çalıştırılan Python programının
içinde bulunduğu dizin olarak ayarlamaktadır.
Pekiyi biz [Link] listesine kalıcı bir biçimde nasıl dizin ekleyebiliriz?
İşte bunu yapabilmek için PYTHONPATH isimli bir
"çevre değişkeni (envirionment variable)" eklemek gerekir. Çevre değişkenleri
konusu kursumuzun kapsamı dışındadır ve Derneğimizde
"Sistem Programlama ve İleri C Uygulamaları" kursunda ele alınmaktadır. Biz
burada yalnızca bu çevre değişkeninin nasıl oluşturulacağını
göreceğiz.
export PYTHONPATH=/istenilen/dizinin/yol/ifadesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
34. Ders 17/08/2022 -
Carsamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir modül import edildiğinde CPython gerçekleştirimi import edilen modülü
"arakoda" dünüştürerek __pycachce__ isimli bir dizinde
.pyc uzantılı bir dosya içerisinde saklamaktadır. Arakodlar bir sonraki import
işleminde daha hızlı bir biçimde işleme sokulmaktadır.
Ancak CPython standart modülleri çalışma dizininin altındaki __pycache__ dizini
içerisinde değil başka bir dizinde tutmaktadır.
Tabii CPython modül dosyasında değişiklik yapıldığında artık __pycache__
dizinindeki arakodu kullanmadan önce modül dosyasını yeniden
arakoda dönüştürür. Böylece __pycache__ içerisinde her zaman modülün en güncel
hali bulunur.
Ancak Python dünyasında standart bir arakod sistemi yoktur. Yani belli bir
Python yorumlayıcısı import işleminde modülü
hiç arakoda dönüştürmeyebilir. Kaldı ki farklı Python yorumlayıcılarında farklı
arakod sistemleri kullanılabilmektedir.
Gnel olarak CPython'ın son versiyonları modülün arakod dosyasını modülün
bulunduğu dizinin içerisindeki __pycache__ diziinnde
saklamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir modüldeki belli değişkenleri modül ismiyle niteliklendirmeden doğrudan
kullanabilmek için from import deyimi
kullanılmaktadır. from import deyiminin genel biçimi şöyledir:
Örneğin:
Biz burada artık foo ve bar değişkenlerini [Link] ve [Link] biçiminde değil
doğrudan foo ve bar biçiminde kullanabiliriz.
import edilen değişkenlere as ile başka isimler verilebilir. Örneğin:
Burada biz artık foo ismini değil f ismini kullanabiliriz. bar isminde as
kullanılmadığına göre bu isim yine bar biçiminde
kullanılacaktır.
from import ile bir değişken import edildiğinde yine modüldeki kodların tamamı
çalıştırılmaktadır. Tabii bu işlem toplamda yine
bir kez yapılır. from import deyimi modülün kendisini import etmemektedir.
Örneğin:
Burada biz util ismini kullanamayız. Eğer util ismini kullanacaksak ayrıca
import uygulamalıyız:
import util
Aşağıdaki from import işlemine bakınız:
import util
foo = [Link]
del util
#----------------------------------------------------------------------------------
--------------------------------------
result = sqrt(10)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
from import deyiminde verilen isim zaten varsa artık o değişkene yeni bir atama
yapılacağına dikkat ediniz. Örneğin:
s = 10
def foo():
import math
result = [Link](10)
print(result)
def bar():
result = [Link](10) # error! çünkü math ismi foo'da yerel
print(result)
foo()
bar()
Tabii farklı fonksiyonlarda aynı modül yerel olarak import edilmiş olsa bile
toplamda modülün içerisindeki kodlar yine yalnızca
bir kez çalıştırılmaktadır. Örneğin:
def foo():
import util
[Link]()
def bar():
import util
[Link]()
foo()
bar()
#----------------------------------------------------------------------------------
--------------------------------------
from import deyiminin özel bir biçimi de yıldızlı biçimdir. Örneğin:
Bu biçimde modüldeki tüm isimler import edilir. Yani o isimlerin hepsini biz
doğrudan kullanabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
result = sqrt(10)
print(result)
result = pow(10, 2)
print(result)
result = sin(0.5)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Python'da başında ve sonunda iki alt tire olan bazı özel isimler çeşitli
nedenlerle kullanılabilmektedir. Örneğin:
__init__
__new__
__main__
__add__
#----------------------------------------------------------------------------------
--------------------------------------
import ya da from import deyiminde modülün tüm kodlarının çalışması bazen
istenmeyebilir. Örneğin birisi [Link] isimli bir
program yazmış olabilir. Bu program çeşitli şeyleri yapıyor olabilir. Ancak bu
[Link] içerisinde foo ve bar isimli fonksiyonlar
başka kişilerin de kullanabileceği faydalı fonksiyonlar olabilir:
# [Link]
def foo():
print('foo')
def bar():
print('bar')
print('çeşitli kodlar')
print('çeşitli kodlar')
print('çeşitli kodlar')
Burada biz bu foo ve bar fonksiyonlarıı kullanmak için modülü import esek
modülün kodları çalışacak ve aslında bizim istemediğimiz
şeyler yapılacaktır. Bu durum nasıl engellenebilir?
if __name__ == '__main__':
pass
Örneğin [Link] isimli bir program yazacak olalım. Programımız da 2'den 1000'e
kadar asal sayıları ekrana yazdırıyor olsun.
Ancak bu modülde isprime isimli asallık testi yapan fonksiyonu da başkalarının
kullanmasına imkan tanıyacak biçimde yazmış olalım.
Şimdi bu util modülünü import eden kişi 2'den 1000'e kadar asal sayıları
yazdırmak istemeyecektir. Yalnızca isprime fonksiyonu kullanmak isteyecektir.
O zaman [Link] dosyası şöyle düzenlenmelidir:
import math
def isprime(val):
if val % 2 == 0:
return val == 2
sqrt_val = [Link](val)
for i in range(3, int(sqrt_val) + 1):
if val % i == 0:
return False
return True
if __name__ == '__main__':
for i in range(2, 1000):
if isprime(i):
print(i, end=' ')
# [Link]
import util
result = [Link](101)
print('prime' if result else 'not prime')
#----------------------------------------------------------------------------------
--------------------------------------
Rastgele sayı üretme yazılımda çeşitli amaçlarla kullanılabilmektedir. Örneğiin
bir oyun programı rastgele sayılar üreterek
birtakım nesnelerin rasgele hareket etmesini sağlayabilir. Benzer biçimde bir
simülayon programı rasgele sayılarla gerçek bir ortamı simüle
etmeye çalışabilir. Rastgele sayı üretimi kriptolojide de kullanılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
rondom modülündeki random iisimli fonksiyon parametresizdir. Her çağrıldığında
[0,1) aralığında float bir sayı üretir.
#----------------------------------------------------------------------------------
--------------------------------------
import random
for i in range(10):
result = [Link]()
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
random modülündeki gauss fonksiyonu normal dağılma uygun rastgele sayılar
üretmek için kullaılır. Fonksiyonun ilk
parametresi ortalamayı, ikinci parametresi standart sapmayı belirtir.
#----------------------------------------------------------------------------------
--------------------------------------
import random
for i in range(10):
result = [Link](100, 15)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
rondom modülündeki randint isimli fonksiyon [a, b] aralığında rastgele bir
tamsayı (int türden sayı)üretir.
#----------------------------------------------------------------------------------
--------------------------------------
import random
for i in range(10):
result = [Link](10, 20)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Biz randint fonksiyonu ile yazı-tura atabiliriz. Yazı tura işlemini belli
miktarda yapıp oranlara bakarsak gitgide sayının
0.5'e yakınsadığını görürürz. Buna "büyük sayılar yasası (law of large
numbers)" denilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
import random
def head_tail(n):
head = 0
tail = 0
for _ in range(n):
result = [Link](0, 1)
if result == 0:
head += 1
else:
tail += 1
#----------------------------------------------------------------------------------
--------------------------------------
random modülündeki choice isimli fonksiyon bir "sequence container'ı" yani
[...] ile indekslenebilen bir nesneyi parametre olarak alır.
O nesnedeki rastgele bir elemanı geri dönüş değeri olarak verir.
#----------------------------------------------------------------------------------
--------------------------------------
import random
result = [Link](a)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
random modülündeki sample isimli fonksiyon bir sequnce contaniner'ı ya da bir
kümeyi parametre olarak alır. Sonra bir de
eleman sayısını parametre olarak alır. O kğmeden o miktarda elemanı rastgele
seçer. (Buna istatistikte "rassal örnekleme" (random sampling))
denilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
import random
result = [Link](a, 3)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
sample fonksiyonundaki counts parametresi birinci parametredeki nesnenin eleman
sayısı kadar olmalıdır. Bu parametre
aslında ağırlıklandırmak için kullanılmaktadır. Yani örneğin:
import random
#----------------------------------------------------------------------------------
--------------------------------------
random modülündeki choices fonksiyonu sample fonksiyonuna benzemektedir. sample
fonksiyonu iadesiz çekim yaparken
choices fonksiyonu iadeli çekim yapmaktadır. isimli kullanılmak zorunda olan k
parametresi kaç elemanlık çekim yapılacağını
belirtmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
import random
#----------------------------------------------------------------------------------
--------------------------------------
random modülündeki randrange fonksiyonu parametrik kullanım bakımından range
fonksiyonuna benzemektedir. Ancak rastgele tamsayı
üretmektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
import random
for i in range(10):
result = [Link](0, 10, 2)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
random modülündeki shuffle isimli fonksiyon karıştırma işlemini yapar.
Fonksiyonun parametresinin bir liste olması gerekir.
Karıştırma "in-place" biçimde yapılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
import random
print(a)
[Link](a)
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
35. Ders 22/08/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aslında karıştırma algoritması çok kolaydır. Genellikle izlenen yok dizinin tüm
elemanlarını sırasıyla rastgele elemanrla
yer değiştirme yöntemidir. Aşağıda bu yöntem uygulanmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
import random
def myshuffle(a):
for i in range(len(a)):
k = [Link](len(a))
a[k], a[i] = a[i], a[k]
a = [1, 2, 3, 4, 5]
print(a)
myshuffle(a)
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte bir oyun kartı destesi oluşturulmuş sonra da bu deste dört
oyuncuya dağıtılmıştır. Ancak oyunculara dağıtılan
kartlar sıraya da dizilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
import random
def create_deck():
card_type = ['Sinek', 'Maça', 'Karo', 'Kupa']
card_name = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Vale', 'Kız',
'Papaz', 'As']
deck = []
for cname in card_name:
for ctype in card_type:
[Link](ctype + '-' + cname)
return deck
def keyfunc(s):
card_type = ['Sinek', 'Maça', 'Karo', 'Kupa']
card_name = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Vale', 'Kız',
'Papaz', 'As']
def distribute_deck(deck):
cards = ([], [], [], [])
for i in range(len(deck)):
cards[i % 4].append(deck[i])
for c in cards:
[Link](key=keyfunc, reverse=True)
return cards
deck = create_deck()
[Link](deck)
north, east, south, west = distribute_deck(deck)
print(north, end='\n\n')
print(east, end='\n\n')
print(south, end='\n\n')
print(west, end='\n\n')
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte rastegele sayı üretilerek pi sayısı elde edilmiştir. Buradaki
matematiksel açıklama için ders notlarına bakınız.
#----------------------------------------------------------------------------------
--------------------------------------
import random
import math
def getpi(n):
inside = 0
for _ in range(n):
x = [Link]()
y = [Link]()
distance = [Link](x ** 2 + y ** 2)
if distance < 1:
inside += 1
return 4 * inside / n
pi = getpi(10000000)
print(pi)
#----------------------------------------------------------------------------------
--------------------------------------
1 ile 49 arasında (49 dahil) 6 tane farklı sayı üretmek aslında basit bir
biçimde şöyle yapılabilir:
import random
def get_column():
column = []
for _ in range(6):
while True:
val = [Link](1, 49)
if val not in column:
break
[Link](val)
return column
result = get_column()
print(result)
def get_column():
column = set()
while len(column) != 6:
val = [Link](1, 49)
[Link](val)
return list(column)
result = get_column()
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
enumerate isimli built-in fonksiyon bizden dolaşılabilir bir nesne alır, bize
dolaşılabilir bir nesne verir. enumerate
fonksiyonun verdiği dolaşılabilir nesne dolaşıldığında iki elemanlı demetler
elde edilecektir. Öyle ki bu demetlerin
ilk elemanları 0'dan başlayan indeks numarasından ikinci elemanları da bizim
verdiğimiz dolaşılabilir nesnedeki elemnalardan oluşur. Örneğin:
for t in enumerate(names)
print(t)
Buradan sırasıyla (0, 'ali'), (1, 'veli'), (2, selami), (3, 'ayşe'), (4,
'fatma') biçiminde demetler elde edilecektir.
Tabii bi<z genellikle unpack yaparak demet elemanlarını elde ederiz:
#----------------------------------------------------------------------------------
--------------------------------------
iterator = enumerate(names)
for t in iterator:
print(t)
iterator = enumerate(names)
for index, x in iterator:
print(index, x)
#----------------------------------------------------------------------------------
--------------------------------------
enumerate fonksiyonu bir dolaşılabilir nesneyi for döngüsü ile dolaşırken hem
elemanların index numaralarını hem de
elemanların değerlerini elde etmek için kullanılşmaktadır. enumerate
fonksiyonunun ikinci bir parametresi de vardır. Bu ikinci
parametreye default olarak 0 değeri verilmiştir. Bu ikinci parametre indeksin
nereden başlatılacağını belirtir. Örneğin:
Burada artık şu demetler elde edilecektir: (10, 'ali'), (11, 'veli'), (12,
selami), (13, 'ayşe'), (14, 'fatma')
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir değişkenin kullanılabildiği program aralığına "faaliyet alanı (scope)"
denilmektedir. Python'da değişkenler faaliyet alanları
bakımından üç gruba ayrılmaktadır:
a = 10
b = a
def foo():
x = 10; # x bir yerel değişken
print(x) # geçerli, x buarada faaliyet gösteriyor
x = 10
def foo():
print(x) # global olan x
def bar():
print(x) # global olan x
foo()
bar()
Bir fonksiyon içerisinde global değişkenle aynı isimli bir değişkene atama
yapılırsa bu durum global değişkene atama yapıldığı
anlamına gelmez. Aynı isimli yeni bir yerel değişken yaratılır. Atama ona
yapılmış olur. Örneğin:
x = 10
def foo():
x = 20 # x yeni bir yerel değişken, global olan değil
print(x) # buradaki x yerel olan x
foo()
print(x) # buradaki x global x, zaten yerel x burada faaliyet
göstermiyor, 10 çıkacak
Örneğin:
global x
global y, z, k
Örneğin:
x = 10
def foo():
global x
x = 20 # global olan x
print(x) # global olan x
foo()
print(x) # 20
x = 10
def foo():
global x # burada global bildirimine gerek yok
print(x) # global olan x
foo()
Bir fonksiyon içerisinde önce bir global değişken kullanılmışsa artık aynı
isimli bir yerel değişken yaratılamaz. Eğer yaratılmak
istenirse bu durum error oluşturur. Örneğin:
x = 10
def foo():
print(x)
x = 20 # geçersiz! çünkü daha önce aynı isimli global değişken
fonksiyonda kullanılmış
print(x)
foo()
Fonksiyon ieçrisinde önce bir global değişken kullanılıp sonra aynı değişkene
ilişkin global bildiri de yapamayız. Örneğin:
x = 10
def foo():
print(x) # global olan x
global x # error önce bir global değişken kullanılıp sonra o
değişkene ilişkin global bildirimi yapılamaz!
x = 20
print(x)
foo()
def foo():
for i in range(10):
print(i)
x = 10
print(x) # geçerli
print(i) # geçerli
#----------------------------------------------------------------------------------
--------------------------------------
global bildiriminde henüz ilgili global değişken yaratılmamış olabilir. Bu
durum geçerlidir. Eğer global bildirimi görüldüğünde
global değişken yaratılmadıysa yine de bu bildirim global bir değişkeni
belirtir. Yani bu sayede global değişken fonksiyonun içerisinde yaratılabilir.
Örneğin:
def foo():
global a
a = 10 # a global değişikeni yaratılıyor, çünkü henüz yaratılmamış
foo()
print(a)
def foo():
global a
foo()
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
Bir değişke (nesneyi kastetmiyoruz) programın belli bir aşamasında yaratılır,
bir süre faaliyet gösterdikten sonra yok edilir.
Değişkenin bellekte kaldığı zaman aralığına "ömür (duration)" denilmektedir.
def foo():
a = 10
# ...
print(a)
- Bir global değişken o değişkene ilk kez değer atandığında yaratılır, program
sonuna kadar yaşamaya devam eder. Bu nedenle
global değişkenler her yerden, fonksiyonlardan kullanılabilmektedir. Örneğin:
x = 10
def foo():
print(x)
print(x)
foo()
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir değişkenin ne zaman yaratılıp ne zaman yok edildiğini yukarıda açıkladık.
Pekiyi değişkenlerin gösterdikleri nesneler
ne zaman yaratılıp ok edilmektedir. Örneğin:
x = 10
x = 20
Burada x değişkeni yaratılırken aynı zamanda int bir nesne de yaratılır. Daha
sonra int türü "değiştirilemez (immutable)" olduğu için
yeni bir int nesnesi yaatılıp x değişkeni artık o yeni yaratılan x nesnesini
gösterecektir. Pekiyi eski nesneye ne olacaktır? İşte Python'da
"çöp toplayıcı (garbage collector)" denilen mekanizma bir nesneyi hiçbir
değişken göstermiyorsa artık o nesneyi arka planda bellekten silmektedir.
Çöp toplama tamamen yorumlayıcı sistem tarafından yönetilir. Dolayısıyla
programcı bu konuda bir şey yapmaz. Örneğin:
def bar()
x = 10 # bu noktada int nesneyi yalnızca x gösteriyor
foo(x)
print(x) # bu noktada yine int nesnesi yalnızca x gösteriyor
bar()
Aşağıdaki örnekte döngünün her ynelenemesinde yeni bir int nesne yaratılır,
ancak öncekiler çöp toplayıcı tarafından yok edilir.
i = 0
for _ in range(100):
print(id(i)) # her defasınde değişik bir adres yazıdırılacaktır
i += 1
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da çokça kullanılan diğer bir "built-in" fonksiyon da map isimli
fonksiyondur. Bu fonksiyon bizden bir fonksiyonu ve dolaşılabilir bir nesneyi
parametre olarak olarak alır. Bize dolaşılabilir bir nesne verir. map
fonksiyonun veridği dolaşılabilir nesne dolaşıldığında
bizim verdiğimiz dolaşılabilir nesnenin elemanları veridğimiz fonksiyona
argüman yapılıp fonksiyonun geri dönüş değerleri elde
edilecektir. Bizim map fonksiyonuna verdiğimiz fonksiyonun bir parametresi
olmak zorundadır.
#----------------------------------------------------------------------------------
--------------------------------------
def foo(a):
return a * a
x = [1, 2, 3, 4, 5]
m = map(foo, x)
for i in m:
print(i, end=' ')
print()
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte isimlerin karakter uzunlukları elde edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte yazı içerisindeki sayılar toplanmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
s = '1 2 3 4 5'
total = 0
for x in map(int, [Link]()):
total += x
print(total)
#----------------------------------------------------------------------------------
--------------------------------------
Built-in sum fonksiyonu dolaşılabilir bir nesne alıp onun tüm elemanlarının
toplamanına geri dönmektedir. O halde yukarıdaki
örneği daha kompakt bir biçimde aşağıdaki gibi oluşturabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
s = '1 2 3 4 5'
#----------------------------------------------------------------------------------
--------------------------------------
Built-in max ve min fonksiyonları bizden dolaşılabilir bir nesne alıp onların
en büyük ve en küçük elemanlarını verir.
Aşağıdaki örnekte en uzun ismin karakter uzunluğu elde edilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aslında map fonksiyonu birden fazla dolaşılabilir nesne alabilmektedir. Bu
durumda bu dolaşılabilir nesnenin karşılıklı elemanları
birinci parametresiyle verilen fonksiyona parametre olarak aktarılır. Yani map
fonksiyonuna biz kaç tane dolaşılabilir nesne verirsek
birinci parametre ile geçirdiğimiz fonksiyonun o kadar parametresi olmak
zorundadır.
#----------------------------------------------------------------------------------
--------------------------------------
a = [1, 2, 3, 4, 5]
b = [10, 20, 30, 40, 50]
c = [100, 200, 300, 400, 500]
iterable = map(foo, a, b, c)
for x in iterable:
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
map fonksiyonuna birden fazla dolaşılabilir nesne geçirdiğimizde bunların
eleman sayısı aynı olmak zorunda değildir.
Bunlardan herhangi birinde sona gelindiğinde tüm dolaşım sonlandırılır.
Aşağıdaki örnekte yalnızca iki dolaşım
yapılacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
a = [1, 2, 3, 4, 5]
b = [10, 20]
c = [100, 200, 300, 400, 500]
iterable = map(foo, a, b, c)
for x in iterable:
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
Python'da iç içe (nested) fonksiyon tanımlamaları yapılabilmektedir. Örneğin:
def foo():
...
def bar():
...
...
Aşağıdaki örnekte biz bar fonksiyonunu ancak foo fonksiyonun içerisinde ve bar
tanımlşandıktan sonra çağırabiliriz.
bar fonksiyonunu dışarıdan çağıramayız.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
print('foo')
def bar():
print('bar')
bar()
foo()
#----------------------------------------------------------------------------------
--------------------------------------
Eğer bir fonksiyon genel değil de yalnızca başka bir fonksiyonun yazımı için
oluşturuluyorsa bu fonksiyonu asıl fonksiyonun iç fonksiyonu
olarak tanımlama iyi bir tekniktir. Çünkü dışarıdaki fonksiyonlar herkesin
ilgisini çeker. Halbuki iç fonksiyonlar dışarıdan kullanılamayacağından
dolayı zaten kişilerin ilgisini çekmez. Onların kafalarını karıştırmaz. Örneğin
2'den parametresiyle belirtilen sayıya kadar asal sayıları yazdıran
bir fonksiyon yazmak isteyelim. Bu fonksiyon sayının asal olup olmadığını test
eden isprime gibi bir fonksiyonu kullanıyor olsun.
Bu isprime fonksiyonu dışarıyı ilgilendiren bir fonksiyon değilse bir iç
fonksiyon olarak yazılabilir. Bir yalnızca başka bir fonksiyonun yazımında
kullanılmak
için oluşturuluyor ise onu iç fonksiyon yapmak iyi bir tekniktir.
#----------------------------------------------------------------------------------
--------------------------------------
import math
def print_primes(n):
def isprime(val):
if val % 2 == 0:
return val == 2
for i in range(3, int([Link](val)) + 1, 2):
if val % i == 0:
return False
return True
print_primes(100)
#----------------------------------------------------------------------------------
--------------------------------------
37. Ders 31/08/2022 -
Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
İç bir fonksiyon dış bfonksiyonun o anda kadar yaratılmış olan yerel
değişkenlerini kullanabilirç. Ancak dış fonksiyon iç fonksiyonun
yerel değişkenlerini kullanamaz. Örneğin:
def foo():
a = 10
def bar():
print(a) # 10
b = 20
print(b) # 20
bar()
foo()
def foo():
a = 10
def bar():
print(a) # 10
b = 20
print(b) # 20
bar()
foo()
#----------------------------------------------------------------------------------
--------------------------------------
Tabii Python'da isim araması fonksiyon çarılıp akış ilgili noktaya geldiğinde
yapıldığına göre dış fonksiyonun yerel değişkeni
iç fonksiyondan sonra da oluşturulmuş olsa eğer çağrılma sırasında bu yerel
değişken yaratılmışsa iç fonksiyon içerisinden kullanılabilir.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
a = 10
def bar():
print(a) # 10
print(b) # 20
b = 20
bar()
#----------------------------------------------------------------------------------
--------------------------------------
İç bir fonksiyon içerisinde dış fonksiyonun yerel değişkeni ile aynı isimli bir
değişkene atama yaptığımızda biz dış fonksiyounun
yerel değişkenini değiştirmiş olmayız. İç fonksiyonda yeni bir yerel değişken
yaratmış oluruz.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
a = 10
def bar():
a = 20 # bar'a ilişkin yeni bir yerel a, 20
print(a)
bar()
print(a) # foo'nun yerel a'sı, 10
foo()
#----------------------------------------------------------------------------------
--------------------------------------
İç fonksiyonun dış fonksiyonun yerel değişkenini değiştirebilmesi için nonlocal
bildirimi yapması gerekir. nonlocal bildiriminin
genel biçimi şöyledir:
Örneğin:
def foo():
a = 10
def bar():
nonlocal a
a = 20 # buradaki a foo'nun a'sı
print(a) # foo'nun a'sı, 20
bar()
print(a) # foo'nun a'sı, 20
foo()
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
a = 10
def bar():
nonlocal a
a = 20 # buradaki a foo'nun a'sı
print(a) # foo'nun a'sı, 20
bar()
print(a) # foo'nun a'sı, 20
foo()
#----------------------------------------------------------------------------------
--------------------------------------
Tabii iç fonksiyonun da iç fonksiyonu olabilir. Bu durrmda nonlocal bildirimi
benzer biçimde etki gösterir.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
a = 10
def bar():
nonlocal a
def tar():
nonlocal a
a = 20 # foo'nun a'sı
tar()
print(a) # foo'nun a'sı yazdırılıyor, 20
bar()
print(a) # foo'nun a'sı yazdırılıyor, 20
foo()
#----------------------------------------------------------------------------------
--------------------------------------
nonlocal bildirimi ile belirtilen ismin aranması içten dışa doğru
yapılmaktadır. Dolayısıyla nonlocal bildirimi için bildirilen
değişkenin hemen dış fonksiyonda bulunyor olması gerekmez.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
a = 10
def bar():
def tar():
nonlocal a # bar'da a olmadığı için foo'nun a'sı
a = 20
tar()
bar()
print(a) # foo'nun a'sı yazdırılıyor, 20
foo()
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi global bildirimi fonksiyon içerisinde henüz global değişken
yaratılmamış olsa bile yapılabiliyordu. Ancak
nonlocal bildirimi için durum böyle değildir. nonlocal bildirimi ile bildirilen
değişken dıştaki herhangi bir fonksiyonda
bulunamazsa bu durum error oluşturur. Örneğin:
def foo():
def bar():
nonlocal a # error! dış fonksiyonda a yok!
a = 10
bar()
foo()
a = 10
def foo():
def bar():
nonlocal a # error! dış fonksiyonda yerel a yok! global bir
a'nın olması önemli değil
a = 10
bar()
foo()
Dış fonksiyonda aynı isimli değişken global bildirimi ile bildirilmiş olsa bile
nonlocal bildirimi bu değişkeni görmez.
Çünkü global bildirimi de hiçbir zaman yerel bir değişkene ilişkin değildir.
a = 10
def foo():
global a
def bar():
nonlocal a # error! dış fonksiyonda yerel a yok! global bir
a'nın olması önemli değil
a = 10
bar()
foo()
Benzer biçimde dış fonksiyonda bir global bildirimi yapılmışsa daha iç bir
fonksiyonda yaratılan aynı isimli değişken
o iç fonksiyonun değişkeni olur. Örneğin:
a = 10
def foo():
global a
a = 20 # global a değiştiriliyor
def bar():
a = 30 # yeni bir yerel a yaratılıyor
bar()
foo()
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Tabii her modülün global değiikenleri o modüle özgüdür. Yani her modül kendi
içerisinde farklı bir isim alanı (nameepace) oluşturmaktadır.
Örneğin util modülü içerisinde globa bir a değişkeni yaratılmış olsun:
# [Link]
a = 10
# [Link]
import util
a = 20
print(a) # 20
print(util.a) # 10
import util
a = util.a
del util
Bu durumda:
deyimi ile biz a'yı kullandığımızda util'deki a'yı kullanmış olmayız. Çünkü int
değiştirilmez bir tür olduğu için a = util.a atamasında
a artık başka bir int değişkeni göstermektedir. Dolayısıyla:
a = 10
print(a) # a bu modüldeki a
import util
print(util.a) # util'deki a
Tabii from import deyimi ile import ettiğimiz değişken değiştitilebilir bir
nesneye ilişkin ise bu durumda gerçekten o nesne
üzerinde yapılan değişiklik o modüldeki nesneyi etkileyecektir. Örneğin:
# [Link]
a = [1, 2, 3, 4, 5]
#[Link]
a[0] = 100
print(a) # [100, 2, 3, 4, 5]
import util
print(util.a) # [100, 2, 3, 4, 5]
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir modül yerel düzeyde import edildiğinde her ne kadar modül değişkenini
yalnızca o mfonksiyonda kullanabiliyor olsak da
aslında hangi düzeyde import yapılmış olursa olsun. Toplamda her zaman tek bir
modül nesnesi olşturulmaktadır. Aşağıdaki örnekte
util modülündeki listenin elemanı yerel değişkende değiştirilmiştir. Ancak
kodun başka bir yerinde yeniden aynı modül import edildiğinde
listenin o elemanı değiştirilmiş olarak görülür.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
import util
util.a[0] = 100
print(util.a) # [100, 2, 3, 4, 5]
print(id(util)) # 1581194445344
foo()
import util
print(util.a) # [100, 2, 3, 4, 5]
print(id(util)) # 1581194445344
del util
import util
print(util.a) # [100, 2, 3, 4, 5]
print(id(util)) # 1581194445344
#----------------------------------------------------------------------------------
--------------------------------------
Python'da globals isimli built-in fonksiyon o andaki tüm global değişkenleri
bir sözlük nesnesi biçiminde bize verir.
Sözlüğün anahtarları global değişkenlerin isimlerinden değerleri ise onların
değerlerinden oluşur. globals fonksiyonu ile
global değişkenleri elde ettiğinizde kendiniz yaratmadığı başka değişkenler de
görürsenin şaşırmayınız. Örneğin __name__ isimli
global değişkenin biz daha önce yorumlayıcı tarafındna oluşturulduğunu
görmüştük. Benzer biçimde bütün built-in değişkenler
__builtins__ isimli bir global sözlük nesnesi içerisinde bulunmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
name = 'ali'
def foo():
pass
g = globals()
print(g)
print(list(g)) # ['__name__', '__file__', '__nonzero__', '__builtins__', 'a',
'name', 'foo', 'g']
#----------------------------------------------------------------------------------
--------------------------------------
Biz globals fonksiyonuyla elde etmiş olduğumuz sözlüğe eleman ekleyerek yeni
global değişkenleri bu yolla oluşturabiliriz.
Yorumlayıcı da zaten bütün global değişkenleri aslında kendi içerisinde bir
sözlükte tutmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
g = globals()
g['b'] = 20
print(b) # 20
print(a) # 10
#----------------------------------------------------------------------------------
--------------------------------------
locals fonksiyonu da hangi fonksiyon içerisinde çağrılmışsa o fonksiyonun yerel
değişkenlerini bir sözlük olarak vermektedir.
locals fonksiyonu global düzeyde çağrılırsa tamamen globals gibi etki
göstermektedir.
#----------------------------------------------------------------------------------
--------------------------------------
a = 10
def foo():
b = 20
l = locals()
print(l) # {'b': 20}
print(list(l)) # ['b']
foo()
#----------------------------------------------------------------------------------
--------------------------------------
locals fonksiyonu iç bir fonksiyonda da çağrılabilir. Bu durumda yalnızca kendi
fonksiyonundaki yerel değişkenleri
verir.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
a = 10
def bar():
b = 20
l = locals()
print(l) # {'b': 20}
bar()
foo()
#----------------------------------------------------------------------------------
--------------------------------------
locals fonksiyonu ile elde edilen sözlüğe bir ekleme yapıldığında gerçekten de
sözlüğe ekleme yapılmış olur. Ancak eklenen
isim yerel değişken olarak kullanılamaz. Bu durum globals fonksiyonundaki
duruma bu bakımdan benzememektedir.
Python yorumlayıcı aslında fonksiyonun yerel değişkenlerini de kendi içerisinde
bir sçzlükte tutmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
a = 10
l = locals()
print(l) # {'a': 10}
l['b'] = 20
print(l) # {'a': 10, 'b': 20}
print(b) # error! b eklenmiş olsa da bu biçimde kullanılamaz!
foo()
#----------------------------------------------------------------------------------
--------------------------------------
İçlemler (comprehensions) "fonksiyonel (functional)" daha çok dillerde
karşımıza çıkan özel ifadelerdir. İçlemler üçe
ayrılmaktadır:
Eğer içlemden bir liste elde ediliyorsa buna "liste içlemi", kğme elde
ediliyorsa buna "küme içlemi", sözlük elde
ediliyorsa buna da "sözlük içlemi" denilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
İçlemlerin genel biçimleri biribirine çok benzemektedir. Liste içlemlerinin
genel biçimi şöyledir:
a = [i * i for i in range(10)]
print(a)
temp = []
for i in iterable:
[Link](ifade)
Örneğin:
a = [i * i for i in range(10)]
print(a)
Her ne kadar yukarıdaki iki ifade eşdeğer gibi gözükse de içlemler genel olarak
daha hızlı olma eğilimindedir.
Çünkü içlemler genellikle daha temel düzeyde ve tek bir operasyon biçiminde
yapılmaktadır.
Tabii aslında for döngüsünün solundaki ifadenin döngü değişkeni ile ilgili
olması zorunluluk değildir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Liste içlemleriyle yapılmak istenen şeylerin bir bölümü map fonksiyonuyla da
yapılabilir. liste içlemleri bize ürün olarak
liste vermektedir. Halbuki map fonksiyonu bize ürün olarak dolaşılabilir bir
nesne verir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
İçlemlerde istenirse for cümlesinin sağına if anahtar sözcüğü ile bir koşul
cümleceği de eklenebilir. Bu durumda for döngüsü
her işletildiğinde sağdaki koşula bakılır. Eğer koşul doğruysa soldaki ifade
işletilir. Koşulsuz yanlışsa soldaki ifade işletilmez
ve bu ifadenin sonucu listeye eklenmez. Örneğin:
a = [i for i in range(10) if i % 2 == 0]
print(a) # [0, 2, 4, 6, 8]
temp = []
for i in iterable:
if koşul:
[Link](ifade)
Örneğin:
Burada için 'a' harfi geçen isimler bir liste biçiminde elde edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
38. Ders 05/09/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte başı 'a' ya da 'A' harfi ile başlayan isimler liste içlemi
yoluyla elde edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte küçük harf olan şehir isimleri büyük harfe dönüştürülerek bir
liste biçiminde elde edilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki liste içlemi tam palindrom olan cümleleri elde etmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
sentences = ['anastas mum satsana', 'izmir', 'ey edip adanada pide ye',
'eskişehir', 'adamla çeneç almada']
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte tam palindrom olmayan cümleler de elde edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
sentences = ['anastas mum satsana', 'izmir', 'ey edip adanada pide ye',
'eskişehir', 'adamla çene çalmada']
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte palindrom sayılar liste içlemi yoluyla elde edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte (şehir_ismi, plaka_numarası) biçimindeki demet listesinde
"şehir_ismi-plaka_numarası" biçiminde
bir string listesi elde edilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki örnek aşağıdaki gibi de yapılabilirdi
#----------------------------------------------------------------------------------
--------------------------------------
cities = [('ankara', 6), ('izmir', 35), ('eskişehir', 26), ('muğla', 48),
('kastamonu', 37)]
#----------------------------------------------------------------------------------
--------------------------------------
Örneğin biz belli bir sayıya kadar olan asal sayıları bir liste biçiminde liste
içlemi yoluyla elde edebiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
def isprime(val):
if val % 2 == 0:
return val == 2
for i in range(3, val, 2):
if val % i == 0:
return False
return True
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte bir listenin çift indisli elemanları liste içlemi yoluyla
elde edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki işlem aşağıdaki gibi enumerate fonksiyonu kullanılarak da
yapılabilirdi.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aslında içlemlerin içerisinde birden fazla for döngüsü de olabilir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte iki kümenin kartezyen çarpımları bir demet listesi biçiminde
elde edilmişir. İçlemlerde demet oluştururken
parantezler gerekmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir içlemdeki ifade başka bir içlem olabilir. Örneğin bu yöntemle biz liste
listeleri elde edebiliriz. Örneğin:
Burada for dngsü her işletildiğinde ifade olarak solda içlem yapılacaktır.
Soldaki içlem de bize bir liste vereceğine göre
burada listelerden oluşan bir liste elde edilecektir.
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte liste içeisindeki listelerdeki çift elemanlar elde elde
edilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Amacımız her biri 5 elemandan oluşan 10 elemanlı tüm elemanları 0 olan bir
liste listesi oluşturmak olsun.
Bu işlemi "repitition" ile yapamayız:
>>> a = [[0, 0, 0, 0, 0]] * 10
>>> a
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0,
0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0],
[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
Her ne kadar görünüşte bu işlem yapılmış gibi olsa da aslında buada bir kusur
vardır. Listeler "değiştirilebilir (mutable)"
olduğu için "repitition" işlemi de kopyalama yoluyla yapıldığı için iç listenin
bir elemanı değiştirildiğinde sanki tüm
listelerin ilgili elemanları değiştirilmiş gibi olur. Örneğin:
Pekiyi bu işlemi doğru olarak nasıl yapabiliriz? İlk akla gelecek yöntem namuel
bir for döngüsü kullanmaktadır:
>>> a = []
>>> for _ in range(10):
... [Link]([0, 0, 0, 0, 0])
...
>>> a
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0,
0, 0], [0, 0, 0, 0, 0],
[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> a[0][0] = 100
>>> a
[[100, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0,
0, 0, 0], [0, 0, 0, 0, 0],
[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
#----------------------------------------------------------------------------------
--------------------------------------
Bir liste içleminde for cümleciğinin "in" kısmında da içerisindeki ifade de
içlem kullanılabilir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Genel olarak içlemler bir ifade oluşturduğu için hem daha kompakt yazımlara
olanak sağlamakta hem de daha hızlı olma
eğilimindedir. Bu nedenle eğer içlem kullanabiliyorsanız içlemi tercih
etmelisiniz.
import time
start = [Link]()
a = []
for i in range(10000000):
[Link](str(i))
stop = [Link]()
start = [Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Küme içlemleri (set comprehension) aslında sentaktik biçim olarak tamamen liste
içlemleri ile aynıdır. Yalnızca dıştaki
köşeli parantezler yerine küme parantezleri kullanılmaktadır. Örneğin:
Bu işlemden artık bir liste değil bir küme elde edilecektir. Küme
parantezlerinin içi tamamen liste içlemlerindeki gibidir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte listenin iki elemanın toplamları bir küme olarak elde
edilmiştir. Burada yinelenen toplamların
alınmadığına dikkat ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
a = [1, 2, 3, 4, 5]
s = {i + k for i in a for k in a}
print(s)
#----------------------------------------------------------------------------------
--------------------------------------
Tabii küme içlemi kullanmak yerine liste içlemi oluşturup elde edilen liste
kümeye de dönüştürülebilir. Ancak bu işlem
küme içlemine göre daha yavaş bir çalışmaya neden olmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
a = [1, 2, 3, 4, 5]
#----------------------------------------------------------------------------------
--------------------------------------
Sözlük içlemleri (dictionary comprehension) tamamen küme içlemleri gibidir.
Ancak ifadenin anahtar: değer biçiminde olması gerekir:
a = [1, 2, 3, 4, 5]
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki işlemi biz manuel olarak bir for döngüsüyle de yapabilirdik. Ancak
içlemler hem kompkt bir ifade oluşturmak da
hem de daha hızlı olma eğilimindedir.
#----------------------------------------------------------------------------------
--------------------------------------
a = [1, 2, 3, 4, 5]
d = {}
for x in a:
d[x] = str(x)
print(d)
#----------------------------------------------------------------------------------
--------------------------------------
Yine aslında biz bir liste içlemi oluşturup oradan sözlük elde edebiliriz.
Ancak bu yöntem dolaylı ve yavaş bir yöntemdir.
#----------------------------------------------------------------------------------
--------------------------------------
a = [1, 2, 3, 4, 5]
#----------------------------------------------------------------------------------
--------------------------------------
Bir sözlükteki anahtarları değer, değerleri anahtar yapan kod parçasını daha
önce aşağıdaki gibi oluşturmuştuk:
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
result = {}
print(result)
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki işlem items metodu kullanılmadan da aşağıdkai gibi yapılabilirdi.
#----------------------------------------------------------------------------------
--------------------------------------
d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
#----------------------------------------------------------------------------------
--------------------------------------
Python'da liste içlemleri, küme içlemleri ve sçzlük içlemleri vardır. Ancal
demeyt içlemleri fiye bir şey yoktur.
Aslında sentaks olarak demet içlemi gibi bir sentaks vardır. Ancak bu sentaks
tamamen farklı bir anlama gelmektedir. Örneğin:
(x for x in a)
#----------------------------------------------------------------------------------
--------------------------------------
zip fonksiyonu çok kullanılan "built-in" bir fonksiyondur. Parametrik yapısı
şöyledir:
def zip(*iterables):
pass
zip fonksiyonu bizden dolaşılabilir nesneleri alır ve bize geri dönüş değeri
olarak bir dolaşılabilir nesne verir.
zip fonksiyonun geri döndürdüğü dolaşılabilir nesneyi dolaştığımızda demetler
elde ederiz. Öyle demetler elde ederiz ki
bu demetin elemanları bizim zip fonksiyonuna verdiğimiz dolaşılabilir nesnenin
karşılıklı elemanlarıdır. Örneğin:
z = zip(a, b, c)
for t in z:
print(t)
#----------------------------------------------------------------------------------
--------------------------------------
z = zip(a, b, c)
for t in z:
print(t)
#----------------------------------------------------------------------------------
--------------------------------------
zip fonksiyonuna verdiğimiz dolaşılabilir nesnelerin uzunlukları aynı olmak
zorunda değildir. En kısa nesne bittiğinde
dolaşım biter. Aşağıdaki örnekte en kısa nesne 3 eleman uzunlukta olduğuna göre
dolaşım üç kere devam edecektir.
#----------------------------------------------------------------------------------
--------------------------------------
z = zip(a, b, c)
for t in z:
print(t)
#----------------------------------------------------------------------------------
--------------------------------------
39. Ders 07/09/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Tabii biz zip fonksiyonunun bize verdiği dolaşılabili,r nesneyi dolaşırken açım
işlemi (unpacking) de yapabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
zip yapılmış bir nesneyi unzip yapabiliriz. Aslında bunun için de yine zip
fonksiyonunun kendisi kullanılmaktadır.
Şöyle ki: zip fonksiyonu bize z isimli dolaşılabilir bir nesne vermiş olsun.
Şimdi biz bu dolaşılşabilir nesneyi her dolaştığımızda
aslında demetler elde ederiz. O zaman biz zip(*z) gibi bir çağrı yaptığımız
zaman bu demetleri sanki zip fonksiyonun argümanları yapmış gibi oluruz.
Buradan bize verilen dolaşılabilir nesne dolaşıldığında eski değerler birer
demet olarak elde edilecektir.
#----------------------------------------------------------------------------------
--------------------------------------
z = zip(a, b, c)
for t in zip(*z):
print(list(t))
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki unzip işleminin daha iyi anlaşılması için aşağıdaki örneği
inceleyiniz.
#----------------------------------------------------------------------------------
--------------------------------------
result = zip(*a)
for t in result:
print(t)
# yukarıdakinin eşdeğeri
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte önce liste enumerate fonksiyonuna sokulmuş sonra oradan elde
edilen dolaşılabilir nesne zip fonksiyonuna
*'lı argüman olarak verilmiştir. Nasıl bir sonuç elde edildiğine dikkat ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
for t in zip(*enumerate(a)):
print(t)
#----------------------------------------------------------------------------------
--------------------------------------
Permütasyon n elemanlı kümenin k elemanlı farklı dizilimlerine denilmektedir.
Halbuki kombinasyon n elemanlı kümenin k'lı
alt kümeleri anlamına gelmektedir. n elemanlı kümenin k'lı permütasyonlarının
sayısı şöyle hesaplanmaktadır:
P(n, k) = n! / (n - k)!
Eskiden bu değerleri veren Python'da standart bir fonksiyon yoktu. Ancak Python
3.8 ile birlikte math modülüne comb ve perm
fonksiyonları eklenmiştir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
itertools modülündeki permutations isimli fonksiyon bizden dolaşılabilir bir
nesneyi ve bir sayıyı parametre olarak alır ve bize
dolaşılabilir bir nesne verir. İşte permutations fonksiyonunun bize verdiği
dolaşılabilir nesneyi dolaştığımızda bizim
birinci parametreyle verdiğimiz kümenin ikinci parametreyle belirtilen
permütasyonlarını elde ederiz.
#----------------------------------------------------------------------------------
--------------------------------------
import itertools
result = [Link](a, 3)
for t in result:
print(t)
#----------------------------------------------------------------------------------
--------------------------------------
Şimdi de permutations fonksiyonun bize verdiği dolaşılabilir nesneyi enumerate
fonksiyonuna sokarak dolaşalım.
#----------------------------------------------------------------------------------
--------------------------------------
import itertools
result = [Link](a, 3)
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte yazdırma işlemi yan yana yapılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
import itertools
#----------------------------------------------------------------------------------
--------------------------------------
itertools modülündeki combinations isimli fonksiyon permutations isimli
fonksiyonla aynı parametrik yapıya sahiptir.
Ancak bu fonksiyon kombinasyonları bize vermektedir.
#----------------------------------------------------------------------------------
--------------------------------------
import itertools
import itertools
#----------------------------------------------------------------------------------
--------------------------------------
Bilindiği gibi temel bellek birimi byte'tır. Bugünkü bilgisayar sistemlerinde
adresleme byte esasına göre yapılmaktadır.
Bu nedenle programcılar çeşitli biçimlerde bir grup byte'ı saklamak durumunda
kalabilirler. Bu yüzden programlamlama dillerinde
byte kavramını temsil eden türler bulundurulmuştur. İşte Python'da da byte
kavramını temsil etmek için "bytes" isimli bir
tür bulundurulmuştur.
Aslında byte kavramı 8 bitten oluşur. 8 bit de [0, 255] arasında bir syaı
belirtir. Ancak bute kavramını int türüyle temsil etmek verimsizdir.
Çünkü int türü sınırsız uzunlukta tamsayıları temsil etmek içimn düşünülmüştür.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir bytes nesnesini pratik bir biçimde oluşturabilmek için tek tırnak, iki
tırnak ya da üç tırnağa yapışık bir 'b' ya da
'B' harfi kullanmak gerekir. Örneğin:
>>> b = b'ankara'
>>> type(b)
<class 'bytes'>
>>> b = B'izmir'
>>> type(b)
<class 'bytes'>
bytes türü byte kavramını temsil etmekle birlikte bytes türünden nesneyi sanki
bir string'miş gibi yaratmak kişilerin
kafasını karıştırmaktadır. Biz bytes nesnesi oluştururken tırnak içerisindeki
karakterlerin ASCII tablosundaki sıra numaralarından
bu nesnenin sayısal değerini oluşturmuş oluruz. Örneğin:
b = b'a'
Biz aslında burada 'a' karakterinin ASCII tablosundaki sıra numarası olan 97
sayısına ilişkin bir bytes nesnesi oluşturmuş oluruz.
Ancak bir bytes nesnesini bu biçimde oluştururken tırnak içerisindeki
karakterlerin ASCII tablosunun ilk 128 karakterlerinden
oluşturulmuş olması gerekir. Örneğin:
>>> b = b'ağrı'
File "<stdin>", line 1
b = b'ağrı'
^
SyntaxError: bytes can only contain ASCII literal characters.
Byte kavramı [0, 255] arasında bir sayı belirttiğine göre biz de ancak ASCII
tablosunun ilk 128 karakterini bu biçimde kullanabildiğimize göre
o zaman geriş kalan byte değerlerini nasıl temsil ederiz? İşte tırnak
içerisinde \x ve yanına iki hex digit yani \xhh biçiminde bir
sentaks hh numaralı hex sayıya karşılık gelen byte'ı temsil etmektedir. İki hex
digit [0, 255] arası tüm sayıları ifade edebildiği için
bir byte'ın her değerini ifade edebilmektedir. Örneğin:
>>> b = b'\xFC\x17\x56'
>>> len(b)
3
>>> b
b'\xfc\x17V'
>>> b = b'\x168\x41\xFF'
>>> b
b'\x168A\xff'
>>> len(b)
4
>>
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir bytes nesnesini oluşturmanın diğer bir yolu bytes fonksiyonunu
kullanmaktadır. Bu fonksiyonu biz argümansız kullanırsak
boş bir bytes nesnesi elde ederiz. Eğer bu fonksiyona dolaşılabilir bir nesneyi
argüman olarak verirsek bu durumda
o dolaşılabilir nesnenin içerisindeki sayılardan bytes nesnesi oluşturulur.
Tabii bu durumda bizim dolaşılabilir nesne içeriside
[0, 255] arası sayılar bulundurmamız gerekir. Örneğin:
Örneğin:
Bir yazıyı byte'larına ayrıştırarak bytes nesnesi elde etmenin diğer bir yolu
da str sınıfının encode metonu kullanmaktadır.
Bu metot da encoding parametresi almaktadır. Ancak bu encoding girilmezse
default durumda 'utf-8' kullanılır. Örneğin:
Bazen yukarıdaki işlemin tersini yapmak da gerekebilir. Yani elimizde bir bytes
nesnesi vardır. Bu nesnenin içerisinde belli bir
encoding'e göre bir yazıyı belirten byte'lar bulunmaktadır. Biz de bu bytes
nesnesinden bir str nesnesi elde etmek isteyebiliriz.
Bunun için str fonksiyonu kullanılabilir. Fonksiyonun birinci parametresi bir
bytes nesnesi alırsa ikinci parametre de
encoding belirtmelidir. Örneğin:
>>> b = b'\x61\x62\x63'
>>> s = str(b, encoding='ascii')
>>> s
'abc'
Bir bytes nesnesinin içerisindeki byte'lardan bir string elde etmenin ikinci
yolu da bytes sınıfının decode metodunu kullanmaktadır.
Yine metot parametre olarak encoding bilgisini alacaktır. Örneğin:
>>> b = b'\x61\x62\x63'
>>> s = [Link](encoding='ascii')
>>> s
'abc'
#----------------------------------------------------------------------------------
--------------------------------------
40. Ders 12/09/2022 Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
bytes türü de "değiştirilemez (immutable)" bir türdür. Dolayısıyla biz bir
bytes nesnesinin içerisindeki yazıyı değiştiremeyiz.
Bu bakımdan bytes nesneleri str nesnelerine çok benzemektedir.
Örneğin:
>>> b = b'ankara'
>>> b
b'ankara'
>>> b[0] = 'x'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'bytes' object does not support item assignment
bytes sınıfının str sınıfında olan pek çok metodu bulunmaktadır. Örneğin:
split, strip, count, index, find gibi.
>>> b = b'ankara'
>>> b
b'ankara'
>>> b[2:4]
b'ka'
>>> b[:-2]
b'anka'
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da built-in bytearray isimli bir tür de vardır. Aslında bytes türü ile
bytearray türü birbirlerine çok benzemektedir.
Aralarındaki tek fark bytes türü "değiştirilemez (immutable)" olduğu halde
bytearray türünün "değiştirilebilir (mutable)"
olmasıdır. Bir bytearray nesnesi ancak bytearray fonksiyonu ile yaratılabilir.
String gibi başına bir şeyler getirilerek yaratılmaz.
>>> ba = bytearray(b'ankara')
>>> ba
bytearray(b'ankara')
#----------------------------------------------------------------------------------
--------------------------------------
bytearray türü değiştirilebilir olduğu için biz bir bytearray nesnesinin
içerisindeki byte'ları değiştirebiliriz. Zaten bytearray
türünün bytes türünden tek farkı budur. Örneğin:
>>> b = b'\x00\x01\x02'
>>> len(b)
3
>>> b
b'\x00\x01\x02'
>>> b[0] = 10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'bytes' object does not support item assignment
>>> ba = bytearray(b'\x00\x01\x02')
>>> ba
bytearray(b'\x00\x01\x02')
>>> len(ba)
3
>>> ba[0] = 10
>>> ba
bytearray(b'\n\x01\x02')
bytearray türü de bytes türü gibi str sınıfında olan pek çok metodu
içermektedir: Örneğin count, index, strip, split gibi.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
bytes türü değiştirilemez olduğu için yorumlayıcının bazı optimizasyonlarına
katılabilmektedir. Aynı zamanda bytes türü
yine değiştirilemez olduğu için toplamda bellekte daha az yer kaplamaktadır. Bu
nednele eğer byte dizisinin elemanlarını değiştirmeyecekseniz
bytes türünü, değiştirecekseniz bytearray türünü tercih etmelisiniz. Yine
değiştirilemez türlerin hash'lenebilir olduğunu ama değiştirilebilir
türlerin hash'lenebilir olmadığını anımsayınız. Bu nedenle örneğin bytes türü
bir sözlüğe anahtar yapılabilir ancak bytearray
türü yapılamaz. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Biz şimdiye kadar hep fonksiyonları kullanarak kodlar oluşturduk. Bir programın
fonksiyonlar yoluyla oluşturulmasına
"prosedürel programlama tekniği" denilmektedir. Sınıflar (classes) "Nesne
Yönelimli Programlama Tekniğini (NYPT)"
uygulamak için gereken yapı taşlarıdır. NYPT "sınıflar kullanarak program
yazma" tekniğidir. Yani işin içine sınıflar
girdiğinde artık NYPT'den bahsederiz.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sınıf tanımlamanın (sınıf oluşturmanın) genel biçimi şöyledir:
Örneğin:
class Sample:
pass
#----------------------------------------------------------------------------------
--------------------------------------
Su-ınıflar aynı zamanda birer tür de belirtmektedir. Oluşturduğumuz bir sınıf
türünden nesneler yaratabiliriz. Bir sınıf türünden
nesne yaratmanın genel biçimi şöyledir:
sınıf_ismi([argüman listesi])
Örneğin:
s = Sample()
s = str()
t = tuple()
a = list()
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
pass
s = Sample()
print(type(s)) # <class '__main__.Sample'>
#----------------------------------------------------------------------------------
--------------------------------------
NYPT'de bir sınıf türünden nesneye o sınıf türünden bir "örnek (instance)" da
denilmektedir. Yani "örnek (instance)"
sözcüğü sınıf türündne nesneler için kullanılmaktadır. Sınıflar elemanlara
sahip olabilir. Sınıf nesneleri (yani örnekler)
bileşik nesnelerdir. Yani parçalara sahiptir. Python'da bir sınıf nesnesinin
elemanlarına yani parçalarına "öznitelik (attribute)"
denilmektedir. (Halbuki sınıf elemanlarına C++'ta "veri elemanı (data member)",
Java ve C#'ta "alan (field)" denilmektedir.)
Eğer öznitelik bir nesnenin parçası durumunda ise buna "örnek özniteliği
(instance attribute)" da denilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir nesne yaratıldığında henüz nesnenin içi boştur. Örneğin:
class Sample:
pass
s = Sample()
Burada s değişkeninin gösterdiği yerdeki nesnenin içi henüz boştur. Yani s'in
henüz bir özniteliği yoktur.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıf türünden birden fazla nesne (yani örnek) yaratabiliriz. Örneğin:
class Sample:
pass
s = Sample()
k = Sample()
Burada s başka bir nesneyi k;'da başka bir nesneyi göstermektedir. Örneğin:
>>> s = Sample()
>>> k = Sample()
>>> id(s)
3104147128816
>>> id(k)
3104147434752
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıf nesnesi yaratıldığında boştur demiştik. İşte bir nesnenin
"öznitelikleri (attributes)" aşağıdaki sentaksla
yaratılmaktadır:
<değişken_ismi>.<öznitel_ismi> = değer
Örneğin:
s = Sample()
s.a = 10
s.b = 20
s.c = 'ankara'
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
C++, Java ve C# gibi dillerde bir sınıf türünden farklı nesnelerin elemanları
hep aynı isimli ve aynı türden olur. Ancak
Python'da böyle olmak zorunda değildir. Örneğin Sample türünden iki sınıf
nesnesi olsun. Bunların elemanları yani öznitelikleri
farklı olabilir:
class Sample:
pass
s = Sample()
k = Sample()
s.a = 10
s.b = 20
k.x = 'ali'
k.y = 12.3
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıf nesnesi için öznitelik yaratıldıktan sonra bu öznitelik istenildiği
zaman <değişken_ismi>.<öznitelik_ismi>
sentaksıyla kullanılabilmektedir. Nesnenin özniteliklerini "nesnenin
içerisindeki değişkenler" gibi düşünebilirsiniz.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
pass
s = Sample()
s.a = 10
s.b = 'ankara'
print(s.a) # 10
print(s.b) # ankara
#----------------------------------------------------------------------------------
--------------------------------------
Python'da sınıfların içerisinde fonksiynlar olabilir. Sınıfların içerisindeki
fonksiyonlara "metot (method)" denilmektedir.
Eğer fonksiyon sınıfların dışındaysa ona "fonksiyon", bir sınıfın içerisindeyse
ona "metot" denilmektedir.
class Sample:
def foo(self):
pass
def zar():
pass
Burada foo, bar ve tar Sample sınıfının metotlarıdır. zar şse sınıf içerisinde
değildir. O halde zar bir fonksiyondur, metot değildir.
Bir metodun çağrılması için o metodun içinde bulunduğu sınıf türünden bir
değişkenin olması gerekir. Metot çağırma işleminin genel
biçimi şöyledir:
<değişken_ismi>.<metot_ismi>([argüman listesi])
Örneğin:
s = Sample()
[Link]()
[Link](10)
[Link](10, 20)
s = Sample()
[Link](10, 20)
class Sample:
def foo(self):
print('foo')
def zar():
print('zar')
s = Sample()
[Link]()
[Link](10)
[Link](10, 20)
zar()
#----------------------------------------------------------------------------------
--------------------------------------
Metotların en azından bir parametresi olmak zorunda olduğunu belirtmiştik. Bu
parametrenin de genellikle "self"
biçiminde isimlendirildiğini söylemiştik. Pekiyi bu self parametresinin anlamı
nedir? İşte metotlar ilgili sınıf türünden değişkenlerle
çağrılırlar. Her zaman self parametresine metodun çağrılmasında kullanılan
değişken (yani onun içindeki adres) atanmaktadır.
Başka bir deyişle self metodun çağrılmasında kullanılan nesneyi belirtmektedir.
Örneğin:
class Sample:
def foo(self, a, b):
pass
s = Sample()
[Link](10, 20)
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıf türünden değişken başka bir değişkene atanabilir. Bu durumda iki
değişken de aynı nesneyi gösterir. Nesneye
hangi değişkenle erişildiğinin bir önemi yoktur. Örneğin:
s = Sample()
s.a = 10
k = s
class Sample:
pass
s = Sample()
s.a = 10
k = s
print(s.a)
print(k.a)
print(id(s)) # 1781626597584
print(id(k)) # 1781626597584
print(id(s.a)) # 1781443422800
print(id(k.a)) # 1781443422800
#----------------------------------------------------------------------------------
--------------------------------------
Bir metot çağrılırken her zaman metodun çağrılmasında kullanılan değişken (yani
onun içerisindeki adres) metodun birinci parametresi
olan self parametresine atanktadır. Bu durumda self parametresi metodun
çağrılmasında kullanılan değişken ile aynı nesneyi gösterir durumda olur.
Metotlar ilgili sınıf türünden bir değişkenle rçağrıldığına göre o değişkenin
atanacağı metotta bir self parametresinin bulunması gerekmektedir.
Aşağıdaki örnekte self parametresi ile s aynı nesneyi göstermektedir.
Dolayısıyla nesnenin a özniteliğine self parametresiyle de
erişilebilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def foo(self):
print(self.a) # 10
s = Sample()
s.a = 10
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki, işlemin tersini de yapabiliriz. Yani metodun içerisinde nesenin
öznitelikleri yaratılabilir. Yaratılan bu öznitelikler
daha sonra kullanılabilir.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def foo(self):
self.a = 10
self.b = 20
s = Sample()
[Link]()
print(s.a, s.b) # 10 20
#----------------------------------------------------------------------------------
--------------------------------------
41. Ders 14/09/2022
Carsamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da başında ve sonunda iki alt tire olan özel bazı isimler
kullanılmaktadır. Örneğin __init__, __add__ gibi.
Bu isimleri kolay ifade edebilmek için __xxx__ için "dunderscore xxx" ya da
"dunder xxx" ifadeleri kullanılmaktadır. Yani örneğin biz
"dunder init" demekle "__init__" ismini kastedeceğiz.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da sınıfların __init__ isimli özel bir metotları vardır. Bu metotlara
"initializer" ya da bazen "constructor" da
denilmektedir. __init__ metodu bir sınıf türünden nesne yaratıldığında otomatik
olarak çağrılan bir metottur.
class Sample:
def __init__(self):
print('__init__ called')
s = Sample()
print('Ok')
k = Sample()
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıf türünden nesne yaratıldığında önce nesne yaratılır. Sonra sınıfın
__init__ metodu çağrılır. Ondan sonra atama işlemi yapılır. Örneğin:
s = Sample()
class Sample:
def __init__(self):
self.a = 10
self.b = 20
s = Sample()
k = Sample()
print(s.a, s.b)
print(k.a, k.b)
#----------------------------------------------------------------------------------
--------------------------------------
__init__ metodunun self dışında başka parametreleri de olabilir. Bu durumda
nesne yaraılırken bu parametreler için de argüman
girilmelidir. Örneğin:
class Sample:
def __init__(self, a, b):
self.a = a
self.b = b
s = Sample(10, 20)
class Sample:
def __init__(self, a, b):
self.a = a
self.b = b
s = Sample(10, 20)
print(s.a, s.b) # 10 20
k = Sample(30, 40)
print(k.a, k.b) # 30 40
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte sınıfın __init__ metodunda a ve b isimli iki örnek özniteliği
yaratılmıştır. disp metodunda da bunlar
ekrana yazdırılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self, a, b):
self.a = a
self.b = b
def disp(self):
print(self.a, self.b)
s = Sample(10, 20)
k = Sample(30, 40)
[Link]() # 10 20
[Link]() # 30 40
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi bir sınıf nedir? Aslında sınıf örnek özniteliklerinden ve metotlardan
oluşan bir veri yapısıdır. Sınıflar belli bir konuda
işlemleri yapan metotlara sahiptirler. Bu metotlar da nesnenin özniteliklerini
ortak bir biçimde kullanmaktadır. NYPT'de
bir konuda bir işi yapabilecek sınıflar yazılır ve program sınıflar yoluyla
oluşturulur.
şağıdaki örnekte Date sınıfı bir tarh bilgisini tutup onu ekrana yazdıran
metoda sahiptir.
#----------------------------------------------------------------------------------
--------------------------------------
class Date:
def __init__(self, day, month, year):
[Link] = day
[Link] = month
[Link] = year
def disp(self):
print(f'{[Link]}/{[Link]}/{[Link]}')
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte Complex sınıfı bir karmaşık sayıyı temsil etmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
class Complex:
def __init__(self, real, imag):
[Link] = real
[Link] = imag
def disp(self):
print(f'{[Link]}+{[Link]}i')
z = Complex(3, 2)
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıf bi konuyla ilgili belli işlemleri yapmak amacıyla yazılır. Biz sınıf
nesnesini yaratırken __init__ metodunda
onu argümanlar geçeriz. __init__ metodu bunları nesnenin özniteliklerinde
saklar. Diğer metotlar da self yoluyla bunlara erişierek
faydalı işlemler yaparlar.
#----------------------------------------------------------------------------------
--------------------------------------
import datetime
d = [Link](2022, 9, 14)
print(d)
print([Link], [Link], [Link])
#----------------------------------------------------------------------------------
--------------------------------------
Şimdi karmaşık sayıyı temsil eden Complex sınıfına add ve sub metotlarını
ekleyelim.
#----------------------------------------------------------------------------------
--------------------------------------
class Complex:
def __init__(self, real, imag):
[Link] = real
[Link] = imag
def disp(self):
print(f'{[Link]}+{[Link]}i')
return result
return result
x = Complex(6, 5)
y = Complex(2, 3)
z = [Link](y)
[Link]()
z = [Link](y)
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Aslında daha önce görmüş olduğumuz list, tuple, set, dict birer sınıftır. Zaten
biz daha önce bu sınıflar türünden nesneler yaratıp
onların metotlarını çağırarak faydalı işlemler yapmıştık.
#----------------------------------------------------------------------------------
--------------------------------------
a = [1, 2, 3, 4, 5]
print(a)
[Link](2)
print(a)
[Link](100)
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi bir metodun ilgili sınıf türünden bir değişkenle çağrılmasının anlamı
nedir? İşte örneğin [Link]() gibi bir çağrı
foo metodunun x değişkeninin gösterdiği yerdeki nesne üzerinde (kısaca buna x
nesnesi üzerinde) işlem yapacağı anlamına gelir.
Yani metotlar spesifik bir nesne üzerinde işlem yaparlar. Bu nedenle bir nesne
ile çağrılırlar. Yani biz bir metodu hangi nesneyle çağırırsak
o metot aslında o nesnenin elemanları yani öznitelikleri üzerinde işlem yapar.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da "function overloading" ya da "method overloading" denilen özellik
yoktur. Yani aynı isimli tek bir fonksiyon ya da
metot bulunabilir. Fakat istersek metotlarımızın parametre değişkenlerine
default değerler verebiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
class Complex:
def __init__(self, real = 0, imag = 0):
[Link] = real
[Link] = imag
def disp(self):
print(f'{[Link]}', end='')
if [Link] != 0:
print(f' + {[Link]}i')
else:
print()
x = Complex(6, 5)
y = Complex(6)
z = Complex()
[Link]() # 6+5i
[Link]() # 6
[Link]() # 0
#----------------------------------------------------------------------------------
--------------------------------------
Sınıflar "değiştirilebilir (mutable)" türlerdir. Yani bir sınıfın örnek
özniteliklerini biz değiştirebiliriz. Örneğin:
class Sample:
pass
s = Sample()
s.a = 10
s.b = 20
s.a = 30 # s.a'yı değiştirdik
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfların metotları alternatif biçimde sınıf ismi ile de çağrılabilmektedir.
Ancak bu çağrı biçiminde self parametresini
açıkça argüman olarak geçirmek gerekir. Örneğin:
class Sample:
def foo(self):
print('foo')
s = Sample()
[Link]()
[Link](10)
[Link](s)
[Link](s, 10)
class Sample:
def foo(self):
print('foo')
s = Sample()
[Link]()
[Link](10)
[Link](s)
[Link](s, 10)
#----------------------------------------------------------------------------------
--------------------------------------
Tabii alternatif sentaksı biz diğer built-inm sınıflarda da kullanabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
a = [1, 2, 3, 4, 5]
print(a)
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
Biz "bir nesnenenin öznteliği" demekle nesnenin elemanlarını kastetmekteyiz.
"Nesnenin özniteliği" ile "sınıfın örnek özniteliği"
aynı anlamdadır. Nesnenin özniteliklerine İngilizce "instance attribute"
denilmektedir. Ancak Pyton'da bir "sınıf öznitelikleri (class attribute)"
denilen bir kavram vardır. Biz "sınıf özniteliği" yerine bazen "sınıf
değişkenleri" de diyeceğiz.
def foo():
y = 20 # y yerel bir değişken
class Sample:
z = 30 # z sınıf değişkeni (sınıfın özniteliği)
s = Sample()
s.a = 10 # a örnek özniteliği
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıfın öznitelikleri sınıfın içerisinde doğrudan ismiyle kullanılabilir.
Ancak sınıfın dışından ve sınıfın metotları içerisinden
ancak sınıf ismi ve nokta operatörü ile niteliklendirilerek
kullanılabilmektedir. Örneğin:
class Sample:
x = 10
def foo():
print(Sample.x) # metot içerisinden x'i doğrudan kullanamayız,
sınıf ismiyle kullanmalıyız
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aslında Python'da sınıflar birer deyim statüsündedir. Yani sınıfların
içerisindeki kodlar da çalıştırılmaktadır.
Örneğin:
class Sample:
print('Sample') # Sample
x = 10
print(x) # 10
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi fonksiyon tanımlama işlemi de aslında Python'da bir deyim
statüsündeydi. Yorumlayıcı bir fonksiyonun
tanımlandığını gördüğünde bir fonksiyon nesnesi yaratıyordu. Fonksiyonun iç
kodlarını o nesnenin içerisine yerleştiriyordu.
Sonra da bu nesnenin adresini fonksiyon ismi olan değişkene atıyordu.
class Sample:
x = 10
def foo(self):
pass
Sample.x = 20 # geçerli
s = Sample()
[Link](s) # geçerli
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
x = 10
def foo(self):
pass
#----------------------------------------------------------------------------------
--------------------------------------
Aslında Python'da sınıfın öznitelikleri (yani sınıf değişkenleri) dışarıdan
sınıf ismiyle kullanılabildiği gibi o sınıf türünden
bir değişkenle de kullanılabilmektedir. Sınıf değişkenlerinin sınıf türünden
değişkenlerle kullanılması yanlış anlaşılmalara yol açtığı
için pek tavsiye edilmemektedir. Örneğin:
class Sample:
x = 10
def foo(self):
pass
s = Sample()
[Link]()
[Link](s)
class Sample:
def __init__(self):
self.x = 100
x = 10
def foo(self):
pass
s = Sample()
print(s.x) # 100
print(Sample.x) # 10
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
[Link] 21/09/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfların birer deyim statüsünde olduğunu daha önce belirtmiştik. Python
yorumlayıcısı bir sınıf ile karşılaştığında
type isimli bir sınıf türünden nesne yaratır. Sınıfın bilgilerini bu nesnenin
içerisine yerleştirir. Bu nesnenin adresini de
sınıf ismi ile belirtilen değişkene atar. Yani sınıf isimleri aslında sıradan
birer değişkendir. Sınıf isimleri type türünden bir nesnesyi
gösterirler. Bu nesnenin içerisinde de sınıfın bilgileri vardır. Ancak sınıflar
türünden de nesneler yaratılabilmektedir. Sınıflar türünden
yaratılan nesneler o sınıf türünden olur. Örneğin:
class Sample:
pass
s = Sample()
print(type(s)) # <class '__main__.Sample'>
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
pass
s = Sample()
print(type(s)) # <class '__main__.Sample'>
#----------------------------------------------------------------------------------
--------------------------------------
type sınıfı gibi bir sınıfın bilgilerini tutan sınıflara Python dünyasında
"meta sınıflar (meta classes)" denilmektedir.
type bir meta sınıftır. Aslında biz de hiç class bildirimi yapmadan doğrudan
type sınıfının __init__ metodu yoluyla bir sınıf
oluşturabiliriz. Örneğin:
class Sample:
pass
Aslında bir değişkenin türünü elde etmek için kullandığımız type fonksiyonu da
aynı fonksiyondur. type fonksiyonun
tek parametreli özel kullanımı yeni bir type nesnesi yaratmaz bize değişkenin
ilişkin olduğu sınıfın type nesnesini verir.
class Sample:
pass
Mample = Sample
Burada Sample değişkeni ile Mample değişkeni aynı type nesnesini görmektedir. O
halde biz her iki ismi de aynı sınıf türünden nesne
yaratmakta kullanabiliriz. Örneğin:
x = Sample()
y = Mample()
print(type(x)) # __main__.Sample
print(type(y)) # __main__.Sample
class Sample:
pass
s = Sample()
X = type(s)
x = X()
y = Sample()
Başka bir deyişle type(x) gibi bir çağrıda aslında type fonksiyonu x değişkenin
ilişkin olduğu sınıfın type nesne adresini
bize vermektedir.
Python'da meta sınıflar nispeten ileri bir konudur. Biz kursumuzda meta
sınıfları uygulama konuları içerisinde ele alacağız.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
O halde bir sınıf nedir? Bir sınıf "belli bir konuda işlemler yapan metotlardan
ve bu metotların ortak kullandığı nesne
özniteliklerinden oluşan bir veri yapısıdır." Sınıflar sayesinde birbirlerinden
kopu izlenim veren fonksiyonlar mantıksal bir
bağ içerisinde bir araya getirilmiş ve algısal kolaylıklar sağlanmıştır.
Prosedürel teknikte her şey fonksiyonlarla yapılmaktadır.
Elimizde sanki birbirlerinden bağımsız olan pek çok fonksiyon var gibidir.
Nesne yönelimli teknikte bir konuya ilişkin fonksiyonlar
metot adı altında bir sınıfın içerisine yerleştirilir. Böylece "çok fazla
fonksiyon var" duygusu yerine "şu sınıf bunu yapar,
bu sınıf şunu yapar" biçiminde algısal kolaylık sağlanmış olur.
class Worker:
def __init__(self, name, department, shift):
[Link] = name
[Link] = department
[Link] = shift
def disp(self):
print([Link], [Link], [Link])
[Link]()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Aslında Python'da sınıflar minimalist biçimde tasarlanmıştır. Yani C++, Java ve
C# gibi dillere kıyasla Python'daki sınıfların
çok detayları yoktur. Bu nedenle Pyton'daki sınıfsal özellikleri C++, Java ve
C# gibi dillerle karşılaştırmamak gerekir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Ağaç, kedi, okul, öğrenci gibi sözcüklerin hepsi birer kavram belirtmektedir.
Kavramlar gerçek nesneler değildir. Bir grup nesnenin
ortak özelliklerinden hareketle uyduurlmuş zihinsel temsillerdir. Aslında
dünyada ağaç diye bir şey yoktur. Somut ağaçlar vardır.
Ağaç bizim biribirine benzeyen bir grup nesne için uydurduğumuz bir temsildir.
Ağaç bir kavramdır ancak evimizin önündeki ağaç
o kavramdan bir örnektir (instance). İşte NYPT'de sınıflar kavramları belirtir.
Sınıflar türünden nesneler de gerçek nesneleri belirtir.
O halde bir proje nesne yönelimli olarak modellenecekse önce proje içerisindeki
bütün kavramlar sınıflarla temsil edilmeli ve sonra da
bu sınıflar türündne nesneler yaratılarak örnekler oluşturulmalıdır. Örneğin
bir hastane otomasyonu için gerekli kavramların bazıları şunlardır:
Hastane, doktor, hasta, hastalık, hasta oadaları, hemşire, ilaç. İşte bizim bu
sınıfları yazmamız gerekir. Sonra örneğin hastanemizde 10 tane
doktor varsa Doktor sınıfından 10 tane nesne yaratmalıyız. 5 tane hemşire için
Hemşire sınıfından 5 nesne yaratırız. Programı nesnleer ile
bu sınıfların metotlarını çağırarak yazarız.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Tabii bir projedeki sınıflar arasında da birtakım ilişkiler söz konusu
olmaktadır. Örneğin Hastane sınıfı ile Doktor sınıfı arasında
bir ilişki vardır. Doktor ile Hasta sınıfları arasında da bir ilişki vardır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıf türünden bir nesne başka bir sınıf türünden bir nesnenin bir
parçasını oluşturuyorsa bu iki sınıf arasında
"içerme (composition)" ilişkisi vardır. Örneğin "insan" ile "karaciğer"
sınıfları arasında, "araba" ile "motor" sınıfları
arasında bu biçimde içerme ilişkisi vardır. İki sınıf arasında içerme ilişkisi
olması için aşağıdaki iki özelliğin ikisin de sağlanması gerekir:
UML (Unfied Modeling Language) bir projeyi nesne yönelimli olarak modelleme
kiçin kullanılan diyagramlardan oluşan bir dildir.
Diyagramlardan biri de "sınıf diyagramları (class diagrams)" denilen
diyagramlardır. Burada projedeki sınıflar ve onların arasındaki ilişkiler
betimlenmektedir. UML sınıf diyagramlarına içerme ilişkisi "içeren sınıf
tarafında içi dolu bir baklavacık (diamond)" ,le temsil edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da içerme ilişkisi içeren sınıfın __init__ metodunda içerilen nesnenin
yaratılarak içeren sınıfın örnek özniteliğinde tutulması yoluyla sağlanabilir.
Örneğin:
class Araba:
def __init__(self):
[Link] = Motor()
#...
class Motor:
pass
a = Araba()
class Board:
def __init__(self):
[Link] = [[Square() for _ in range(8)] for _ in range(8)]
class Square:
pass
board = Board()
Burada Board sınıfı Square sınıfını içermektedir. Ancak 1 Board nesnesi 64 tane
Square nesnesini içerir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Birleşme ilişkisinde (aggregation) bir sınıf türünden nesne başka bir sınıf
türünden nesneyi kullanmaktadır. Ancak kullanılan nesne
tek bir nesne tarafından kullanılmak zorunda değildir. Kullanan nesneyle
kullanılan nesnenin ömürleri de aynı olmak zorunda değildir.
Bu nesnelerin sınıfları arasında "birleşme" ilişkisi vardır. Aslında genel
olarak "içerme" ilişkisine benzeyen ancak
içerme ilişkisi olmayan iişkiler "birleşme ilişkisidir. Örneğin "hstane" ile
"doktor" sınıfları arasında, "bilgisayar" ile "fare" sınıfları
arasında "oda" ile "duvar" sınıfları arasında içerme ilişkisi yoktur, birleşme
ilişkisi vardır.
UML sınıf diyagramlarında birleşme ilişkisi "kullanan sınıf tarafından içi boş
bir baklavacık (diamond)" ile temsil edilmektedir.
Yine birleşme ilişkisi 1'e 1 olabileceği gibi 1'e N olabilmektedir. Birleşme
ilişkisine İngilizce "holds a" ilişkisi de denilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da birleşme ilişkisi kullanan sınıfta kullanılan nesneyi geçici olarak
tutma yoluyla sağlanabilir. Yani birleşme ilişkisinde
kullanılacak nesnenin kulalna nesneye dahil edilmesi ve çıkartılması gibi
işlemler söz konusu olmaktadır. Örneğin:
class Hospital:
def __init__(self, name):
[Link] = name
[Link] = []
class Doctor:
def __init__(self, name, specialty):
[Link] = name
[Link] = specialty
h1 = Hospital('A')
h2 = Hospital('B')
#...
d1 = Doctor('Ali', 'Kulak Burun')
#...
h1.add_doctor(d1)
#...
h2.add_doctor(d1)
Burada bir Hospital nesnesi birden fazla Doctor nesnesini kullanabildiği gibi
bir Doctor nesnesi de birden fazla hastane tarafından
kullanılabilmektedir. Hospital nesnesinin ve Doctor nesnelerinin ömürlerinin
farklı olabildiğine dikkat ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
43. Ders 26/09/2022 Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sınıflar arasındaki diğer bir ilişki biçimi de "türetme" ilişkisidir. Türetme
ilişkisine İngilizce "inheritance" yani
"kalıtım" da denilmektedir. Türetme mevcut bir sınıfa dokunmadan onu genişletme
anlamına gelir. Burada asıl sınıfa "taban sınıf (base class)",
genişletilmiş olan sınıfa da "türemiş sınıf (derived class)" denilmektedir.
Türetmede türemiş sınıf tamamen taban sınıf gibi de işlem görür
ancak fazlalıkları da vardır. UML sınıf diyagramlarında türetme ilişkisi
türemiş sınıftan taban sınıfa doğru çekilen içi boş
bir okla gösterilmektedir. Türem,ş sınıftan yeniden türetme yapılabilir.
Böylece bir dizi türetme söz konusu olabilir.
Bir sınıfın birden fazla fazla sınıfa taban sınıflık yapması tamamen normal bir
durumdur. Ancak bir sınıfın birden fazla taban sınıfa
sahip olması durumu özel bir durumdur. Buna NYPT'de "çoklu türetme (multiple
inheritance)" denilmektedir. Doğada çoklu türetme az olmasına karşın
karşılaşılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sınıflararası son ilişki biçimine "çağrışım ilişkisi (association)"
denilmektedir. Çağrışım ilişkisinde bri sınıf nesnesi
başka bir nesneyi kullanır. Ancak bu kullanma yüzeyseldir. Bünyesine katarak
bir kullanma değildir. Örneğin Taksi sınıfı Müşteriyi kullanır.
Ancak Taksi ile Şoför arasındaki ilişki birleşme ilişkisiyken Taksi ile Müşteri
arasındaki ilişki çağrışım ilişkisidir.
Çağrışım ilişkisi UML sınıf diyagramlarında kullanan sınıftan kullanılan sınıfa
çekilen ince bir ok ile temsil edilir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da türetme sentaksının genel biçimi şöyledir:
Örneğin:
class A:
pass
class B(A):
pass
#----------------------------------------------------------------------------------
--------------------------------------
class A:
def foo(self):
print('[Link]')
def bar(self):
print('[Link]')
class B(A):
def tar(self):
print('[Link]')
b = B()
[Link]()
[Link]()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Türetme ilişkisinde türemiş sınıf taban sınıfı kullanmaktadır. Taban sınıf
türemiş sınıfı kullanmamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi nesnelerin öznitelikleri tipik olarak __init__ metotlarında
yaratılmaktadır. Türemişl sınıf türünden
bir nesne yaratıldığında türemiş sınıfın __init__ metodu çağrılır. Bu durumda
bu türemiş sınıf türünden nesneyle taban sınıf
metodu çağrıldığında taban sınıf metodu taban sınıfın __init__ metodunda
yaratılacak olan öznitelikleri kullanıyorsa sorun oluşacaktır.
Çünkü bu öznitelikler henüz yaratılmamış olacaktır. Örneğin:
class A:
def __init__(self):
self.a = 10
def dispA(self):
print(self.a)
class B(A):
def __init__(self):
self.b = 20
a = A()
[Link]() # sorun yok
b = B()
[Link]() # sorun var! Nesnenin a özniteliği yaratılmamış!
class A:
def __init__(self):
self.a = 10
def dispA(self):
print(self.a)
class B(A):
def __init__(self):
self.__init__() # dikkat metot kendi kendisini çağırıyor!
self.b = 20
b = B()
[Link]()
class A:
def __init__(self):
self.a = 10
def dispA(self):
print(self.a)
class B(A):
def __init__(self):
A.__init__(self) # açıkça A'nın __init__ metodunun çağrıldığı
anlaşılıyor
self.b = 20
b = B()
[Link]() # geçerli, sorun yok
Pekiyi türemiş sınıfın __init__ metodu taban sınıfın __init__ metdounu nerede
çağırmalıdır? İşte en normal olan ve uygun durum türemiş sınıfın __init__
metodunun taban sınıfın __init__ metodunu hemen türemiş sınıfın __init__
metdounun başında çağırmasıdır. Çünkü önce nesnenin taban sınıf kısmının
ilkdeğer alması normal olan durumdur. Ne de olsa türemiş sınıf taban sınıfı
kullanabilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıfın "taban sınıfları (base classes)" denildiğinde onun yukarıya doğru
tüm taban sınıfları anlaşılır. Ancak sınıfın
"doğrudan taban sınıfları (direct base classes)" sınıfın hemen bir
yukarısındakiş taban sınıflar anlaşılır. Sınıfın
"dloaylı taban sınıfları (indirect base classes)" sınıfın doğurdan taban
sınıflarının taban sınıflarıdır. Örneğin aşağıdaki
gibi bir türetme şeması olsun:
A
B
C
D
Burada D'nin taban sınıfları C, B ve A'dır. D'nin doğrudan taban sınıfı C'dir.
D'nin dolaylı taban sınıfları A ve B'dir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Türemiş sınıfın taban sınıfn __init__ metodunu çağırması gerektiğini
belirtmiştik. İşte türemiş sınıflar tüm btaban sınıfların __init__
metotlarını çağırmamalıdır. Yalnızca doğrudan taban sınıfların __init__
metotlarını çağırmalıdırlar. Zaten her sınıf kendi doğrudan
taban sınıflarının __init__ metotlarını çağırınca tüm taban sınıfların __init
metotları çağrılmış olur. Örneğin:
class A:
def __init__(self):
pass
class B(A):
def __init__(self):
A.__init__(self)
pass
class C(B):
def __init__(self):
B.__init__(self)
pass
c = C()
#----------------------------------------------------------------------------------
--------------------------------------
Taban sınıfla türemiş sınıf aynı isimli metotlara sahip olabilir. Bu durumda bu
metotlar hangi sınıf türünden nesneyle çağrılmışsa
o sınıfın aynı isimli metodu çağrılmış olur. Örneğin:
class A:
def foo(self):
print('[Link]')
class B(A):
def foo(self):
print('[Link]')
b = B()
[Link]() # b nesnesi B sınıfı türündne olduğu için [Link] çağrılır
a = A()
[Link]() # [Link] çağrılır, zaten taban sınıf türemiş sınıfa erişemez!
Yabii türemiş sınıfın bir metodunda taban sınııfn aynı isimli metodu
çağrılacaksa burada açıkça taban sınıf ismi belirtilmelidir.
Örneğin:
class A:
def foo(self):
print('[Link]')
class B(A):
def foo(self):
[Link](self) # açıkça belirtilmeli
print('[Link]')
b = B()
[Link]() # b nesnesi B sınıfı türündne olduğu için [Link] çağrılır
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıda da belirtildiği gibi Python'da çoklu türetme vardır. Ancak Java ve C#
gibi bazı dillerde çok türetme yapılamamaktadır.
C++'ta ve Object Pascal'da da çoklu türetme bulunmaktadır. Çoklu türetmede
türemiş sınıf tüm taban sınıflarının metotlarını
kullanabilir.
#----------------------------------------------------------------------------------
--------------------------------------
class A:
def foo(self):
print('[Link]')
class B:
def bar(self):
print('[Link]')
c = C()
[Link]()
[Link]()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi türemiş sınıfn __init__ metodunun kendi doğrudan taban
sınıflarının __init__ metotlarını çağırması gerekiyordu. Çoklu türetmede
türemiş sınıf bütün taban sınıflarının __init__ metotlarını çağırmalıdır.
Örneğin:
class A:
def __init__(self):
self.a = 10
def dispA(self):
print(self.a)
class B:
def __init__(self):
self.b = 10
def dispB(self):
print(self.b)
def dispC(self):
print(self.c)
c = C()
[Link]()
[Link]()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Çoklu türetmede baklava (diamond) biçiminde türetme şemasında her sınıfın kendi
doğrudan taban sınıflarının __init__metotlarını çağırması önemli bir soruna yol
açmaktadır. Aşağıda baklava biçiminde bir türetme şeması görüyorsunuz:
A
B C
D
Burada her sınıfın __init__ metodu kendi doğrudan taban sınıfının __init__
metodunu çağırdığında A sınıfının __init__ metodu
iki kez çağrılmışç olur. Bir sınıfın __init__ metodunun iki kez çağrılması
bazen sorunlarlara yol açmayabilir ancak bazen sorunlara
yol açabilir. Örneğin C++'ta burada A sınıfının constructor'ının iki kez
çağrılmasının engellenmesi için "virtual base class" denilen
bir kavram uydurulmuştur.
class A:
def __init__(self):
print('A.__init__')
class B(A):
def __init__(self):
A.__init__(self)
print('B.__init__')
class C(A):
def __init__(self):
A.__init__(self)
print('C.__init__')
d = D()
#----------------------------------------------------------------------------------
--------------------------------------
44. Ders 28/09/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Karmaşık türetme durumlarında taban sınıflarda aynı isimli elemanlar varsa isim
araması kabaca aşağıdan yukarıya doğru yapılmaktadır.
Ancak çoklu türetme söz konusu olduğunda bu durum o kadar açık olmayabilir.
Örneğin:
A B
C
D
Böyle bir türetme şemasında aşağıdakiş gibi bir foo metodu çağrılmış olsun:
d = D()
[Link]()
A
B C
D
E
Burada [Link]() çağrısı yapıldığını düşünelim. E'de ve D'de foo olmasaydı foo
hangi sırada ve nerede aranacaktı?
class A:
pass
class B(A):
pass
class C:
pass
class E(D):
pass
print(E.__mro__)
#----------------------------------------------------------------------------------
--------------------------------------
MRO sırası inspect modülündeki getmro fonksiyonuyla da elde edilebilir. Bu
fonksiyona biz bir sınıf ismini (yani tytpe nesnesini veririz). Örneğin:
import inspect
t = [Link](E)
print(t)
def getmro(cls):
return cls.__mro__
Genel olarak MRO sırası şu biçimdedir: Eğer çoklu türetme yoksa aşağıdan
yukarıya doğrudur. Çoklu türemte varsa soldan sağa taban sınıflarda
bir kol bitirildikten sonra diğerine geçilecek biçimde bir sıra izlenir.
Örneğin:
A
B D
C E
F
G
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
MRO sırası isim aramasında etkili olmaktadır. Bir sınıf türünden değişkenle ya
da sınıf ismi kullanılarak nokta operatörü ile
bir eleman belirtilirse bu elemanın sırasıyla türetme durumunda hangi
sınıflarda aranacağına MRO sırası denilmektedir. Tabii isim
bulunursa arama devam etmez. Örneğin:
A
B C
D
E
e = E()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Tabii MRO sırasında asla bir sınıftan iki tane bulunmamaktadır? Pekiyi baklava
şekline ilişkin türetmede MRO sırası nasıl olacaktır?
Örneğin:
A
B C
D
#----------------------------------------------------------------------------------
--------------------------------------
class A:
pass
class B(A):
pass
class C(A):
pass
print(D.__mro__)
#----------------------------------------------------------------------------------
--------------------------------------
Python'da "her şey bir çeşit nesne" olduğuna göre tüm sınıflar aslında doğrudan
ya da dolaylı olarak object denilen bir sınıftan
türetilmiş durumdadır. Biz bir sınıfta taban sınıf belirtmezsek Python
yorumlayıcısı onun object sınıftan türetildiğini varsayar.
Örneğin:
class Sample:
pass
Örneğin:
class A:
pass
class B(A):
pass
#----------------------------------------------------------------------------------
--------------------------------------
super isimli fonksiyon built-in bir fonksiyondur. super fonksiyonu aslında
baklava şeklindeki türetmelerde __init__
metotlarının aynı sınıf için ikinci kez çağrılmasını önlemek amacıyla
düşünülmüştür.
d = D()
super(D, d).foo()
Burada aslında oluşan etki [Link]() gibi bir etkidir. Ancak bu etki
oluşturulurken isim araması d değişkenin ilşkin olduğu sınıfın MRO sırasnda
D sınıfından sonraki sınıftan başlatılmaktadır. Örneğin d değişkeninin ilişkin
olduğiu sınıfın MRO sırası şöyle olsun: D, B, C, A, object. Burada super(D,
d).foo() çağrısında arama B sınıfından başlatılacaktır.
class A:
def foo(self):
print('[Link]')
class B(A):
def foo(self):
print('[Link]')
b = B()
[Link]() # [Link]
#----------------------------------------------------------------------------------
--------------------------------------
super fonksiyonu ne işe yaramaktadır? Kabaca elimizde bir sınıf türünden bir
değişken varsa biz de bu değişkenle kendi sınıfımızın değil
taban sınıfın metodunu çağırmak istiyorsak super fonksiyonu kullanırız.
Örneğin:
class A:
def foo(self):
print('[Link]')
class B(A):
def foo(self):
print('[Link]')
b = B()
Şimdi biz [Link]() biçiminde bir çağrı uyaparsak B sınıfının foo metodu
çağrılır. Pekiyi biz B sınıfını bypass ederek A
sınıfının foo metodunu nasıl çağırmalıyız. Yöntemlerden biri şöyle olabilir:
[Link](b)
Burada açıkça A sınıfının foo metodu b ile çağrılmıştır. Aynı şeyi super
fonksiyonu ile de yapabilirdik:
super(B, b).foo()
class A:
def foo(self):
print('[Link]')
class B(A):
def foo(self):
print('[Link]')
class C(B):
def foo(self):
print('[Link]')
c = C()
#----------------------------------------------------------------------------------
--------------------------------------
class A:
def foo(self):
print('[Link]')
class B(A):
def foo(self):
print('[Link]')
class C(B):
def foo(self):
print('[Link]')
c = C()
[Link]() # [Link]
super(C, c).foo() # [Link]
super(B, c).foo() # [Link]
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi mademki aslında taban sınıf ismini belirterek taban sınıfını
çağırabiliyoruz bu durumda super fonksiyonuna ne gerek vardır? Örneğin:
class A:
def foo(self):
print('[Link]')
class B(A):
def foo(self):
print('[Link]')
class C(B):
def foo(self):
print('[Link]')
c = C()
[Link](c) # [Link]
[Link](c) # [Link]
İşte bu biçimde taban sınıf ismi ile çağrı yapıldığında MRO sırası dikkate
alınmamaktadır. super fonksiyonu MRO sırasını dikkate
aldığı için önemli bir işleve sahiptir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aslında super fonksiyonun en önemli faydası baklava şeklinde türetme durumunda
taban sınıfın __init__ metodunun birden fazla kez çağrılmasını engellemek
içindir. Anımsanacağı gibi super(X, r).foo() gibi bir çağırmada foo metodunun
aranması r değişkeninin ilişkin olduğu sınıfın MRO sırasında
X sınıfından sonraki sınıftan başlatılmaktadır. O halde örneğin:
class X(Y):
def __init__(self):
super(X, self).__init__()
Burada self'in ilişkin olduğu sınıfın MRO sırasında X'ten sonraki sınıfan
başlanarak __init__ metodu aranacaktır.
class A:
def __init__(self):
print('A.__init__')
class B(A):
def __init__(self):
super(B, self).__init__() # D'nin MRO sırasında göre B'den sonraki
sınıf C'dir
print('B.__init__')
class C(A):
def __init__(self):
super(C, self).__init__() # D'nin MRO sırasına göre C'den sonraki sınıf
A'dır
print('C.__init__')
d = D()
Burada super(d, self).__init__ ile self D sınıfı türünden olduğu için ve D'nin
MRO sırasında D'den sonra gelen sınıf B olduğu için B'nin__init__
metodu çağrılacaktır B'nin __init__ metodu şöyledir:
class B(A):
def __init__(self):
super(B, self).__init__() # self hala D sınıfı türünden D'nin MRO
sırasında göre B'den sonraki sınıf C'dir
print('B.__init__')
Burada self yine D sınıfı türündendir. D'nin MRO sırasına göre B'den sonraki
sınıf C olduğuna göre aslında super(Bi self).__init__
çağrısı ile C sınıfının __init__ metodu çağırılır. C sınıfının __init__ metodu
şöyledir:
class C(A):
def __init__(self):
super(C, self).__init__() # self hala D türünden, D'nin MRO sırasına
göre C'den sonraki sınıf A'dır
print('C.__init__')
Burada da self hala D türündendir. D'nin MRO sırasına göre C'den sonraki sınıf
A olduğu için aslında A'nın __init__ metodu çağrılır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki anlatımlşardan şu sonucu çıkartabiliriz: Biz her zaman taban sınıfın
__init__ metodunu taban sınıf ismiyle değil
super fonksiyonuyla çağırmalıyız. Bu durumda baklava biçiminde bir türetme olsa
bile her zaman taban sınıfların __init__
metotları bir kez çağrılmış olur. Örneğin:
class A:
def __init__(self):
pass
class B(A):
def __init__(self):
super(B, self).__init__()
super(kendi_sınıfımızın_ismi, self).__init__(...)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi türetme şemasının en yukarısında her zaman object sınıfı olduğuna göre
biz object sınıfının da __init__ metodunu
çağırmalı mıyız? Örneğin:
class Sample:
def __init__(self):
super(Sample, self).__init__()
# ...
#----------------------------------------------------------------------------------
--------------------------------------
Biz yukarıda genel olarak türemiş sınıfın __init__ metodunun taban sınıfn
__init__ metodunu super fonksiyonuyla çağırması
gerektiğini belirtmiştik. Sonra da taban object sınıfı için __init__ metodunun
çağrılmasına gerek olmadığını da ifade etmiştik.
Pekiyi bu durumda aşağıdaki gibi bir çoklu türetmede ne olacaktır:
A B
C
Burada C'nin MRO sırası C, A, B biçimindedir. Burada eğer A'da A'nın object
sınıfından türetilmiş olduğu fikriyle A'nın
__init__ metodunda super çağrısı yapılmazsa B'nin __init__ metodu
çağrılmayacaktır. O zaman A sınıfını oluşturan kişi
object sınıfın __init__ metodunu super fonksiyonu ile çağırmalı mıdır? Örneğin:
class A:
def __init__(self):
print('A.__init__')
class B:
def __init__(self):
print('B.__init__')
c = C()
class A:
def __init__(self):
super(A, self).__init__()
print('A.__init__')
class B:
def __init__(self):
super(B, self).__init__()
print('B.__init__')
class C(A, B):
def __init__(self):
super(C, self).__init__()
print('C.__init__')
c = C()
class A:
def __init__(self):
print('A.__init__')
class B:
def __init__(self):
print('B.__init__')
c = C()
Eğer sınıfın dokümantasyonunda özellikle çoklu türetme desteği için bir şey
söylenmemişse sınıfın çoklu türetmeyi desteklemediği
sonucu çıkartılmalıdır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
45. Ders 03/10/2022 -
Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Çoklu türetmede diğer bir sorun da çoklu türetilmiş sınıfın __init__
metotlarının MRO sırasına göre diğer sınıfların __init__
metotlarına iletilmesidir. Burada genel uygulama çoklu türetmeyi destekleyen
sınıfların __init__ metotlarının *args ve **kwargs
parametrelerini bulundurmaları ve bu parametreleri *'lı bir biçimde super
fonksiyonunda kullanmalarıdır. Böylece aktarım başaırıyla yapılabilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
class A:
def __init__(self, a, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
self.a = a
print('A.__init__')
class B:
def __init__(self, b, *args, **kwargs):
super(B, self).__init__(*args, **kwargs)
self.b = b
print('B.__init__')
#----------------------------------------------------------------------------------
--------------------------------------
super fonksiyonu parametresiz de kullanılabilir. Bu durumda fonksiyonun birinci
parametresi içinde bulunulan sınıfının ismi
olarak, ikinci parametresi self olarak geçilmiş kabul edilir. Tabii bu kullanım
yalnıcz metotlar içerisinde yapılabilir. ÖÇrneğin A
sınıfının __init__ metodunda şöyle bir çağırma yapılmış olsun:
super().__init__()
super(A, self).__init__()
#----------------------------------------------------------------------------------
--------------------------------------
class A:
def __init__(self):
print('A.__init__')
class B(A):
def __init__(self):
super().__init__() # super(B, self).__init__()
print('B.__init__')
class C(B):
def __init__(self):
super().__init__() # super(C, self).__init__()
print('C.__init__')
c = C()
#----------------------------------------------------------------------------------
--------------------------------------
NYPT'nin önemli bir prensibi "kapsülleme (encapsulation)" denilen prensiptir.
Kapsülleme bir kavramın bir sınıfla temsil edilmesi
ve sınıfın dışarıya ilgilendirmeyen, iç işleyişe ilişkin olan öğelerinin dış
dünyadan gizlenmesi anlamına gelmektedir.
Bu gizleme hem algısal bir açıklık sağlamakta hem de yanlış kullanımları
engellemektedir. Aslında kapsülleme dış dünyada da sıklıkla
karşılaştığımız bir olgudur. Örneğin bir otomobilin pek çok aksamı kaput
içerisinde gizlenmiştir. Yalnızca kullanıcıyı ilgilendiren kısımları
görünür hale getirilmiştir. Bir televizyon için de aynı durum söz konusudur.
Biz televizyonu kullanmak için onun karmaşık yapısını bilmemiz
gerekmemektedir.
Ancak Python'da bu anlamda bir gizleme mekanizması yoktur. Yani C++, Java ve C#
gibi dillerdeki "public", "protected", private" kavramları
Python'da bulunmamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da her ne kadar C++, 'java ve C# gibi dillerdeki sınıflarda bulunan
"public", "private", "protcted" bölümler olmasa da
bir sınıf elemanının ya da nesnenin özniteliğinin dış dünyadan gizlenmesi
isimsel biçimde sağlanmaya çalışılmıştır. Öyle ki
eğer bir sınıfın bir elemanının ya da nesnenin özniteliğinin ismi '_' ile
başlatılırsa bu durum private etkisi yaratmaktadır. Ancak bu
etki yalnızca bir bilgi sağlamaktadır. Yorumlayıcı tarafından erişimde bir
denetleme yapılmamaktadır. Başka bir deyişle biz Python'da
sınıfın bir elemanının ya da bir nesnenin özniteliğinin '_' ile başladığını
gördüğümüzde "bu elemanlara ya da özniteliklere
dışarıdan erişilmesinin istenmediği" anlamını çıkartmalıyız. Ancak yine de biz
istersek bu elemanlara ya da özniteliklere erişebiliriz.
Burada bir zorlayıcılığın olmadığna yalnızca yazım biçiyle bir bilgi
verildiğine dikkat ediniz. Örneğin:
class Sample:
def do_someting_important(self):
# ....
self._foo()
# ....
self._bar()
# ....
self._tar()
#....
def _foo(self):
pass
def _bar(self):
pass
def _tar(self):
pass
s = Sample()
s.do_someting_important()
s._foo()
import math
class Circle:
def __init__(self, radius):
[Link] = radius
self._area = radius * radius * [Link]
# ...
c = Circle(10)
print([Link])
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfın elemanlarının ve örnek özniteliklerinin dış dünyadan gizlenmesi için
diğer bir yöntem de isimleirn başına '__' (iki alt tire)
getirmektir. Bir isim iki alt tire ile başlanarak isimlendirilmesi "dışarıdan
kullanımın daha katı bir biçimde istenmediği" anlamına
gelmektedir. İki alt tire ile verilen isimlere gerçekten dışarıdan erişilemez.
Ancak _sınıf_ismi__ öneki erişilebilir. Yani başka bir deyişle
Sample sınıfının __xxx elemanına biz s.__xxx gibi bir ifadeyle erişemeyiz.
Ancak s._Sample__xxx ismiyle erişebiliriz. Bunun amacı kişilerin
ilgili elemana yanlışlıkla erişmelerinin engellenmesidir. Mutlak anlamda
erişmelerinin engellenmesi değildir.
Örneğin:
class Sample:
def do_someting_important(self):
# ....
self.__foo()
# ....
self.__bar()
# ....
self.__tar()
#....
def __foo(self):
pass
def __bar(self):
pass
def __tar(self):
pass
s = Sample()
s.do_someting_important()
s.__foo() # error!
import math
class Circle:
def __init__(self, radius):
[Link] = radius
self.__area = [Link] * radius * radius
def disp(self):
print([Link], self.__area)
#...
c = Circle(1)
print(c._Circle__area) # geçerli
print(c.__area) #error!
Başka bir deyişle biz sınıf içerisinde bir özniteliği ya da bir metodu iki alt
tire ile isimlendirirsek aslında
yorumlayıcı bu özniteliği ya da metodu _sınıfismi__ öneki ile
isimlendirmektedir. Biz sınıf içerisinde iki alt tireli
isimlere iki alt tire ile erişebilmeteyiz. Ancak dışarıdan iki alt tire ile
erişemeyiz. Dışarıdan ancak _sınıfismi__
öneki ile erişebiliriz.
Ancak iki altireli bir örnek özniteliği sınıfın içerisinde değil de dışarıda
oluşturulmuşsa bu durumda erişim iki alt tireli
isimle yapılabilmektedir. Örneğin:
class Sample:
def __init__(self):
self.__a = 10
s = Sample()
s.__b = 20
print(s.__b) # dışarıdan erişilebilir
print(s.__a) # dışarıdan erişilemez!
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi biz dışarıdan kullanılmasını istemediğimiz isimlerin başına tek alt tire
mi yoksa iki alt tire mi getirmeliyiz?
Bu durum tamamen bizim isteğimize bağlıdır. Eğer biz "dışarıdan erişme" fikrini
vurgulamak istiyorsak ik alt tire kullanabiliriz.
Ancak programcıların çoğu tek alt tire kullanmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Pek çok teorisyene göre bir dilin "nesne yönelimli" olması için dilin şu üç
özelliği destekliyor olması gerekmektedir:
- Sınıf kavramı
- Türetme kavramı
- Çokbiçimlilik (polymophism)
Eğer bir dilde sınıflar olduğu halde çokbiçimlilik yoksa böyle dillere "nesne
tabanlı (object based)" diller denilmektedir.
Başka bir deyişle dilin nesne yönelimli olabilmesi için "çokbiçimli
mekanizmaya" sahip olması gerekmektedir. C++, Java, C# gibi dillerde
bu çokbiçimli mekanizma bulunmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
NYPT'de çokbiçimlilik biyolojiden aktarılmış bir terimdir. Biyolojide
çokbiçimlilik "canlıların çeşitli doku ve organlarının
temel işlevleri aynı kalmak üzere farklılıklar göstermesi" anlamına
gelmektedir. Yani örneğin kulak pek çok canlıda vardır.
Temel işlevi duymaktır. Ancak her canlı kendine göre bir kulak yapısına
sahiptir.
NYPT'de bir eylem temel işlevi aynı olmak üzere sınıflar arasında farklılıklar
gösteriyorsa bu eyleme "çokbiçimli" eylem denilmektedir.
Örneğin A, B, C, D, E sınıflarının disp isimli metotları olsun. Bu metotlar
kendi sınıflarının birtakım elemanlarını kendilerine özgü biçimde
ekrana yazdırıyor olsun. Burada disp metodu "çokbiçimli" bir metottur. Çeşitli
sınıflarda vardır. Temel işlevi sınıf hakkında bilgileri
ekrana yazdırmaktır. Ancak her sınıfın disp metodu kendine özgüdür ve temel
işlevi aynı olmasına karşın birbirlerinden farklıdır. Örneğin
Worker, Manager, Executive, SalesPerson sınıfları ösz konusu olsun. Bu
sınıfların hepsinde kişilerin maaşlarını hesaplayan calculate_salary
isimli bir metot olsun. Buradaki eylem çokbiçimlidir. Bu sınıfların hepsinde
vardır ama her sınıfın maaş hesabı kendine özgü bir biçimde yapılmaktadır.
Çokbiçimli metotlar farklı sınıflarda aynı isimle bulunurlar. Bunların
yaptıkları işler anahatlarıyla aynıdır ancak o sınıfa özgü
farklılıklar içermektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
C++, Java, C# gibi dillerde çokbiçimli mekanizma "türetme yoluyla"
sağlanmaktadır. Tipik olarak bir taban sınıf oluşturulur.
O taban sınıftan sınıflar türetilir. Taban sınıftaki bir fonksiyon (metot)
türemiş sınıfta aynı isimle yeniden yazılır. Buna
taban sınıftaki fonksiyonun (metodun) türemiş sınıfta "override" edilmesi
denilmektedir. Ancak Python dinamik tür sistemine
sahip olduğu için Python zaten doğuştan çokbiçimlidir. Python'da zaten isim
araması programın çalışma zamanında yapıldığı için
çokbiçimli mekaznizma türetmeye bağlı değildir. Bu nedenle Python'da çokbiçimli
mekanizmayı oluşturmak için özel bir sentaks da yoktur.
Aşağıdaki örnekte "duy" metodu çokbiçimli bir metottur. Farklı sınıflarda aynı
isimle bulunmaktadır. Ancak o sınıflara özgü bir
gerçekleştirimi vardır. Aşağıdaki örnekteki foo fonksiyonu bir canlıyı alır. O
canlının hangi canlı olduğunu bilmeden
onun duyma eyleminden hareketle ona özgü eylemleri gerçekleştirmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
class Kedi:
def duy(self):
print('kedi duyuyor')
class Köpek:
def duy(self):
print('köpek duyuyor')
class At:
def duy(self):
print('at duyuyor')
class Aslan:
def duy(self):
print('aslan duyuyor')
class İnsan:
def duy(self):
print('insan duyuyor')
def foo(canlı):
print('--------')
canlı.duy()
#....
canlı.duy()
#...
canlı.duy()
kedi = Kedi()
köpek = Köpek()
at = At()
aslan = Aslan()
insan = İnsan()
foo(kedi)
foo(köpek)
foo(at)
foo(aslan)
foo(insan)
#----------------------------------------------------------------------------------
--------------------------------------
46. Ders 05/10/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Çokbiçimlilikten en önemli amaç "türden bağımsız kod parçalarının"
oluşturulmasıdır. Çokbiçimlilik biribirine benzeyen ancak farklı olan nesnelerin
sanki aynı nesneymiş gibi işleme sokulmasına olanak sağlamaktadır.
Aşağıdaki örnekte böyle bir Tetris oyunu mantıksal olarak simüle edilmeye
çalışılmıştır. Burada biz Microsoft sepsifik msvcrt
modülünü kullandık. Bu modül standart Python kütüphanesinde olmasına karşın
yalnızca Windows sistemlerinde kullanılabilmektedir.
Ayrıca maalesef bu modül Spyder konsolunda çalışmamaktadır. Bu nedenle bu
örneği Windows konsoluna geçerek çalıştırınız.
#----------------------------------------------------------------------------------
--------------------------------------
import random
import time
import msvcrt
class Shape:
def __init__(self):
pass
class BarShape(Shape):
def __init__(self):
super().__init__()
def move_down(self):
print('BarShape moves down')
def move_left(self):
print('<<BarShape moves left>>')
def move_right(self):
print('<<BarShape moves right>>')
def rotate(self):
print('<<BarShape rotates>>')
class SquareShape(Shape):
def __init__(self):
super().__init__()
def move_down(self):
print('SquareShape moves down')
def move_left(self):
print('<<SquareShape moves left>>')
def move_right(self):
print('<<SquareShape moves right>>')
def rotate(self):
print('<<SquareShape rotates>>')
class ZShape(Shape):
def __init__(self):
super().__init__()
def move_down(self):
print('ZShape moves down')
def move_left(self):
print('<<ZShape moves left>>')
def move_right(self):
print('<<ZShape moves right>>')
def rotate(self):
print('<<ZShape rotates>>')
class LShape(Shape):
def __init__(self):
super().__init__()
def move_down(self):
print('LShape moves down')
def move_left(self):
print('<<LShape moves left>>')
def move_right(self):
print('<<LShape moves right>>')
def rotate(self):
print('<<LShape rotates>>')
class TShape(Shape):
def __init__(self):
super().__init__()
def move_down(self):
print('TShape moves down')
def move_left(self):
print('<<TShape moves left>>')
def move_right(self):
print('<<TShape moves right>>')
def rotate(self):
print('<<TShape rotates>>')
class Tetris:
def __init__(self):
pass
def get_random_shape(self):
shapes = [BarShape, TShape, ZShape, LShape, SquareShape]
shape_type = [Link](shapes)
return shape_type()
def run(self):
while True:
shape = self.get_random_shape()
for _ in range(20):
shape.move_down()
if [Link]():
ch = [Link]()
if ch == b'a':
shape.move_left()
elif ch == b's':
shape.move_right()
elif ch == b'd':
[Link]()
elif ch == b'q':
return
[Link](0.5)
tetris = Tetris()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıf türünden nesneyi str türüne dönüştürebiliriz. Böyle bir dönüştürme
ilgili sınıfın __str__ isimli bir metodunun
bulunuyor olması gerekir. s bir sınıf türünden değişken olmak üzere str(s)
işlemi tamamen s.__str__() ile aynı anlamdadır.
Programcı sınıfın __str__ metodunu bir str nesnesiyle geri döndürmelidir.
print(s)
ile
print(s.__str__())
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __str__(self):
return 'this is a test'
s = Sample()
print(s) # this is a test
k = str(s) # k = s.__str__()
print(k) # this is a test
#----------------------------------------------------------------------------------
--------------------------------------
Tipik olarak programcı sınıfı için __str__ metodunu yazarak o sınıfın tuttuğu
bilgiyi bu metotta bir yazı olarak döndürür.
Böylece bu türden bir sınıf nesnesi print ile yazdırılmak istendiğinde ekranda
o nesneyi betimleyen bir yazı görünecektir.
#----------------------------------------------------------------------------------
--------------------------------------
class Date:
def __init__(self, day, month, year):
[Link] = day
[Link] = month
[Link] = year
def __str__(self):
return f'{[Link]}/{[Link]}/{[Link]}'
#----------------------------------------------------------------------------------
--------------------------------------
Aslında biz list gibi, tuple gibi sınıf nesnelerini print yazdırdığımızda önce
o nesneler string türüne dönüştürülüp elde
edilen yazılar yazdırılmaktadır. Bunun için o sınıflarda __str__ metotları
bulunmaktadır. Bir listenin print ile nasıl
yazdırılabildiği hakkında fikir edinmeniz için aşağıdaki örneği veriyoruz.
#----------------------------------------------------------------------------------
--------------------------------------
class MyList:
def __init__(self, *args):
[Link] = args
def __str__(self):
s = '['
for x in [Link]:
if s != '[':
s += ', '
s += str(x)
s += ']'
return s
ml = MyList(1, 2, 3, 4, 5)
print(ml)
#----------------------------------------------------------------------------------
--------------------------------------
Örneğin bir karmaşık sayıları temsil eden bir sınıfın __str__ metodu nesnenin
tuttuğu karmaşık sayıyı yazı olarak verebilir.
#----------------------------------------------------------------------------------
--------------------------------------
class Complex:
def __init__(self, real, imag):
[Link] = real
[Link] = imag
def __str__(self):
return f'{[Link]}+{[Link]}i'
z = Complex(3, 2)
print(z) # 3+2i
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi bir sınıf türünden bir değişken ile bir metot çağrıldığında
metot sırasıyla değişkenin ilişkin olduğu sınıfın
MRO sırasına göre sınıflarda aranıyordu. Normal türetme durumlarında MRO
sırasının aşağıdan yukarıya doğru olduğunu anımsayınız.
Daha önce bir sınıfı hiçbir sınıftan türetmemişsek onun otomatik olarak object
sınıfından türetildiğini belirtmiştik. İşte
object sınıfının da bir __str__ metodu vardır. object sınıfının bu __str__
metodu nesnenin ismini id'sini hex sistemde yazı olarak vermektedir.
Yani özetle biz kendi sınıfımız için __str__ metodunu yazmazsak object
sınıfının __str__ metodu çağrılacak, object sınıfının __str__
metodu da değişkenin ilişkin olduğu sınıfın ismini ve onun id'sini verecektir.
#----------------------------------------------------------------------------------
--------------------------------------
class A:
pass
a = A()
print(a) # <__main__.A object at 0x000001F36F45E580>
#----------------------------------------------------------------------------------
--------------------------------------
Programcı bir sınıf yazmışsa onun için bir __str__ metodunu yazması iyi bir
tekniktir. Tabii __str__ metotlarının nasıl bir yazı
geri döndüreceğinin bir standardı yoktur. Programcı bu metotlarda genel olarak
sınıfın örnek özniteliklerini yazısal olarak
kendisinin istediği biçimde verir. Python'ın standart kütüphanesindeki
sınıflarda da hep bu __str__ metotları yazılmış durumdadır.
#----------------------------------------------------------------------------------
--------------------------------------
import datetime
d = [Link](2022, 10, 4)
print(d) # 2022-10-04
s = str(d)
print(s) # 2022-10-04
#----------------------------------------------------------------------------------
--------------------------------------
__str__ metodunun __repr__ isminde bir benzeri de vardır. __repr__ metodu da
tıpkı __str__ netodunda olduğu gibi bir
string ile geri dönmelidir. Pekiyi iki metot arasında ne farklılık vardır? İşte
__str__ metodu "daha çok kullanıcıya yönelik",
__repr__ metodu "daha çok programcıya yönelik" bir yazı verme iddiasındadır.
Tabii bu iki metodun veridği yazılar aynı da olabilir.
Örneğin Python2ın komut satırında, Spyder'ın komut satırında (IPython) bir
değişkeni yazıp ENTER tuşuna bastığımızda bu komut satırı programları
değişken ile __repr__ metodunu çağırıp buradan elde edilen yazıyı
bastırmaktadır. Örneğin:
Özetle:
1) Eğer sınıfın __repr__ metodu varsa fakat __str__ metodu yoksa str türüne
dönüştürmede __repr__ metodu kullanılmaktadır. Aynı zamanda
komu satırında değişken ismi yazılıp ENTER tuşuna basıldığında da __repr__
nmetodu kullanılmaktadır.
2) Sınıfta hem __str__ hem de __repr__ metodu varsa bu durumda str türüne
dönüştürmede __str__ metodu, komut satırından değişken ismini
yazıp ENTER tuşuna basıldığında __repr__ çağrılmaktadır.
3) Sınıfta yalnızca __str__ metodu varsa str türüne dönüştürmede __str__ metodu
çağrılır. Komut satırında değişken ismi yazıldığında onject sınıfının
__repr__ metodu çağrılır.
def repr(o):
return o.__repr__()
import datetime
d = [Link](2022, 10, 4)
print(d)
print(repr(d)) # print(d.__repr__())
import datetime
f = open('[Link]')
s = [Link]()
print(repr(s))
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
47. Ders 10/10/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da bir nesne yaratıldığında sistem o nesnesnin kaç değişken değişken
tarafından referans edildiğini (yani gösterildiğini)
tutmaktadır. Buna nesnenin "referans sayacı (reference count)" denilmektedir.
Nesnenin referans sayacı programın çalışması
sırasında azalıp artabilmektedir. Nesnenin referans sayacı 0 olduğunda artık o
nesneyi hiçbir değişken göstermiyor durumda olur.
def foo(x):
# RC: 5
y = x
# RC: 6
#----------------------------------------------------------------------------------
--------------------------------------
Bir nesnenin referans sayacı sys modülündeki getrefcount isimli fonksiyonla
elde edilebilir. Bu fonksiyon nesnenin referans
sayacını her zaman bir fazla olarak vermektedir. Çünkü bu fonksiyon referans
sayacı bulunacak nesnenin adresini parametre
yoluyla aldığı için fonksiyonun içerisinde nesnenin referans sayacı bir artmış
olmaktadır. Ayrıca örneğin bir değişken
bir fonksiyona geçirildiğinde Python yorumlayıcısı fonksiyonun parametre
değişkenini de ayrıca tuttuğu için nesnenin referans
sayacı bir değil iki de artabilir. Tabii burada aslında sayıların pek önemi
yoktur. Buradaki en önemli nokta nesnesinin referans sayacının
sıfıra düşmesidir. bı
Tabii Python'da aslında int, float, str, bool gibi temel türler de birer sınıf
biçimindedir. Dolayısıyla örneğin int bir nesnenin de referans
sayacı vardır, floatr bir nesnenin de referans sayacı vardır.
#----------------------------------------------------------------------------------
--------------------------------------
import sys
def foo(x):
print([Link](x))
y = x
print([Link](x))
#----------------------------------------------------------------------------------
--------------------------------------
getrefcount fonksiyonu ile nesnelerin referans sayaçlarını elde ettiğinizde bu
sayılar umduğunuz gibi çıkmayabilir.
Çünkü Python yorumlayıcıları kendi içlerinde çeşitli nedenlerden dolayı
nesnenin başka değişkenler tarafından da gösterilmesini
sağlayabilmektedir. Ayrıca CPython gibi yorumlayıcılar belli sabitleri işin
başında bir kez yaratıp o sabitler kullanıldığında
zaten yaratılmış olan nesnelerin adreslerini kullanabilmektedir. Bu nedenle pek
çok sabitin yüksek bir referans sayacı olabilmektedir. Örneğin:
>>> a = 0
>>> [Link](a)
313
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Python'da bir nesne yaratıldktan sonra o nesnenin referans sayacı sıfıra
düştüğünde nesne artık çöp duruma gelmektedir ve
"çöp toplayıcı (garbage collector)" denilen mekanizma sayesinde bellekten yok
edilmektedir. Yani Python'da programcı nesneleri yaratır
ancak onların yok edilmesiyle uğraşmaz. Onların yok edilmesi yorumlayıcı sistem
tarafından otomatik yapılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
Python'ın çöp toplayıcı mekanizması nesneyi yok etmeden hemen önce o nesne için
eğer varsa ilgili sınıfın __del__ isimli metodunu çağırmaktadır.
Böylece programcı nesne yok edilmeden önce nesne ile ilgili birtakım son
işlemleri yapabilir. Tabii programcılar genel olarak Python
dünyasının içerisinde kaldıklarından dolayı bu metodu yazmaya pek gereksinim
duymamaktadır. Ancak eğer programcı Python dünyasının dışında
birtakım tahsisatları sınıfın __init__ metdounda yapmışsa bu tahsisatların
otomatik geri bırakılmasını __del__ metodunda sağlayabilir.
İşletim sistemi düzeyinde tahsis edilen kaynaklara .NET ve Java dünyasında
"unmanaged" kaynaklar denilmektedir. Aslında Python2ın standart
kütüphanesi zaten bu tür unmanaged kaynakları kullanmaktadır ve onların
boşaltımını ilgili sınıfların __del__ metotlarında yapmaktadır.
Yukarıda da belirttiğimiz gibi programcılar genel olarak bu __del__ metotlarını
yazmayı gerektirecek uygulamalar içerisinde seyrek olarak
bulunurlar.
Aşağıdaki örnekte __del__ metodu tam olarak s = None işleminden sonra hemen
çağrılacaktır. CPython yorumlayıcısında kullanılan
algoritma nesnenin referans sayacı sıfıra düşer düşmez nesneyi yok eder ve
nesneyi yok etmeden önce sınıfın __del__ metodunu çağırır.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self):
print('işletim sistemi düzeyinde kaynak tahsis ediliyor')
def __del__(self):
print('işletim sistemi düzeyinde tahsis edilen kaynak boşaltılıyor')
print('program başladı')
s = Sample()
print('program devam ediyor')
s = None
print('program sonlanıyor')
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte bir fonksiyon içerisinde bir nesne yaratılmıştır. Fonksiyon
biterken yerel değişkenler yok edildiği için
fonksiyon içerisinde yaratılmış olan nesnenin referans sayacı sıfıra düşmüş ve
çöp toplayıcı nesneyi hemen silmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self):
print('işletim sistemi düzeyinde kaynak tahsis ediliyor')
def __del__(self):
print('işletim sistemi düzeyinde tahsis edilen kaynak boşaltılıyor')
def foo():
print('foo başlıyor')
s = Sample()
print('foo bitiyor')
print('program başlıyor')
foo()
print('program bitiyor')
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi CPython'daki __del__ metodunun çağrılması ytamamen deterministik midir?
Aslında büyük ölçüde CPython'da bu __del__
metodunun çağrılması deterministik gibi gözükmektedir. Ancak yine de tam olarak
böyle bir durum söz konusu değildir. Örneğin:
def foo():
print('foo begins')
s = Sample()
print('foo ends')
Burada s ne zaman hayatını kaybetmektedir? CPython yorumlayıcısı s'in bir daha
kullanılmadığını fark edip hemen
son print'ten önce nesneyi silebilmektedir. Halbuki örneğin C++'ta kesinlikle
bu durumda "destructor" fonksiyonun nerede
çağrılacağı kesin olarak bellidir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Biz bir sınıftan türetme yapıyorsak ve sınıfımız için __del__ metodunu
yazmışsak bu durumda türemiş sınıf türünden nesnemiz
çöp toplayıcı tarafından yok edilirken bizim türemişl sınıfın __del__ metodu
çağrılacaktır. Ancak eğer taban sınıfın da __del__ metodu varsa
bizim türemiş sınıfın __del__ metodu içerisinde taban sınıfın __del__ metodunu
çağırmamız gerekir. Bu çağırma işlemi tipik olarak
türemiş sınıfın __del__ metodunun sonunda yapılmalıdır. Tabii eğer taban
sınıfın bir __del__ metodu yoksa biz türemiş sınıfın __del__
metodu içerisinde taban sınııfın __del__ metodunu çağırmamalıyız. Çünkü
Python'da object sınıfınn bir __del__ metodu yoktur.
#----------------------------------------------------------------------------------
--------------------------------------
class A:
def __init__(self):
print('A.__init__')
def __del__(self):
print('A.__del__')
class B(A):
def __init__(self):
super().__init__()
print('B.__init__')
def __del__(self):
print('B.__del__')
super().__del__()
b = B()
b = None
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi biz taban sınıfın __del__ metodunun olduğunu nasıl anlayabiliriz?
Aslında en sağlam yol dökümantasyona bakmak olabilir. Eğer
dokğmantasyon yoksa biz de taban sınıfın __del__ metoduna sahip olup olmadığını
bilemeyecek durumdaysak o zaman hasattr fonksiyonuyla bu kontrolü yapabiliriz.
(has fonksiyonu ileri ele alınacaktır.) Aşağıdaki örnekte türemiş sınıfın
__del__ metodu önce hasattr fonksiyonu ile taban sınıfın __del__ metodu var mı
diye bakmıştır. Sonra eğer varsa taban sınıfın __del__ metodunu çapırmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
class A:
def __init__(self):
print('A.__init__')
def __del__(self):
print('A.__del__')
class B(A):
def __init__(self):
super().__init__()
print('B.__init__')
def __del__(self):
print('B.__del__')
o = super()
if hasattr(o, '__del__'):
o.__del__()
b = B()
b = None
#----------------------------------------------------------------------------------
--------------------------------------
48. Ders 12/10/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
"Operatör metotları" konusu nesne yönelimli dillerin çoğunda bulunmaktadır.
Örneğin bu özellik C++'ta, C#'ta, Swift'te
vardır. Ancak Java'da bulunmamaktadır. Python'da da "operatör metotları"
bulunmaktadır. Operatör metotları aslında dile ekstra
bir özellik katmamaktadır. Yalnızca okunabilirlik bakımından bir avantaj
sağlamaktadır. Yani başka bir deyişle nesne yönelimli bir dilde
operatör metotları olmasa da bu işlemler normal metotlarla sağlanabilir. Ancak
operatör metotları güzel bir görünüm sunmaktadır.
Aşağıdaki örnekte bir Complex sayı sınıfı oluşturulmuştur. İki Complex sayıyı
toplayabilmek için sınıfa add isimli bir metot eklenmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
class Complex:
def __init__(self, real = 0, imag = 0):
[Link] = real
[Link] = imag
return result
def __repr__(self):
return f'{[Link]}+{[Link]}i'
x = Complex(3, 2) # 3 + 2i
print(x)
y = Complex(4, 2) # 4 + 2i
print(y)
z = [Link](y)
print(z)
#----------------------------------------------------------------------------------
--------------------------------------
Operatör metotları özel dunder isimli bazı metotlardır. Örneğin toplama
işlemini yapan operatör metodu __add__, çıkartma işlemini
yapan opereatör metodu __sub__, çarpma işleminş yapan operatör metodu __mull__
ismindedir. Programcı bu metotları yazarak
Python opereatörlerinde kendi sınıfları için bu metotların çağrılmasını
sağlayabilir.
class Complex:
def __init__(self, real = 0, imag = 0):
[Link] = real
[Link] = imag
return result
def __repr__(self):
return f'{[Link]}+{[Link]}i'
x = Complex(3, 2) # 3 + 2i
print(x)
y = Complex(4, 2) # 4 + 2i
print(y)
z = x.__add__(y)
print(z)
z = x + y # x.__add__(y)
#----------------------------------------------------------------------------------
--------------------------------------
a, b ve c bir sınıf türünden değişkenler olsun. a + b + c işleminin eşdeğeri
a.__add__(b).__add__(c) biçimindedir.
#----------------------------------------------------------------------------------
--------------------------------------
class Complex:
def __init__(self, real = 0, imag = 0):
[Link] = real
[Link] = imag
return result
def __repr__(self):
return f'{[Link]}+{[Link]}i'
x = Complex(3, 2) # 3 + 2i
y = Complex(4, 2) # 4 + 2i
z = Complex(1, 4) # 1 + 4i
result = x.__add__(y).__add__(z)
print(result)
result = x + y + z;
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Operatör metotları operatör önceliklerini değiştirmez. Örneğin a, b, c birer
sınıf türünden olmak üzere a + b * c işleminin
eşdeğeri a.__add__(b.__mull__(c)) biçimindedir. Operatör metotları bu biçimde
kombine edilebilir.
#----------------------------------------------------------------------------------
--------------------------------------
class Complex:
def __init__(self, real = 0, imag = 0):
[Link] = real
[Link] = imag
return result
return result
def __repr__(self):
return f'{[Link]}+{[Link]}i'
x = Complex(3, 2) # 3 + 2i
y = Complex(1, 2) # 4 + 2i
z = Complex(1, 4) # 1 + 4i
result = x + y * z;
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Tabii iki operand'lı operatörlerin diğer operand'ları temel türlere ilişkin
olabilir. Yani iki operand da aynı sınıfa ilişkin
olmak zorunda değildir. Örneğin z değişkeni Complex sınıfı türünden olsun. Biz
iki Complex sayıyı toplama isteyebileceğimiz gibi
bir Complex sayı ile bir int, float değeri de toplamak isteyebiliriz. Bu
durumda örneğin bu int ya da float değer Complex nesnesinin
gerçek kısmıyla toplanır.
#----------------------------------------------------------------------------------
--------------------------------------
class Complex:
def __init__(self, real = 0, imag = 0):
[Link] = real
[Link] = imag
return result
def __repr__(self):
return f'{[Link]}+{[Link]}i'
x = Complex(3, 2) # 3 + 2i
y = Complex(1, 2) # 4 + 2i
result = x + 2 + y
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi Python'da iki tane bölme operatörü vardır. / operatörü her
zaman float değer vermektedir. // operatörü (floordiv)
ise bölüm sonucunda noktadan sonraki kısmı atmaktadır. / operatyörü __truediv__
ismiyle // operatörü __floordiv__ ismiyle yazılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
class Number:
def __init__(self, number = 0):
[Link] = number
def __add__(self, val):
result = Number()
if isinstance(val, (int, float, bool)):
[Link] = [Link] + val
else:
[Link] = [Link] + [Link]
return result
return result
return result
def __repr__(self):
return str([Link])
a = Number(10)
b = Number(4)
result = a / b
print(result)
result = a // b
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Python'a 3.5 versiyonuyla birlikte @ operatörü de eklenmiştir. Bu operatöre
"matris çarpımı" operatörü de denilmektedir.
Ancak bu @ operatörü standart Python sınıfları tarafından kulalnılmamaktadır.
Fakat NumPy gibi Pandas gibi kütüphaneler
bu operatörü kullanmaktadır. Bu operatör aslında sınıfının __matmul__ isimli
bir metodunu çağırır. Operatörün Python'a
eklenme sebebi matris çarpımı işlemi olsa da aslında operatör başka amaçlarla
da kullanılabilmektedir.
class Matrix:
def __init__(self, matrix):
[Link] = matrix
[Link] = len(matrix)
[Link] = len(matrix[0])
return result
m = Matrix([[1, 2, 1]])
k = Matrix([[3, 2, 1], [1, 1, 1], [1, 2, 3]])
result = m @ k
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Karşılaştırma operatörleri için operatör metot isimleri de şöyledir:
> __gt__
< __lt__
>= __ge__
<= __file__
== __eq__
!= __ne__
Tabii programcı tipik olarak bu operatör metotlarını bool bir değerle geri
döndürür.
#----------------------------------------------------------------------------------
--------------------------------------
class Number:
def __init__(self, number = 0):
[Link] = number
def __repr__(self):
return str([Link])
a = Number(4)
b = Number(4)
if a > b:
print('a > b')
elif a < b:
print('a < b')
elif a == b:
print('a == b')
#----------------------------------------------------------------------------------
--------------------------------------
Bazı operatörlerin değişme özelliği vardır. Yani örneğin a + 3 gibi bir ifadeyi
biz 3 + a gibi de yazabiliriz. Ancak operatör metotlarında
soldaki operandın bizim sınıfımız türünden olması gerekmektedir. Örneğin a
Number sınıfı türünden olsun. a + 3 gibi bir toplama
işleminş yapan bir operatör metodu yazabiliriz. Pekiyi 3 + a işlemini yapacak
bir operatör netodunu nasıl yazabiliriz? 3 + a
işleminşn eşdeğeri 3.__add__(a) biçimindedir. int sınıfı ise bizim yazdığımız
bir sınıf değildir. İşte iki operand'lı operatör metodunun ismi
__xxx__ olmak üzere bir bunların __rxxx__ biçiminde başı "r" ile başlayan
(reverse sözcüğünden geliyor) versiyonları vardır. Python yorumlayıcısı
@ bir operatör belirtmek üzere a @ b işlemini yapacak operatör metodunu önce
sol taraftaki operandın sınıfında __xxx__ biçiminde arar.
Bulursa bu ifadeyi a.__xxx__(b) olarak değerlendirir. Eğer bu sınıfta bu
operatör metodu yoksa bu kez sağ taraftaki operandın sınıfında
__rxxx__ metodunu arar. Eğer bu metodu bulursa ifadeyi b.__rxxx__(a) biçiminde
değerlendirir.
class Number:
def __init__(self, number = 0):
[Link] = number
[Link] = [Link] + n
return result
__radd__ = __add__
def __repr__(self):
return str([Link])
class Number:
def __init__(self, number = 0):
[Link] = number
def __add__(self, n):
result = Number()
[Link] = [Link] + n
return result
__radd__ = __add__
def __repr__(self):
return str([Link])
a = Number(4)
result = a + 3 # a.__add__(3)
print(result)
result = 3 + a # a.__radd__(3)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
@ bir operatör sembolü olmak üzere her zaman a = a @ b ile a @= b aynı anlamda
olmayabilir. Örneğin a + b ile a+= b
int, float gibi temel türlerde eşdeğerdir. Ancak örneğin listelerde bu iki
ifade eşdeğer değildir. Listelerde bilindiği gibi
a += b aslnda a üzerinde eklemeye yol açmaktadır. Yani programcı @=
operatörleri için farklı operatör metotları yazarak
semantik farklılık oluşturabilmektedir. İşte @= operatörleri için __ixxx__
biçiminde başı "i" ile başlayan operatörler bulundurulmuştur.
Yorumlayıcı a @= b ifadesini gördüğünde eğer sınıfta bu işi yapacak bir
__ixxx__ metodu yoksa bu ifadeyi a = a @ b olarak ele alır.
Ancak eğer fınıfta bu işi yapacak bir __ixxx__ metodu varsa ifadeyi a =
a.__ixxx__(b) olarak ele alır. Yani özetle biz bu işlemli
atama operatörleri için operatör metodu yazmak zorunda değiliz. Bu durumda
normal operatör metodu devreye girecektir.
class Number:
def __init__(self, number = 0):
[Link] = number
return result
def __repr__(self):
return str([Link])
Burada __iadd__ metodu aslında toplamı self üzerinde oluşturup yeniden self
nesnesine geri dönmüştür. Örneğin:
a = Number(4)
b = Number(3)
print(id(a)) # 4565685328
a += b # a = a._iadd__(b)
print(id(a)) # 4565685328
Yani özetle a += b gibi bir işlemde eğer sınıfta __iadd__ operatör metodu yoksa
bu işlem a = a.__add__(b) ile varsa
a = a.__iadd__(b) ile eşdeğerdir. Bu durumda a += b işleminde zaten a sınıfı
"değiştirilebilir (mutable)" olmadıktan sonra
ya da a = a + b ile aynı semantik uygulanmadıktan sonda __iadd__ metodunun
yazılmasına da gerek yoktur. Örneğin list sınıfında __iadd__
metodu vardır. Ancak tuple sınıfında yoktur.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
49. Ders 17/10/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
a bir sınıf türünden değişken olmak üzere a(...) biçiminde biz bu değişkeni
sanki fonksiyonmuş gibi fonksiyon çağırma operatörü ile
kullanabiliriz. Ancak bunun için ilgili sınıfın __call__ isiml bir operatör
metodunun bulunuyor olması gerekir. Yani:
a(...)
a.__call__(...)
class Sample:
def __call__(self):
print('this is a test')
s = Sample()
s()
#----------------------------------------------------------------------------------
--------------------------------------
Tabii __call__ metotları ekstra parametrelere ve geri dönüş değerlerine sahip
olabilir.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self, text):
[Link] = text
s = Sample('test')
#----------------------------------------------------------------------------------
--------------------------------------
Fonkisyon yerine sanki fonksiyonmuş gibi sınıf nesnelerinin kullanılması bazı
durumlarda faydalar sağlamaktadır. Sınıf nesneleri
örnek özniteliklerinde bilgi tutabildiği için çağrılar arasında daha tutulan
bilgiler sonraki çağrılarda kullanılabilmektedir.
Fonksiyon yerine sınıf nesnelerinin kullanılması "callback" mekanizmasında
yoğun olarak kullanılabilmektedir. Bir foksiyon bir işi yaparken
arka planda bizim ona verdiğimiz bir fonksiyonu çağırıyor olabilir. Bu
mekanizmaya "callback" mekanizaması, burada çağrılan fonksiyona da "callback"
fonksiyon denilmektedir. İşte bu tür callback fonksiyonların "callable"
nesnelerle oluşturulmasının bazı avantajları vardır.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self, text):
[Link] = text
s = Sample('test')
#----------------------------------------------------------------------------------
--------------------------------------
Biz daha önce listelerini demetlerin, sözlüklerin len fonksiyonuna
sokulabildiğini gördük. Aslında len fonksiyonu ilgili
sınıfın __len__ metodunu çağırmaktadır. Yani:
len(a)
ile
a.__len__()
aynı anlamdadır. Yani len fonksiyonu aslında şöyle yazılmıştır:
def len(a):
return a.__len__()
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self, *args):
[Link] = args
def __len__(self):
return len([Link])
print(s.__len__()) # 7
#----------------------------------------------------------------------------------
--------------------------------------
Kendi sınıfımız türünden bir nesneyi biz int, float, bool, str, complex
türlerine dönüştürmek istediğimizde bu dönüştürme
işlemi için sınıfımızın sırasıyla __int__, __float__, __bool__, __str__ ve
__complex__ metotları çağrılmaktadır. Bu metotların
yalnızca self parametreleri bulunmaktadır. Aslında biz daha önce __str__
metodunu görmüştük. Bu metot str türüne dönüştürülürken
çağrılıyordu. İşte diğer metotlar da diğer türlere dönüştürülürken
çağrılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
class Rational:
def __init__(self, num, denom):
[Link] = num
[Link] = denom
def __float__(self):
return [Link] / [Link];
r = Rational(1, 4)
result = float(r)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıf türünden değişken köşeli parantez operatörüyle kullanıldığında
sınıfın __getitem__ metodu çağrılmaktadır.
Böylece sanki sınıf türündne nesne bir liste gibi köşeli parantez
operatörleriyle kullanılabilmektedir. Sınıf türünden
değişken atama operatörünün solunda da kullanılabilir. Bu durumda da
__setitem__ metodu çağrılmaktadır. __getitem__ metodu self
parametresinin indeks belirten bir parametre daha sahiptir. __setitem__ ise hem
indeks belirten hem de atanacakl değeri belirten
iki ek parametreye sahiptir. Bu iki metodun parametik tapıladı şöyledir:
b = a[index]
b = a.__getitem__(index)
Benzer biçimde:
a[index] = value
a.__setitem__(index, value)
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self, *args):
[Link] = list(args)
def __str__(self):
return str([Link])
def __len__(self):
return len([Link])
#----------------------------------------------------------------------------------
--------------------------------------
Köşeli parantez operatöründe dilimleme yapabilmek için slice isminde bir
sınıftn faydalanılmaktadır. slice sınıfı built-in
bir sınıftır. Bir slice nesnesi tek argümanla, iki argümanla ya da üç argümanla
yaratılabilir. slice sınıfının start, stop ve
step isimli üç örnek özniteliği vardır. Eğer slice nesnesi tek argümanla
yaratılmışsa start ve step None değerinde ancak stop
girilen argüman değerinde olur. slice nesnesi iki argümanla yaratılmışsa start
birinci argümanın, stop ikinci argümanın değerinde olur ancak
step None değrinde olur. slice nesnesi üç argümanla yaratılmışsa argümanlar
sırasıyla start, stop ve step değerlerinde olur.
Örneğin:
>>> s = slice(10)
>>> print([Link], [Link], [Link])
None 10 None
>>> s = slice(10, 20)
>>> print([Link], [Link], [Link])
10 20 None
>>> s = slice(10, 20, 2)
>>> print([Link], [Link], [Link])
10 20 2
result = a[2:4]
Örneğin:
s[2:10:2] = 100
class Sample:
def __init__(self, *args):
[Link] = list(args)
self.a = 0
if isinstance(index, slice):
return [Link][[Link]:[Link]:[Link]]
"""
def __getitem__(self, index):
return [Link][index]
"""
def __str__(self):
return str([Link])
def __len__(self):
return len([Link])
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte Date sınıfına köşeli parantez desteği verilmiştir. Yani bu
örnekte Date nesnesinin gün, ay, yıl bileşenlerine
sanki onlar bir diziymiş gibi erişebilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
class Date:
def __init__(self, day, month, year):
[Link] = day
[Link] = month
[Link] = year
def __repr__(self):
return f'{[Link]}/{[Link]}/{[Link]}'
d[1] = 1
print(d)
#----------------------------------------------------------------------------------
--------------------------------------
Python'a 3'lü versiyonlarla birlikte "... (ellipsis)" biçiminde bir sabit de
eklenmiştir. Önceleri bu sabit yalnızca
köşeli parantez içerisinde kullanılıyordu. Sonradan genelleştirildi. "..."
sabiti aslında Ellipsis isimli anahtar sözcük
olan bir nesneyle temsil edilmektedir. Başka bir deyişle:
a = ...
ile
a = Ellipsis
aynı anlamdadır. Tıpkı None sabitinde olduğu gibi Ellipsis sabiti için de
toplamda tek bir nesne vardır. Yani program içerisindeki
bütün "..." sabitleri ve Ellipsis değişkenleri aslında aynı nesneyi
göstermektedir. Bunun için iki Ellipsis nesnesi == ve !=
operatörleriyle karşılaştırılabilir ya da benzer biçimde is ve is not
operatötleriyle de karşılaştırmalar yapılabilir. Örneğin:
>>> a = ...
>>> a
Ellipsis
>>> type(a)
<class 'ellipsis'>
>>> id(a)
140734384027912
>>> b = Ellipsis
>>> type(b)
<class 'ellipsis'>
>>> id(b)
140734384027912
>>> a == b
True
>>> a is b
True
#----------------------------------------------------------------------------------
--------------------------------------
Bir sınıfın bir metodunun self parametresiyle belirtilen nesnenin
özniteliklerini kullanmadığını düşünelim. Örneğin
tarih işlemlerini yapan Date isimli sınıfta parametresiyle aldığı bir yılın
artık olup olmadığına geri dönen bir isleap metodu
düşünelim. Bu metot aslında bu self parametresini hiç kullanmamaktadır:
class Date:
def __init__(self, day, month, year):
[Link] = day
[Link] = month
[Link] = year
def __repr__(self):
return f'{[Link]}/{[Link]}/{[Link]}'
if [Link](2000):
print('artık')
else:
print('artık değil')
class Date:
def __init__(self, day, month, year):
[Link] = day
[Link] = month
[Link] = year
def __repr__(self):
return f'{[Link]}/{[Link]}/{[Link]}'
def isleap(year):
return year % 4 == 0 and year % 100 != 0 or year % 400 == 0"
if isleap(2000):
print('artık')
else:
print('artık değil')
Ancak NYPT'de belli bir konu üzerinde işlem yapan fonksiyonların o konuya
ilişkin sınıflarda ulundurulması iyi bir tekniktir.
O halde bizim şöyle bir şeye ihitiyacımız vardır. Bir fonksiyon hem bir metot
olsun hem de self parametresine sahip olmasın.
Yani global bir fonksiyon gibi metot olsun. Tabii bu durum self parametresini
kaldırarak sağlanamaz. Çünkü self parametresinin ismi self
olmak zorunda değildir. Metotların birinci parametreleri simi ne olursa olsun
self parametresidir. Dolayısıyla aşağıdaki gibi bir yazım
hatalıdır:
class Date:
def __init__(self, day, month, year):
[Link] = day
[Link] = month
[Link] = year
def __repr__(self):
return f'{[Link]}/{[Link]}/{[Link]}'
def isleap(year):
return year % 4 == 0 and year % 100 != 0 or year % 400 == 0"
if isleap(2000):
print('artık')
else:
print('artık değil')
İşte bir fonksiyon hem bir sınıfın içerisinde olsun hem de self parametresine
sahip olmasın isteniyorsa bu fonksiyonun
static metot yapılması gerekir. static metotlar self parametresine sahip
değildir ancak sınıfın içerisinde bulunurlar.
class Date:
def __init__(self, day, month, year):
[Link] = day
[Link] = month
[Link] = year
def __repr__(self):
return f'{[Link]}/{[Link]}/{[Link]}'
@staticmethod
def isleap(year):
return year % 4 == 0 and year % 100 != 0 or year % 400 == 0
#----------------------------------------------------------------------------------
--------------------------------------
50. Ders 19/10/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Örneğin yukarıdaki Date sınıfına o anki tarihi veren today isimli bir metot
eklemek isteyelim. Bu metot bir nesneyle çağrılmayacaktır.
Yalnızca geri dönüş değeri olarak bir Date nesnesi verecektir. Bu metot tipik
olarak sınıfın static bir metodu biçiminde yazılabilir.
#----------------------------------------------------------------------------------
--------------------------------------
class Date:
def __init__(self, day, month, year):
[Link] = day
[Link] = month
[Link] = year
def __repr__(self):
return f'{[Link]}/{[Link]}/{[Link]}'
@staticmethod
def isleap(year):
return year % 4 == 0 and year % 100 != 0 or year % 400 == 0
@staticmethod
def today():
import datetime
dt = [Link]()
d = Date([Link], [Link], [Link])
return d;
d = [Link]()
print(d)
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfların statik metotlara benzer ismine "sınıf metotları (class methods)"
denilen metotları da olabilmektedir. Sınıf metotlrı ile
statik metotlar tamamen benzer amaçlarla kullanılırlar. Ancak sınıf
metotlarının ekstra bir parametresi olur. Bunların birinci parametreleri
metodun çağrılmasında kullanılan (yani içinde bulunduğu sınıfa ilişkin) sınıfın
type nesnesini almaktadır. Sınıf metotları @classmethod isimli
dekoratörle dekore edilirler. Örneğin:
class Sample:
@staticmethod
def foo():
pass
@classmethod
def bar(cls):
pass
def tar(self):
pass
Burada foo bir static metottur. bar ise bir sınıf metodudur. tar metodu normal
bir metottur. bar metodunun cls parametresi self anlamında değildir.
Bu parametreye sınıfın type nesnesi geçirilir.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
@classmethod
def foo(cls):
print(cls) # <class '__main__.Sample'>
print(cls is Sample) # True
[Link]()
s = Sample()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Taban sınıfta bir sınıf metodu bulunyor olabilir. Bu sınıf metodu türemiş sınıf
ismiyle çağrılırsa metodun cls parametresine
türemiş sınıfın type nesne referansı aktarılır. Böylece biz bu metodun hangi
sınıf ismi ile çağrıldığını anlayabiliriz.
static metotlarda bunu anlayamayız.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
@classmethod
def foo(cls):
print(cls)
print(cls is Sample)
@staticmethod
def bar():
print('bar')
class Mample(Sample):
pass
[Link]()
[Link]()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Python'da özel bir durum vardır. Eğer sınıf içerisindeki bir metot hiç
parametre almazsa Pyton'ın 3'lü versiyonlardında o metot
sanki static bir metot gibi ele alınmaktadır. Bu durum Python'ın 2'li
versiyonlarında geçersizdi. Örneğin:
class Sample:
def foo(): # foo sanki statik metot gibi
print('foo')
[Link]() # geçerli
s = Sample
[Link]() # geçerli
Ancak yine statik metot parametresiz olsa bile onu açıkça @staticmethod
dekoratörü ile dekore etmemiz iyi bir tekniktir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
NYPT'de "dekoratör (decorator)" denilen bir "tasarım kalıbı (design pattern)"
vardır. Python'da bu tasarım kalıbı sentaks
bakımından desteklenerek bir dil öğesi haline getirilmiştir. Bu sayede bazı
tasarımlar daha özlü bir biçimde yapılabilmektedir.
@foo
def bar():
pass
@foo
class Sample:
pass
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki gibi dekore edilmş bir fonksiyon olsun:
@foo
def bar():
pass
def bar():
pass
bar = foo(bar)
Yani @ atomunun yanındaki bir fonksiyondur (genel olarak calllable bir nesne).
Dekore edilmiş fonksiyon bu fonksiyona parametre
yapılıp yeniden bu fonksiyon ismine atanmıştır. Bu durumda artık dekore edilmiş
fonksiyonu çağıran birisi aslında dekoratör fonksiyonun geri
döndürdüğü fonksiyonu çağırmış olacaktır. Örneğin:
def foo(f):
print('foo')
return
@foo
def bar():
print('bar')
Burada aslında foo fonksiyonu çağrılmış geri dönüş değeri yine bar değişkenine
atanmıştır. bar değişkeni yine bar fonksiyonunu belirtmektedir.
Ancak ekranda "foo" yazısı gözükecektir. Aşağıdaki örnekte dekoratör fonkisyonu
başka bir fonksiyonla geri dönmüştür. O halde artık
bar ismi tar ismi ile fonksiyonu ile aynı fonksiyonu belirtmektedir. Ekranda
foo ve tar yazıları görülecektir.
def tar():
print('tar')
def foo(f):
print('foo')
return tar
@foo
def bar():
print('bar')
bar()
#----------------------------------------------------------------------------------
--------------------------------------
def tar():
print('tar')
def foo(f):
print('foo')
return tar
@foo
def bar():
print('bar')
bar()
#----------------------------------------------------------------------------------
--------------------------------------
Dekoratörlerin en önemli kullanım nedeni "araya girme" işlemini
gerçekleştirmektir. Yani biz bir fonksiyon çağırırken aslında başka bir
fonksiyonu çağırırız o fonksiyon da birşeyler çağırdığımızı sandığımız
fonksiyonu çağırır. Böylece biz fonksiyonu her çağırdığımızda
aslında bir araya girme işlemi yapılmış olur. Tabii bunun sağlanması için tipik
olarak dekortör fonksiyonunun iç bir fonksiyonla
geri dönmesi gerekir. Örneğin:
def foo(f):
def bar():
print('araya giren kod')
f()
return bar
@foo
def tar():
print('tar')
tar()
tar()
tar()
#----------------------------------------------------------------------------------
--------------------------------------
def foo(f):
def bar():
print('araya giren kod')
f()
return bar
@foo
def tar():
print('tar')
tar()
tar()
tar()
#----------------------------------------------------------------------------------
--------------------------------------
Tabii yukarıdaki araya girme işlemini daha genel bir biçimde yapabiliriz. Yani
örneğin tar fonksiyonun bir geri dönüş değeri ve
parametreleri de söz konusu olabilir. Bunu yapabilmek için geri döndürülen
fonksiyonun *'lı ve **'lı parametrelere sahip olması
ve bunu yine *'lı ve **'lı argümanlarla iletebilmesi gerekmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
def foo(f):
def bar(*args, **kwargs):
print('araya giren kod')
return f(*args, **kwargs)
return bar
@foo
def tar(a, b, c):
print(f'tar: {a}, {b}, {c}')
count = 0
def foo(f):
def bar(*args, **kwargs):
global count
count += 1
return bar
@foo
def tar():
print('tar')
tar()
tar()
tar()
print(count)
#----------------------------------------------------------------------------------
--------------------------------------
Tabii dekorasyonun illa da @ atomuyla yapılması gerekmez. Biz dekorasyonla
eşdeğer olan çağrıyı kendimiz de yapabiliriz.
Örneğin:
[Link] = foo([Link])
Burada biz sqrt fonksiyonunu dekore etmiş olduk. Görüldüğü gibi dekorasyon için
@ sentaksı yalnızca bir kolaylık sağlamaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
count = 0
def foo(f):
def bar(*args, **kwargs):
global count
count += 1
return bar
import math
[Link] = foo([Link])
result = [Link](10)
result = [Link](20)
result = [Link](30)
print(count)
#----------------------------------------------------------------------------------
--------------------------------------
Daha önce görmüş olduğumuz @staticmethod ve @classmethod da aslına birer built-
in callable nesnelerdir.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
@staticmethod
def foo():
print('foo')
def bar():
print('bar')
bar = staticmethod(bar)
[Link]()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Aslında araya girme işlemi bir sınıf yoluyla da yapılabilir. Genellikle bu
yöntem daha fazla tercih edilmektedir. Örneğin:
@Sample
def foo():
pass
def foo():
pass
foo = Sample(foo)
Burada eğer Sample bir sınıf ise aslında artık foo Sample sınıfı türünden bir
nesne haline gelecektir. O halde biz bu foo'yu
çağırdığımızda aslında Sample sınıfının __call__ metodu çağrılacaktır.
class Sample:
def __init__(self, f):
self.f = f
@Sample
def foo():
print('foo')
foo()
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self, f):
self.f = f
@Sample
def foo():
print('foo')
foo()
#----------------------------------------------------------------------------------
--------------------------------------
51. Ders 24/10/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Görüldüğü gibi dekoratörler aslında arka planda birtakım işlemlerin pratik bir
biçimde yapılması için araç oluşturmaktadır.
Örneğin bir dekoratör fonksiyonun çalışma zamanını ölçerek geri dönüş değeri
biçiminde bize verebilir. Bu durumda dekore edilmiş
fonksiyonun geri dönüş değerine ilaveten fonksiyonun çalışma zamanı da bize
veriliyor olabilir.
#----------------------------------------------------------------------------------
--------------------------------------
import time
class Profiler:
def __init__(self, f):
self.f = f
@Profiler
def add_total(n):
[Link](1000)
total = 0
for i in range(n):
total += i
return total
#----------------------------------------------------------------------------------
--------------------------------------
Aslında daha önce görmüş olduğumuz statc ve class metotların yazılmasında
kullanılan @staticmethod ve @classmethod
sentaksı bir dekoratördür.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
@staticmethod
def foo():
print('static method')
class Mample:
def foo():
print('static method')
foo = staticmethod(foo)
[Link]()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Dekoratörler fonksiyonların yanı sıra sınıf tanımlamalarında da
kullanılabilmektedir. Bunlara sınıf dekoratörleri denilmektedir.
Sınıf dekoratörlerin genel biçimi şöyledir:
@decorator
class class_name:
pass
@decorator
class class_name:
pass
class class_name:
pass
class_name = decorator(classd_name)
#----------------------------------------------------------------------------------
--------------------------------------
def foo(c):
print('foo')
return c
@foo
class Sample:
pass
#----------------------------------------------------------------------------------
--------------------------------------
Sınıf dekoratörleri sayesinde örneğin biz bir sınıf türünden nesne
yaratıldığında araya girip bir şeyler yapabiliriz.
Genellikle burada araya giren kodlar nesne üzerinde bazı öznitelikleri de arka
planda yaratmaktadır.
def foo(c):
def bar():
print('bar')
return c()
return bar
@foo
class Sample:
def disp(self):
print('disp')
# Sample = foo(Sample)
s = Sample()
[Link]()
k = Sample()
[Link]()
def foo(c):
def bar(*args, **kwargs):
print('bar')
return c(*args, **kwargs)
return bar
@foo
class Sample:
def disp(self):
print('disp')
# Sample = foo(Sample)
s = Sample()
[Link]()
k = Sample()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Tabii yukarıdaki örnekte iç fonksiyon kullanmak yerine dekoratör olarak bir
sınıf da kullanabilirdik. Genel olarak dekoratör
olarak sınıf kullanmak tercih edilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
class foo:
def __init__(self, cls):
[Link] = cls
@foo
class Sample:
def disp(self):
print('disp')
# Sample = foo(Sample)
s = Sample()
[Link]()
k = Sample()
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Bu tür sınıf dekoratörleri bazı framework'ler tarafından bazı karmaşık
işlemleri yapmak için kullanılabilmektedir.
Örneğin bir sınıf içerisine gizlice bir metot eklemek, sınıf türünden
nesnelerin içerisine öznitelikler eklemek
sık karşılaşılan durumlardandır.
Aşağıda örnekte bir sınıfa dekoratör yoluyla aslında o sınıfta olmayan bir
metot eklenmiştir. Aynı zamanda o sınıf türünden
nesne her yaratıldığında ona arka planda bir örnek özniteliği de eklenmiştir.
Tabii bu örnekte neden sınıfa bu eklemelerin yapıldığını
merak edebilirsiniz. Bu tür uygulamalar genellikle ileri düzey frtamework'lerde
karşımıza çıkmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
def test(self):
print('test')
class foo:
def __init__(self, cls):
[Link] = cls
[Link] = test
def __call__(self, *args, **kwargs):
obj = [Link](*args, **kwargs)
obj.x = 'this is a test'
return obj
@foo
class Sample:
def disp(self):
print('disp')
# Sample = foo(Sample)
s = Sample()
[Link]()
print(s.x)
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki örnekler bu tür temalarla ilk kez karşılaşanlara kavramsal olarak
karmaşık ve biraz da soyut gelmektedir.
Bu konuyla ilgili olan diğer ileri bir konu da "meta class" konusudur. Biz bu
"meta class" konusunu Python Uygulamaları kursunda
göreceğiz. Böylece kişilerin anlaması bazı konuları gördükten ve uygulamaları
yaptıktan sonra daha kolay olacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Dekoratörler parametre de alabilmektedir. Örneğin log isimli bir dekoratör
olsun. Bu dekoratör bir fonksiyon çağrıldığında
çağrılma zamanını, fonksiyonun çalışma zamanını bir log dosyasına yazıyor
olsun. ama onun yazacağı dosyayı biz belirleyecek olalım.
İşte bu tür durumlarda dekoratörlerin parametre alması gerekebilmektedir.
Örneğin:
@log('[Link]')
def foo():
pass
@decorator(a, b, c)
def foo():
pass
Burada aslında parametreli dekoratör çağrılmış bunun geri dönüş değeri yeniden
çağrılmıştır. Yani parametreli dekoratörün çağrılması sonucunda
yine bir callable nesne elde edeilmelidir.
foo()
foo()
"""
Eşdeğeri
def foo():
print('foo')
foo = bar(10, 20, 30)(foo)
"""
Ekran çıktısı şöyle oalacaktır:
bar
tar
her çağrışta araya giren kod dekoratör parametrelerini kullanabiliyor: 10, 20, 30
her çağrışta araya giren kod dekoratör parametrelerini kullanabiliyor: 10, 20, 30
"""
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte her ne kadar henüz dosya işlemlerini görmediysek de fonksiyon
çağrıldıkça bir dosyaya log bilgilerini yazan
örnek bir parametreli dekoratör verilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
import datetime
import time
def log(file):
def loglog(f):
def logloglog(*args, **kwargs):
now = [Link]()
[Link](f'{f.__name__} => {[Link]}:{[Link]}:{[Link]} ')
start = [Link]()
retval = f(*args, **kwargs)
stop = [Link]()
[Link](f'({stop - start})\n')
[Link]()
return retval
return logloglog
return loglog
f = open('[Link]', 'w')
@log(f)
def get_total(n):
total = 0
for i in range(n):
total += i
return total
result = get_total(100000000)
print(result)
result = get_total(1000000)
print(result)
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Parametreli dekoratörler de bir sınıf biçiminde yazılabilirler. Aşağıda bu
işlemin nasıl yapılabileceğine ilişkin bir
fikir verilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
class decorator:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
foo()
foo()
#----------------------------------------------------------------------------------
--------------------------------------
Dekoratör olarak sınıf kullanarak yukarıdaki log örneğini de aşağıdaki gibi
yapabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
import datetime
import time
class log:
def __init__(self, file):
[Link] = file
return self.do_log
f = open('[Link]', 'w')
@log(f)
def get_total(n):
total = 0
for i in range(n):
total += i
return total
result = get_total(100000000)
print(result)
result = get_total(1000000)
print(result)
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Sonuç olarak biz bir fonksiyonundekore edildiğini gördüğümüzde şunu
düşünmeliyiz: "Bu fonyksiyonu çağırdığımda aslında
ben muhtemelen başka bir fonksiyonu çağırmış olacağım. Ama o fonksiyon da benim
fonksiyonumu çağıracak. Fakat bu arada
benim faydama bazı şeyler de arka planda yapılmış olacak". Benzer biçimde bir
sınıf dekoratörünü gördüğümüzde de
şunu düşünmeliyiz: "Bu sınıfa benim eklediklerimden başka şeyler de eklenşyor
olabilir. Ben bu sınıf türünden nesne yarttığımda
arka planda başka şeyler de yapılıyor olabilir. Yarattığım nesneye bazı
öznitelikler de ekleniyor olabilir."
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
52. Ders 26/10/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
İngilizce "exception" sözcüğü "istisna" anlamına gelmektedir. Ancak bu sözcük
yazılımda "programın çalışma zamanı sırasında oluşan
problemlemli durumları" anlatmak için kullanılmaktadır. Exception mekanizması
genel olarak nesne yönelimli programlama dillerinde bulunmaktadır.
Prosedürel dillerin çoğunda bu mekanizma yoktur. Exception programın çalışma
zamanına ilişkin bir kavramdır. Etimolojik kökeni
donanımsal sorunlara dayanmaktadır.
>>> int('ali')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'ali'
>>> 10 + 'ali'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>> a = [1, 2, 3]
>>> a[10]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
try: <suit>
try bloğunu bir ya da birden fazla except bloğu izleyebilir. Ya da try bloğunu
hiç except bloğu olmadan finally bloğu da izleyebilir.
eğer try bloğunu except bloğu izliyorsa finally bloğu isteğe bağlı olarak
except bloklarının sonuna yerleştirilebilir.
except anahtar sözcüğünün yanında bie exception sınıf ismi bulunur. Eğer birden
fazla exc eption sınıf ismi bulunduurlacaksa
parantezler içerisinde demet sentaksıyla bu sınıfların belirtilmesi gerekir.
Exception sınıf isimlerinden sonra isteğe bağlı olarak
as anahtar sözcüğü ve değişken ismi getirilebilir. Örneğin geçerli except
blokları şöyle olabilir:
except TypeError:
pass
except IndexError as e:
pass
except:
pass
try bloğunu bir ya da birden fazla except bloğu izleyebilir ya da finally bloğu
izleyebilir. finally bloğu her zaman en sonda
bulunmak zorundadır. Örneğin:
try:
pass
except IndexError:
pass
except ValueError:
pass
finally:
pass
try:
pass
finally:
pass
try:
pass
except IndexError:
pass
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Exception mekanizması şöyle çalışlmaktadır: Programın akışık try bloğuna
girdikten sonra bir exception kontrolü
uygulanır. Akış try bloğuna girdikten sonra herhangi bir yerde exception
oluşursa akış bir goto işlemi gibi oluşan exception'ın türüne
uygun olan except bloğuna aktarılır. İlgili except bloğu çalıştırılır, diğer
except blokları atlanır. Akış except bloklarının sonundan
devam eder. Eğer programın akışı try bloğuna girdikten sonra hiç exceptionb
oluşmazsa akış try bloğunun sonuna geldiğinde except
blokları atlanır ve akış except bloklarının sonundan devam eder. Yani except
blokları "exception oluşursa çalıştırılmaktadır". Exception
oluşmazsa onların bir işlevi kalmaz. Bir exception oluştuğunda akış o exception
ile aynı türden except bloğuna aktarılmaktadır.
except bloklarının yalnızca bir tanesi çalıştırılmaktadır. (Yani bunlar adeta
match/case gibi işlem görmektedir).
Exception nerede oluşursa oluşsun akış except bloğuna aktarıldıktan sonra bir
daha geri dönmez. except bloklarının sonundan devam eder.
finally bloğu ve parametresiz except bloğu daha ileride ele alınacaktır. Bir
exception oluştuğunda bu exception'ın türüne uygun bir except
bloğunun bulunyor olması gerekir. Aksi takdirde yine program çökecektir.
#----------------------------------------------------------------------------------
--------------------------------------
def tar():
print('tar başladı')
int('xxxxx') # ValueError oluşacak
print('tar bitti')
def bar():
print('bar başladı')
tar()
print('bar bitti')
def foo():
print('foo başladı')
bar()
print('foo bitti')
try:
foo()
except ValueError:
print('ValueError yakalandı')
print('program devam ediyor')
#----------------------------------------------------------------------------------
--------------------------------------
Python'da en çok karşılaşılan altı Exception sınıfı vardır: ValueError,
TypeError, IndexError, KeyError, NameError ve AttributeError. Bir fonksiyonun ya da
metodun parametresi uygun girilmemişse genel olarak ValueError oluşur. Ancak
bir fonksiyona argüman olarak yanlış bir tür verilmişse
ya da bir işlemde türler uygunsuz ise TypeError oluşmaktadır. Örneğin
int('xxx') işlemi ValueError oluşturur. Çünkü burada int fonksiyonu
str türünden bir argüman alabilir. Ama aldığı argümanın içeriği hatalıdır.
Fakat örneğin z bir complex nesnesi olmak üzere int(z)
gibi bir işlem TypeError oluşturur. Çünkü int fonksiyonu argüman olarak complex
türünden bri nesne alamaz. Yani tür uygun ancak
içerik uygun değilse ValueError, tür uygun değilse TypeError oluşmaktadır. Bir
liste, demet gibi indekslenebilir nesnelerde
indeksin uygun bir değer belirtmemesi durumunda (örneğin 10 elemanlı bir
listede 20'inci elemana erişmek istediğimiz bir durumda)
IndexError oluşmaktadır. Sözlük tarzı bir veri yapısında köşeli parantezler
içerisinde anahtar verildiğinde böyle bir anahtar
bulunamadıysa KeyError oluşmaktadır. Bir değişken kullanıldığında henüz o
değişken yaratılmamışsa NameError oluşmaktadır.
Bir sınıf türünden değişken nokta operatörüyle kullanıldığında o değişken
yaratılmış olabilir ama nokta operatörünün sağındaki
öznitelik yaratılmamış olabilir. Bu durumda ise AttributeError exception'ı
oluşmaktadır. Yani a.b gibi bir ifadede a yoksa NameError ancak
a var fakat b yoksa AttributeError oluşur. Tüm sınıflar built-in sınıflardır.
Yani hiçbir şeyi import etmeden bu sınıfları kullanabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte klavyeden bir sayı okunmak istenmiştir. Kullanıcı sayı olarak
yanlış bir giriş yapmışsa sayı yeniden
istenmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
while True:
try:
val = int(input('Bir sayı giriniz:'))
print(val * val)
break
except ValueError:
print('Girdiğiniz sayı geçersiz!')
#----------------------------------------------------------------------------------
--------------------------------------
Exception'ı oluşturan asıl deyim raise deyimidir. Exception'lar kendiliğinde
oluşmaz. Exceptyion'ı oluşturmk için raise
anahtar sözcüğünü kullanmak gerekir. raise deyiminin genel biçimi şöyledir:
raise anahtar sözcüğünün yanında exception nesnesi bulunur. Bir problem ortaya
çıktığında programcı bir exception nesnesi
oluşturur. Probleme ilişkin bazı bilgileri eğer gerekiyorsa o nesnenin örnek
özniteliklerine yazar ve o nesneyle raise işlemi yapar.
C++ gibi bazı programlama dillerinde bu tür işlemlerde herhangi bir türden
nesneler kullanılabilmektedir. Ancak Java gibi, C# gibi,
Python gibi dillerde exception nesneleri özel sınıflardan türetilmiş olan
sınıflar türünden olmak zorundadır.
def foo(a):
print('foo başladı')
if a < 0:
raise ValueError()
print('foo bitti')
try:
foo(1)
print('her şey yolunda')
except ValueError:
print('hata oluştu')
print('program bitiyor')
#----------------------------------------------------------------------------------
--------------------------------------
Programcı kendi exception sınıflarını yazabilir ve onları kullanabilir. Bir
sınıf ile raise işleminin yapılabilmesi için ve o sınıfın
except anahtar sözcüğünün yanında kullanılabilmesi için BaseException isimli
built-in bir sınıftan türetilmiş olması gerekir.
Tabii bizim kendi exception sınıflarımızı hemen BaseException sınıfından
türetmemiz gerekmez. Önemli olan o sınıftan yukarıya çıkıldığında
BaseException sınıfının bulunuyor olmasıdır. Örneğin biz ValueError sınıfından
da sınıf türetip onu kullanabiliriz.
BaseException sınıfından türetilmiş olan en önemli sınıf Exception sınıfıdır.
Yukarıda sözünü ettiğimiz TypeError, ValueError, NameError,
AttributeError sınıfları Exception sınıfından türetilmiştir. IndexError ve
KeyError sınıfları LookupError sınıfından, LookupError
sınıfı da Exception sınıfından türetilmiş durumdadır.
class NegativeError(ValueError):
pass
def foo(a):
print('foo başladı')
if a < 0:
raise NegativeError()
print('foo bitti')
try:
foo(-1)
print('her şey yolunda')
except NegativeError:
print('hata oluştu')
print('program bitiyor')
#----------------------------------------------------------------------------------
--------------------------------------
raise deyiminde yalnızca exception sınıfının ismi de yazılabilir. Bu durumda
yorumlayıcı bu sınıf türünden nesneyi yaratıp o nesne ile
raise işlemş yapmaktadır. Yani:
raise ValueError
ile
raise ValueError()
#----------------------------------------------------------------------------------
--------------------------------------
Exception sınıflarının __init__ metotları *args parametresine sahiptir ve bu
args parameresi nesnenin args isimli özniteliğinde
saklanmaktadır. Yani biz bir exception nesnesini istediğimiz kadar argüman
girerek yaratabiliriz. Sonra bu argümanları
sınıfın args örnek özniteliğinden bir demet biçiminde elde edebiliriz. Exception
sınıflarının __repr__ ve __str__ metotları
yazılmış durumdadır. __str__ metodu eğer nesne yaratılırken tek bir argüman
girilmişse o argümanı bir yazı olarak verir.
Ancak birden fazla argüman girilmişse o argümanları demet sentaksıyla bir yazı
olarak vermektedir. Örneğin:
>>> ve = ValueError('test')
>>> str(ve)
'test'
>>> ve = ValueError('test', 123)
>>> str(ve)
"('test', 123)"
>>> ve
ValueError('test', 123)
class ValueError(Exception):
def __init__(self, *args):
super(*args)
class NegativeError(Exception):
def __init__(self, *args):
super().__init__(*args)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
53. Ders 31/10/2022 -
Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir exception raise deyimi ile fırlatıldığında oluşan exception yakalandığında
exception nesnesi except bloğunun as
ile belirtilen değişkenine atanmaktadır. Böylece programcı oluşan exception
nesnesini as ile elde edip içerisindeki bilgilere
args özniteliği ile erişebilmektedir. Örneğin:
try:
foo()
except ValueError as e:
print(e)
def foo(a):
if a < 0:
raise ValueError('value must not be negative', 123)
print('ok')
try:
foo(-4)
except ValueError as e:
print(f'Exception occured with message "{[Link][0]}", error code: {[Link][1]}')
#----------------------------------------------------------------------------------
--------------------------------------
Programcı kendi exception sınıflarını oluşturup yine aynı biçimde kendi
exception nesneleriyle exception fırlatabilir.
#----------------------------------------------------------------------------------
--------------------------------------
class NegativeError(Exception):
def __init__(self, *args):
super().__init__(*args)
def foo(a):
if a < 0:
raise NegativeError('value must not be negative', 123)
print('ok')
try:
foo(-4)
except NegativeError as e:
print(f'Exception occured with message "{[Link][0]}", error code: {[Link][1]}')
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Programcı genellikle zaten var olan exception sınıflarını kullanır. Ancak bazen
var olan exception sınıfları yetersiz kalabilmektedir.
Bu tür durumlarda programcılar kendi exception sınıflarını yazarlar. Standart
kütüphanede de built-in exception sınıflarının dışında
o modüle özgü çeşitli exception sınıfları kullanılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi birden fazla türdeki exception demet sentaksıyla tek bir
except bloğu tarafından yakalanabiliyordu.
Bu durumda yine as cümleceği kullanılabilir. Ancak tabii yakalanan exception'ın
türünü programcı ancak isinstance fonksiyonuyla
anlayabilir. Örneğin:
try:
foo(1000)
except (ValueError, TypeError) as e:
print(e)
def foo(a):
if not isinstance(a, int):
raise TypeError('argument must be int')
print('Ok')
try:
foo(1000)
except (ValueError, TypeError) as e:
print(e)
#----------------------------------------------------------------------------------
--------------------------------------
Türemiş sınıf türünden oluşan bir exception taban sınıf türünden bir except
bloğu tarafından yakalanabilir. Örneğin bu sayede
biz tüm exception'ları BaseException ya da Exception sınıflarıyla
yakalaybiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
def foo(a):
if not isinstance(a, int):
raise TypeError('argument must be int')
print('Ok')
try:
foo(1000)
except Exception as e:
print(e)
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte klavye kuması sırasında oluşan hatalar doğrudan Exception
sınıfı ile yakalanmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
try:
val = int(input('Bir sayı giriniz:'))
print(val * val)
except Exception as e:
print(e)
#----------------------------------------------------------------------------------
--------------------------------------
Bazen programcı bazı özel exception'ları ilgili exception sınıfı türünden except
blokları ile yakalayıp işlemek isteyebilir.
Ancak geri kalan exception'ların hepsini Exception parametreli bir except bloğu
ile işlemek isteyebilir. Yani başka bir deyişle
hemen taban sınıfa ilişkin hem de türemiş sınıfa ilişkin except bloklarını
bulundurabilir. İşte bu tür durumlarda except bloklarına yukarıdan
aşağıya doğru baklıdığı için türemiş sınıfa ilişkin except bloğunun taban sınıfa
ilişkin except bloğunun yukarısında bulundurulması uygun olur.
Aksi takdirde taban sınıfa ilişkin except bloğu türemiş sınıfa ilişkin
exception'ları da yakalar ve durum anlamsızlaşır.
Örneğin:
try:
foo('ankara')
except ValueError as e:
pass
except Exception as e:
pass
try:
foo('ankara')
except Exception as e:
pass
except ValueError as e:
pass
#----------------------------------------------------------------------------------
--------------------------------------
Programın akışı birden fazla kez trye bloğuna girebilir. Bu durumda bir
exception oluşursa try bloklarının except kısımları içten
dışa doğru (yani son girilen try bloğundan ilk girilene doğru) gözden
geçirilir. Eğer exception iç try bloğunun except blokları tarafından
yakalanırsa orada ele alınmış alur. Artık dış try bloklarına bu exception
yansıtılmaz. Eğer exception dış hiçbir try bloğunun
except blokları tarafından yakalanamazsa program çöker.
#----------------------------------------------------------------------------------
--------------------------------------
def foo(a):
print("foo başladı")
try:
bar(a)
except ValueError:
print("ValueError foo'da yakalandı")
print("foo bitiyor")
def bar(a):
print('bar başladı')
print('bar bitiyor')
try:
foo(1000)
print('Ok')
except ValueError:
print("ValueError dışarıda yakalandı")
except TypeError:
print("TypeError dışarıda yakalandı")
print('---------------------------------------')
try:
foo('ali')
print('Ok')
except ValueError:
print("ValueError dışarıda yakalandı")
except TypeError:
print("TypeError dışarıda yakalandı")
#----------------------------------------------------------------------------------
--------------------------------------
Özel bir except bloğu da parametresiz except bloğudur. Parametresiz except
blokları tüm exception'ları yakalar. Ancak parametresiz
except blokları bulundurulacaksa tüm except bloklarının sonunda
bulundurulmalıdır. Örneğin:
try:
foo()
except ValueError:
pass
except TypeError:
pass
except:
pass
#----------------------------------------------------------------------------------
--------------------------------------
try bloğunun bir de finally bloğu olabilmektedir. Bu blok parametresiz olmak
zorundadır. finally bloğu except blokları ile birlikte
ya da except blokları olmadan kullanılabilir. Her durumda finally bloğu en
sonda olmak zorundadır. Örneğin:
try:
foo()
except ValueError:
pass
except TypeError:
pass
except:
pass
finally:
pass
def foo(a):
print('foo başladı')
print('foo bitiyor')
try:
foo(10)
except ValueError:
print("ValueError oluştu...")
except:
print("Diğer bir exception oluştu...")
finally:
print('finally...')
print('------------------')
try:
foo('ankara')
except ValueError:
print("ValueError oluştu...")
except:
print("Diğer bir exception oluştu...")
finally:
print('finally...')
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi mademki finally bloğu exception oluşsa da oluşmasa da
çalıştırılmaktadır. O halde finally bloğu yerine oradakileri
except bloklarının sonuna taşırsak değişen ne olacaktır? Örneğin:
try:
foo()
except:
print('exception oluştu')
finally:
print('finally bloğunda bir işlem yapılıyor')
try:
foo()
except:
print('exception oluştu')
print('finally bloğunda bir işlem yapılıyor')
İşte finally her zaman çalıştırılmaktadır. Örneğin bir dönü içerisinde try
bloğu olabilir. Bu döngüden dolayısıyla try bloğundan
break ya continue deyimleriyle çıkılmış olabilir. Bu durumda yine finally bloğu
çalıştırılır. Örneğin:
while True:
try:
val = int(input('Bir değer giriniz:'))
if val == 0:
break
print(val * val)
except:
print('giriş geçersiz!')
finally:
print('finally işlemi yapılıyor')
while True:
try:
val = int(input('Bir değer giriniz:'))
if val == 0:
break
print(val * val)
except:
print('giriş geçersiz!')
print('finally işlemi yapılıyor')
def foo():
try:
val = int(input('Bir değer giriniz:'))
if val == 0:
return
print(val * val)
except:
print('giriş geçersiz!')
finally:
print('finally işlemi yapılıyor')
foo()
Görüldüğü gibi try bloğundan nasıl çıkılmış olursa olsun her zaman finally
bloğu çalıştırılmaktadır.
Ayrıca iç içe try bloklarının olduğu durumda exceptin oluştuğunda exception dış
try bloğu tarafından yakalansa bile
iç try bloklaının finally blokları yine çalıştırılmaktadır. Örneğin:
def foo(a):
print("foo başladı")
try:
bar(a)
except ValueError:
print("ValueError foo'da yakalandı")
finally:
print("foo'daki finally bloğu çalıştırılıyor")
print("foo bitiyor")
def bar(a):
print('bar başladı')
print('bar bitiyor')
try:
foo('test')
print('Ok')
except TypeError:
print("TypeError dışarıda yakalandı")
Buradan çıkan sonuç şudur: finally bloğu her zaman çalıştırılır. Yani
çalıştırılması garanti edilmiştir. İşte finally
bloğu birtakım tahsisatların garantili bir biçimde boşaltılmasını sağlamak için
kullanılmaktadır. Yani programın akışı başka bir yere
gidip ilgili bağlamdan koparsa yine de boşaltım gerçekleşir. Örneğin:
def foo():
f = None
try:
f = open('[Link]')
s = [Link]()
#...
s = [Link]()
#...
finally:
if f:
[Link]()
Burada programcının read metodundaki IO hatalarını dışarıda ortak bir yerde ele
aldığını düşünelim. read metotlarının birinde IO hatası
olduğunda akış başka bir faaliyet alanına gidecek dolayısıyla dosyanın
kapatılma imkanı kalmayacaktır. (Gerçi dosya bu tür sınıfların
__del__ metotlarında da kapatılmaktadır ancak çöp toplayıcı mekanizmanın da
nasıl gerçekleştirileceği belli değildir.) İşte bu tür
durumlarda kaynakların finally bloğunda boşaltılması en uygun durumdur.
Özellikle çöp toplama mekanizmasının etkili olmadığı Python dünyasının
dışındaki tahsisatların garantili boşaltılması için finally bloğuna gereksinim
duyulmaktadır. Programcı herhangi bir noktada
exception oluştuğunda o zaman akadar yapılan birtakım işlemleri geri almak
istiyorsa bunu finally bloğunda yapmalıdır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
raise deyiminde raise anahtar sözcüğünün yanına ifade getirilmeyebilir. Buna
"reraise" denilmektedir. Bu biçimdeki reaise
deyimi ancak except bloklarında kullanılabilir. Reraise işlemi aslında
"exception'ın yakalanmamış hale getirmektedir. Başka bir deyişle
aynı exception aynı parametreyle yeniden fırlatılır. Reraise işlemleri tipik
olarak iç içe try bloklarında exception'ın içteki try bloğunun
except blokları tarafından yakalanması durumunda aynı yakalamanın dışarıda da
yapılması amacıyla kullanılmaktadır.
def foo(a):
print("foo başladı")
try:
bar(a)
except ValueError:
print("ValueError foo'da yakalandı")
except TypeError:
print("TypeError foo'da yakalandı")
raise # reraise işlemi exception aynı nesneyle yeniden fırlatılıyor
finally:
print("foo'daki finally bloğu çalıştırılıyor")
print("foo bitiyor")
def bar(a):
print('bar başladı')
print('bar bitiyor')
try:
foo('test')
print('Ok')
except TypeError:
print("TypeError dışarıda yakalandı")
#----------------------------------------------------------------------------------
--------------------------------------
Python'ın standart sys modülünde exc_info isimli bir fonksiyon vardır. Bu
fonksiyon bir except bloğu içerisinde çağrılabilmektedir.
Bu fonksiyon bize üçlü demet vermektedir. Demetin ilk elemanı oluşan
exception'ın türünü, ikinci elemanı exception nesnesini ve üçüncü
elemanı da trace bilgisini verir. Programcı isterse as cümleciği ile elde
edebileceği bu bu bilgileri bu fonksiyonla da elde edebilir.
Örneğin parametresiz except bloklarında as cümleciği kullanılamayacağından
dolayı exception nesnesi ve türü bu yolla elde edilebilmektedir.
Bu fonksiyonu çağırmanın bir exception oluşmamışsa anlamı yoktur. Bu durumda
fonksiyon üç eşemanı da None olan bir demete geri dönmektedir.
Yani bu fonksiyon except bloklarında gerekirse çağrılmalıdır.
import sys
def foo(a):
print('foobaşladı')
print('foo bitiyor')
try:
foo('test')
except:
exc_type, exc_obj, exc_trace = sys.exc_info()
print(exc_type, exc_obj.args[0], exc_trace)
#----------------------------------------------------------------------------------
--------------------------------------
sys.exc_info fonksiyonundan elde edilen trace bilgisi bir bağlı liste
biçimindedir ve bu bilgi exception'ın program kodunun sırasıyla nerelerinde
oluşarak ortaya çıktığını bize gösterir. trace nesnelerinin tb_next
öznitelikleri bir sonraki tarce nesnesini belirtir. tb_linno özniteliği
ise exception oluşumuna ilişkin fonksiyon çağrılarını belirtir. Bu bilgiler
genellikle debugger gibi, REPL gibi ortamlar tarafından kullanılmaktadır.
def foo():
bar()
def bar():
tar()
def tar():
raise ValueError()
try:
foo()
except:
_, _, trace = sys.exc_info()
while trace:
print(trace.tb_lineno)
trace = trace.tb_next
#----------------------------------------------------------------------------------
--------------------------------------
54. Ders 02/11/2022 -
Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
İçerisinde bilgilerin bulunduğu ikincil belleklerdeki bölgelere "dosya (file)"
denilmektedir. Dosya aslında işletim sistemleri tarafından
oluşturulan yüksek seviyeli bir organizasyona ilişkin bir terimdir. İşletim
sistemlerinin dosya işlemlerini yapan bölümlerine
"dosya sistemi (file system)" denilmektedir. Dosyaların parçaları aslında
disklerdeki bloklarda bulunur. İşletim sistemi bir biçimde
hangi dosyaların parçalarının diskte hangi bloklarda olduğunu tutmaktadır. Bu
tutuş biçimine göre dosya sistemleri çeşitli isimlerle
anılmaktadır. Örneğin NTFS, Ext-2, HFS gibi. İşletim sistemleri uygulama
programcılarına dosyaları sanki ardışıl byte topluluklarından
oluşan varlıklarmış gibi göstermektedir. Dosya işlemleri hangi dille yapılıyor
olursa olsun eninde sonunda işletim sistemlerinin sistem
fonksiyonları yoluyla gerçekleştirilmektedir. Çünkü dosya işlemlerinin asli
sorumlusu işletim sistemleridir.
Mutlak yol ifadeleri her zaman kök dizinden itibaren yer belirtmektedir. Göreli
yol ifadeleri ise prosesin "çalışma dizininden (current working directory)"
itibaren yer belirtir. Çalışmakta olan programlara "prosess (process)"
denilmektedir. İşletim sistemleri her proses için bir
çalışma dizini tutmaktadır. İşte göreli yol ifadeleri bu çalışma dizininden
itibaren bir yer belirtir. Örneğin programımızın
çalışma dizini "C:\temp" olsun. Biz de "a\b\[Link]" biçiminde göreli bir yol
ifadesi vermiş olalım. Bu dosya işletim sistemi tarafından
"c:\temp\a\b\[Link]" biçiminde ele alınacaktır. Eğer yol ifadesi "\b\[Link]"
biçiminde olsaydı prosesin çalışma dizininin bir
önemi kalmayacaktı. Çünkü mutlak yol ifadeleri her zaman kök dizinden itibaren
bir yer belirtmektedir. Örneğin "[Link]" biçimindeki bir
yol ifadesi göreli bir yol ifadesidir. Prosesin çalışma dizininde aranır.
#----------------------------------------------------------------------------------
--------------------------------------
Prosesin çalışma dizini "os" modülündeki getcwd fonksiyonu ile bir string
biçiminde elde edilebilir.
#----------------------------------------------------------------------------------
--------------------------------------
import os
cwd = [Link]()
print(cwd)
#----------------------------------------------------------------------------------
--------------------------------------
Prosesin çalışma dizini yine "os" modülü içerisindeki chdir fonksiyonu ile
değiştilebilmektedir. Eğer bu fonksiyonda geçersiz bir dizin
girilirse FileNotFoundError isimli exception oluşmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
import os
cwd = [Link]()
print(cwd)
[Link](r'c:\windows')
cwd = [Link]()
print(cwd)
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi prosesin çalışma dizinini [Link] fonksiyonu ile hiç değiştirmemişsek
program çalışmaya başladığında default çalışma dizini
neresi olmaktadır? İşte aslında işletim sistemlerinde prosesin çalışma dizini
üst prosesten alınmaktadır. Ancak üst proses bunu değiştirebilmektedir.
Python'da eğer komut satırında programı çalıştırıyorsak o anda komut satırında
hangi dzindeysek prosesin default çalışma dizini o dizin olur.
Spyder IDE'sinde default çalışma dizini Python kaynak dosyasının bulunduğu
dizindir. PyCharm'da ise proje dizini default çalışma dizinidir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir dosya ile işlem yapmadan önce dosyanın açılması gerekir. İşlemler bitince de
dosya kapatılmalıdır. Eğer dosya kapatılmazsa
program bittiğinde zaten otomatik olarak kapatılır. Dosyanın açılması sırasında
işletim sistemi bazı ön işlemler yapmaktadır.
Python'da dosya built-in open fonksiyonu ile açılır. open fonksşyonunun birinci
parametresi açılacak dosyanın yol ifadesini belirtir.
İkinci parametre ise açış modunu belirtmektedir. Açış modu "dosya üzerinde
yapılmak istenen eylemi" belirtmektedir. Açış modunda
kullanılabilecek mod belirten yazılar şunlardır:
'r': Bu modda ancak var olan dosyalar açılabilir. Açılan dosyadan yalnızca okuma
yapılabilir. Dosya yoksa exception olur.
Açış modu hiç girilmezse default durum 'r' kabul edilmektedir.
'w': Bu modda dosya yoksa yaratılır ve açılır, dosya varsa sıfırlanarak açılır
(yani dosyanın içerisindekiler silinir). Bu modda
dosyaya ancak yazma yapılabilir.
'r+': Bu modda yine var olan dosya açılabilir. Ancak dosyaya hem okuma hem de
yazma yapılabilir. Dosya yoksa exception oluşur.
'w+': Bu modda yine dosya yoksa yaratılır ve açılır, dosya varsa sıfırlanarak
açılır. Ancak hem okuma hem yazma yapılabilir.
'a': Bu modda eğer dosya yoksa yaratılır ve açılır, dosya varsa olan dosya
açılır (sıfırlanmaz). Ancak bu modda dosyaya her yazılan
hep sona eklenir. Dosyanın başka bir yerine bir şey yazmak mümkün değildir.
Dosyadan okuma yapılamaz.
'a+': Bu modda da eğer dosya yoksa yaratılır ve açılır, dosya varsa olan dosya
açılır (sıfırlanmaz). Ancak bu modda dosyaya her yazılan
hep sona eklenir. Dosyanın başka bir yerine bir şey yazmak mümkün değildir.
Ancak dosyadan okuma da yapılabilir.
- Var olan bir dosyanın bir yerindeki yazının bir kısmını değiştirmek isteyelim.
Bu durumda dosyayı 'r+' modunda açmalıyız.
- Sıfırdan bir dosyanın içerisine bir şeyler yazmak isteyelim. Bunun dosyayı 'w'
modunda açmalıyız. Ancak dosya varsa dosya varsa dosyanın içeriği
silinecektir. Dikkat etmemiz gerekir.
Dosya başarılı bir biçimde açılmışsa open fonksiyonu bir "dosya nesnesine (file
object)" geri döner. Artık programcı işlemleri
bu geri döndürülen sınıf nesnesinin metotlarıyla yapar.
#----------------------------------------------------------------------------------
--------------------------------------
f = open('[Link]', 'r')
#----------------------------------------------------------------------------------
--------------------------------------
İşletim sistemi bir dosyadaki her bir byte'a ilk byte 0 olmak üzere ardışıl bir
pozisyon numarası vermektedir. Buna
"ilgili byte'ın offset'i" denilmektedir. Dosya işlemleri adeta kalemin ucu
görevini yapan "dosya göstericisi (file pointer)"
denilen bir offset'ten itibaren yapılmaktadır. Dosya açıldığında dosya
göstericisi 0'ıncı offset'tedir. Okuma ve yazma miktarı kadar dosya
göstericisi otomatik ilerletilir. Yazma ve okuma işlemleri her zaman dosya
göstericisinin gösterdiği yerden itibaren yapılır. Örneğin:
0 1 2 3 4 5 6 7
x x x x x x x
Burada dosya göstericisi 0'ın offset'te olsun. Biz dosyaya iki byte
yazdığımızda artık 0 ve 1 numaralı offset'lerdeki byte'lar güncellenir
ve dosya göstericisi 2'inci offset'i gösterir duruma gelir.
#----------------------------------------------------------------------------------
--------------------------------------
Dosyalar "text" ve "binary" olmak üzere ikiye ayrılmaktadır. (Aslında işletim
sistemi böyle bir ayrım yapmamaktadır. Ancak bazı programlama dilleri
işlemleri kolaylaştırmak için bu ayrımı yaparlar.) İçerisinde yalnızca
yazıların bulunduğu dosyalara "text" dosyalar, içerisinde yazıların
dışında başka bilgilerin bulunduğu dosyalara "binary" dosyalar denilmektedir.
Örneğin bir Python kaynak dosyası, bir notepad dosyası text dosyalardır.
Ancak örneğin bir jpeg dosyası, bir exe dosyası binary dosyalardır. Dosyalar
Python'da text ya da binary modda açılabilmektedir.
Default mod text moddur. Binary modda açış yapmak için açış modunun sonuna 'b'
harfi getirilir. Örneğin:
Fakat örneğin:
Bir dosyayı text modda açtığımızda ondan yazı okuruz, ona yazı yazarız. Ancak
binary modda açtığmızda ondan byte'lar okuyup byte'lar yazarız.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Açılan bir dosya programcının işi bittiğinde kapatılmalıdır. Kapatma işlemi
open fonksiyonunun geri döndürdüğü dosya nesnesine
ilişkin sınıfın close metoduyla yapılmaktadır. Bir dosyayı kapatmazsak dosya en
kötü olasılıkla program bittiğinde kapatılır. Ancak ne olursa olsun
programcının dosyayı artık kullanmayacağı noktada kapatması iyi bir tekniktir.
Örneğin:
f = open('[Link]')
...
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Dosya nesnesine ilişkin sınıfın read metodu dosya göstericisinin gösterdiği
yerden n byte ya da n karakter okumak için
kullanılır. ASCII yazılarda ve ASCII karakterlerinin kullanıldığı UTF-8
yazılarda yazının her bir karakteri 1 byte'tır.
Eğer dosya text modda açılmışsa read metodu n karakter okur ve bize onu bir str
nesnesi olarak verir. Eğer dosya binary modda
açılmışsa read metodu n byte okur ve bize onu bytes nesnesi olarak verir. read
metodu ile dosya göstericisinin gösterdi yerden
dosya sonuna kadar olan karakter ya da byte sayısından daha fazla okuma yapmak
istenirse bu durum normal karşılanmaktadır.
Bu durumda okunabilen kadar karakter ya da byte okunur. Dosya göstericisi EOF
konumuna gelir. Eğer dosya göstericisi EOF
durumundaysa read metodu hiçbir karakter ya da byte kuyamaz. Bu durumda dosya
text modda açılmışsa boş string, binary modda
açılmışsa boş bytes nesnesi elde edilir. Eğer okuma sırasında bir IO hatası
oluşursa exception fırlatılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
f = open('[Link]', 'r')
s = [Link](2)
print(s)
s = [Link](2)
print(s)
s = [Link](1000)
print(s)
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
read metoduna hiç parametre geçilmezse "EOF'a kadar dosyanın tamamı" okunmaya
çalışılır.
#----------------------------------------------------------------------------------
--------------------------------------
f = open('[Link]', 'r')
s = [Link]()
print(s)
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Bir dosyanın içerisindekileri tek hamlede değil bir döngü içerisinde okumak
isteyelim. Yani her defasında örneğin 1024 byte okuya okuya
dosyanın tamamını okumak isteyelim. Bunu şöyle yapabiliriz
#----------------------------------------------------------------------------------
--------------------------------------
SIZE = 1024
f = open(r'F:\Dropbox\Kurslar\Python\Doc\[Link]', 'r')
while True:
s = [Link]()
if s == '':
break
print(s, end='')
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Tabii yukarıdaki kod walrus operatörüyle aşağıdaki gibi daha sade yazılabilir.
#----------------------------------------------------------------------------------
--------------------------------------
SIZE = 1024
f = open(r'F:\Dropbox\Kurslar\Python\Doc\[Link]', 'r')
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
55. Ders 07/11/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir binary dosyadan read metodu ile okuma yapmak için binary dosyayı açarken
açış moduna 'b' eklememiz gerekir. Bundan sonra artık
biz read işlemi yaptığımızda read metodu bize str nesnesi değil bytes nesnesi
verecektir. Tabii biz istersek text dosyaları da binary modda
açabiliriz. Bu durumda read metodu bize yine bytes nesnesi verir. Ancak binary
bir dosyanın text modda açılması çoğu kez anlamsızdır.
Örneğin bir PDF dosyası binary bir dosyadır. Bizim onu binary modda açıp
okumamız uygun olur. Bu durumda read metodu bize bytes nesnesi verecektir.
#----------------------------------------------------------------------------------
--------------------------------------
f = open('../doc/[Link]', 'rb')
b = [Link](10)
print(b)
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Bir dosyaya yazma yapmak için write metodu kullanılmaktadır. write metodunun
tek bir parametresi vardır. Dosya text modda açılmışsa
write metodu bir string argüman alır, binary modda açılmışsa bir bytes
nesnesini argüman olarak alır. Tabii dosyaya yazma yapılabilmesi için
açış modunun uygun olması gerekir.
#----------------------------------------------------------------------------------
--------------------------------------
f = open('[Link]', 'w')
[Link]('this is a test')
[Link]('\nyes this is a test')
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte binary modda açılan bir dosyaya write metodu ile bytes
nesnesinin içerisindeki byte'lar yazdırılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
f = open('[Link]', 'wb')
[Link](b'\x01\x02\x03\x04\x05')
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Dosya işlemlerinde hatalar oluştuğunda open fonksiyonu, read ve write metotları
exception fırlatmaktadır. Fırlatılan exception'lar
OSError ya da eşdeğer olarak IOError sınıfları türünden ya da bu sınıflardan
türetilmiş sınıflar türündendir. Programcı
kodunda bir sorun oluşursa hatayı rapor etmek için dosya işlemlerini try-
except-finally bloklarını kullanarak yapabilir.
Dosyanın kapatılması tipik olarak finally bloğunda yapılır. Çünkü finally bloğu
arada bir exception oluşup akış başka bir yere
gitse bile çalıştırılmaktadır. Tabii bu işlemi daha pratik yapmak için with
deyimi de kullanılabilir. with deyimi izleyen
bölümlerde ele alınacaktır. Örneğin:
f = None
try:
f = open('[Link]', 'r')
s = [Link](10)
print(s)
except IOError as e:
print(f'file error: {e}')
except Exception as e:
print(e)
finally:
if f:
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Dosyalar ister text olsun ister binary olsun ardışıl byte topluluklarından
oluşmaktadır. Dolayısıyla bir dosyanın arasına bir şey insert
etmek ya da dosyanın arasından bir şeyler silmek kolay işlemler değildir. Bu
tür işlemler tipik olarak başka bir dosya yaratılarak
dolaylı bir biçimde yapılmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Dosya nesneleri (file objects) dolaşılabilir nesnelerdir. Bir dosya nesnesi
dolaşıldığında dosyadaki satırlar elde edilir.
Tabii elde edilen satırların sonında '\n' karakterleri de vardır. (Yani
satırları print fonksiyonuyla yazdırırken imleci iki kere
aşağı satıra geçirmemek için end parametresi '' biçiminde geçilmelidir.)
Dosyanın satırları bittiğinde dolaşma da biter.
f = open('[Link]')
for line in f:
print(line, end='')
[Link]()
f = open('[Link]')
a = list(f)
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
Dosya nesnesine ilişkin sınıfın readline isimli metodu da vardır. Bu metot
dosya göstericisinin gösterdiği yerden satır sonuna
kadar ('\n' karakteri de dahil olmak üzere) karakterleri okur ve o yazaıyla
geri döner. Eğer dosya sonuna eglinirse metot boş
string'le geri dönmektedir. Bu durumda biz bir text dosyayı readline
metotlarını çağırarak satır satır aşağıdaki gibi de
okuyabiliriz:
f = open('[Link]', 'r')
while True:
s = [Link]()
if s == '':
break
print(s, end='')
f = open('[Link]', 'r')
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bazen dosyanın içerisinde bir yerlerden okuma yapmak ya da içerisinde bir
yerlere yazma yapmak gerekebilmektedir.
Bunun için seek metodu kullanılmaktadır. seek metodunun iki parametresi vardır.
Birinci parametre offset belirtir. İkinci parametre ise
konumlandırmanın nereden itibaren yapılacağını belirten orijin belirtir. İkinci
parametre 0, 1 ya da 2 değerini alır. Başka bir değer alamaz.
0 konumlandırmanın baştan itibaren yapılacağını, 1 konumlandırmanın o anda
dosya göstericisinin gösterdiği yerden itibaren yapılacağını,
2 ise konumlandırmanın EOF pozisyonundan itibaren yapılacağını belirtir. İkinci
parametre 0 ise birinci parametre >= 0 olmak zorundadır.
İkinci parametre 1 ise birinci parametre pozitif, negatif ya da 0 olabilir.
Pozitif ileriye, negatif geriye konumlandırma anlamına gelir.
İkinci parametre 2 ise birinci parametre <= 0 olmak zorundadır. Bu durumda
konumlandırma EOF'tan itibaren geriye yapılır. Metodun
ikinci parametresi 0 default değerini almıştır. Aşağıda bazı örnekler ve
anlamları verilmiştir:ç
#----------------------------------------------------------------------------------
--------------------------------------
f = open('[Link]', 'r')
[Link](20, 0)
s = [Link](10)
print(s)
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Dosya nesnesine ilişkin sınıfın tell isimli metodu dosya göstericisinin baştan
itibaren konumunu gei döndrümektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir dosyaya bir şeyler eklemek için ya dosya "a" ya da "a+" modunda açılmalı ya
da dosya göstericisi EOF durumuna çekilerek
yazma yapılmalıdır. Aşağıdaki örnekte bir text dosyanın sonuna "ankara\n"
yazısı eklenmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
f = open('[Link]', 'r+')
[Link](0, 2)
[Link]('ankara\n')
[Link]()
#----------------------------------------------------------------------------------
--------------------------------------
Biz dosyaları text dosyalar ve binary dosyalar olmak üzere ikiye ayırmıştık.
Ancak text dosyalar da içerisindeki karakterlerin
kodlanma biçimine göre farklılıklar gösterebilmektedir. İşte text dosyalarda
bilinmesi gereken bazı karmaşık konular bulunmaktadır.
Bu konudaki önemli terimler şunlardır:
İlk karakter tablosu ASCII tablosudur. ASCII tablosu 128 karakterden oluşuyordu
ve [0-127] code point'e sahipti. Code point'ler
doğrudan binary sayılara dönüştürülerek encoding yapılıyordu. Sonra latin
dilleri bu 128 karakter yetmeyince ASCII tablosunu
128 daha artırıp 256 karaktere çıkarttılar. Yani 128 code point daha eklediler.
Böylece ilk 128 code point standart ASCII olan
ancak diğer 128 code point farklı farklı karakterlerden oluşan tablolar ortaya
çıktı. Ancak her ulus bu düzenlemeyi kendisine göre
yaptığı için karmaşa oluştu. Bugün ASCII tablosunun bu farklı düzenlemelerine
"code page" denilmektedir. Code page denilince
ASCII tablosunun birinci yarısı aynı olan ancak ikinci yarısı farklı
düzenlemelere sahip olan ASCII tarzı karakter tabloları anlaşılmaktadır.
Fakat bu durum meseleyi hepten karmaşık hale getirmiştir. Örneğin Türkçe için
bile üç farklı code page vardır: OEM 754, Microsoft 1254,
ISO 8859-9. Nihayetinde dünyanın bütün karakterlerini tutan ancak 65536 code
point'e sahip olan UNICODE isimli yeni bir karakter
tablosu geliştirildi. Tabii code point'ler 256'dan fazla olduğu için bunların
bir byte ile kodlanması (encoding) mümkün olamamaktadır.
UNICODE tablonun ilk 128 code point'e karşı gelen karakterleri standart ASCII
tablosu ile aynı karakterlerdir. Sonraki 128 code point'e
karşı gelen karakterler ise Latin-1 code page'inin (ISO 8859-1)
karakterleridir. UNICODE tablonun değişik encoding'leri vardır.
En yaygın kullanılan encoding'i UTF-8'dir. UTF-8 encoding'inde UNICODE'un ilk
128 code point'i 1 byte ile kodlanır. Sonraki code point'leri
2 byte, 3 byte, 4 byte ve 5 byte ile kodlanmaktadır. Bunun bir algoritması
vardır. UTF-8 kodlaması adeta UNICODE tablonun sıkıştırılmış bir kodlaması
gibidir. Ayrıca standart ASCII metinler tamamen UTF-8 encoding'i ile aynı
olmaktadır.
Python'da bir dosya text modda open fonksiyonuyla açılırken open fonksiyonun
encoding parametresi ile encoding belirtilebilmektedir. Örneğin:
#----------------------------------------------------------------------------------
--------------------------------------
56. Ders 14/11/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir exception oluştuğunda exception başka bir yere eele alınıyor olabilir.
Ancak bu tür durumlarda exception'ın oluştuğu noktadan önce
bir kaynak tahsis edilmişse akış başka bir yere gitmeden önce kaynağın geri
bırakılması gerekmektedir. Bunun için try-finally kalıbı
kullanılabilmektedir. Örneğin:
f = None
try:
f = open(...)
...
...
...
finally:
if f:
[Link]()
İşte genel olarak with deyimi bu tür durumlarda kolay bir yazım sağlamak için
kullanılmaktadır. Deyimin genel biçimi şöyledir:
with <ifade>:
<suite>
Bir sınıfın bağlam yönetim protokolüüne uygun olması için sınıfta __enter__ ve
__exit__ isimli iki metodun bulunuyor olması gerekir.
__enter__ metodunun yalnızca self parametresi vardır. Ancak __exit__ metodunun
self dışında üç parametresi daha bulunur.
Bu parametreler istenildiği gibi isimlendirilebilir. Ancak programcılar
genellikle bunları xc_type, exc_value, traceback biçiminde isimlendirirler.
Örneğin:
class Sample:
def __enter__(self):
pass
def __exit__(self, xc_type, exc_value, traceback):
pass
with Sample() as s:
pass
with deyimi kabaca şöyle çalışmaktadır: Önce with anahtar sözcüğünün yanındaki
ifade yapılır. Sonra bu nesne ile __enter__ metodu çağrılır, __enter__ metodunun
geri dönüş değeri as değişkenine atanır. Akış with deyiminden hangi yolla çıkarsa
çıksın (exception dahil olmak üzere) with deyiminin yanındaki nesne ile __exit__
metodu çağrılır.
Örneğin:
class Sample:
def __init__(self):
print('__init__')
def __enter__(self):
print('__enter__')
def __exit__(self, xc_type, exc_value, traceback):
print('__exit__')
with Sample() as s:
print('suite')
print('ends')
__init__
__enter__
suite
__exit__
ends
with deyimi bir sınıf nesnesi yaratılırken yapılan bazı tahsisatların otomatik
bırakımını mümkün hale getirmek için düşünülmüştür.
Tabii bu tür kaynaklar genellikle Python dünyasının dışında tahsis edilen
"unmanaged" denilen kaynaklardır. Fakat herhangi bir kaynak
bu tür durumlarda otomatik bırakılabilir. Örneğin:
with open('[Link]') as f:
s = [Link]()
print(s)
Burada open fonksiyonun geri döndürdüğü dosya nesnesine ilişkin sınıf bağlam
yönetim protokolünü desteklemektedir. Bu nedenle
with deyiminden çıkılırken yorumlayıcı tarafından adeta f.__exit__(...)
biçiminde sınıfın __exit__ metodu çağrılacaktır. İşte bu metotta
sınıfı yazanlar dosyayı zaten kapatmışilardır. O halde biz open fonksiyonunu bu
biçimde kullanırsak with deyiminden çıılırken
dosya kapatılacaktır. Bizim ayrıca dosyayı close ile kapatmamıza gerek yoktur.
with deyimi try-finally gibi düşünülmelidir. with deyimi ile exception ele
alınmamaktadır. Yalnızca otomatik boşaltım mekanizması oluşturulmaktadır.
Exception ele alınmak isteniyorsa ayrıca try deyimi uygulamalıdır. Örneğin:
class Sample:
def __init__(self):
print('__init__')
def __enter__(self):
print('__enter__')
def __exit__(self, xc_type, exc_value, traceback):
print('__exit__')
def foo(self):
raise ValueError
try:
with Sample() as s:
[Link]()
print('suite')
except:
print('exception caught')
print('ends')
Programcı bağlam yönetim protokülüne uygun bir sınıf yazarken gerekli boşaltım
işlemlerini __exit__ metodu içerisinde yapmalıdır.
manager = (EXPRESSION)
enter = type(manager).__enter__
exit = type(manager).__exit__
value = enter(manager)
hit_except = False
try:
TARGET = value
SUITE
except:
hit_except = True
if not exit(manager, *sys.exc_info()):
raise
finally:
if not hit_except:
exit(manager, None, None, None)
manager = (EXPRESSION)
value = manager.__enter__()
hit_except = False
try:
TARGET = value
SUITE
except:
hit_except = True
if not manager.__exit__(*sys.exc_info()):
raise
finally:
if not hit_except:
manager.__exit__(None, None, None)
O halde bizim en azından __enter__ metodu içerisinde self ile geri dönmemiz
gerekir. Örneğin:
class Sample:
def __enter__(self):
return self
...
with open('[Link]) as f:
pass
Burada open fonksiyonunda bir exception oluşursa bu exception dıştaki bir try
blok tarafından yakalanabilir. Çünkü henüz
with deyiminin içerisine girilmemiştir.
Aşağıdaki örnekte bir dosyayı sarmalayan bir sınıf örneği verilmiş ve __exit__
metodunda dosyanın kapatılması sağlanmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
class FileWrapper:
def __init__(self, *args, **kwargs):
self.f = open(*args, **kwargs)
def __enter__(self):
return self
#----------------------------------------------------------------------------------
--------------------------------------
Aslında with deyiminde birden fazla ifade de kullanılabilmektedir. Yani
örneğin:
gibi bir with deyimi geçerlidir. Bu biçimdeki with deyimleri "iç içe" gibi ele
alınmaktadır. Yani yukarıdaki with deyiminin eşdeğeri
şöyledir:
Burada örneğin ifade3'te bir exception oluşursa önce ifade2 için sonra ifade1
için __exit__ metotları çağrılacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Biz şimdiye kadar dolaşılabilir (iterable) nesneler üzerinde çalıştık. Onları
for döngüsüyle doğrudan list gibi tuple gibi
sınıflarla dolaylı olarak dolaştık. Pekiyi kendimiz nasıl dolaşılabilir bir
sınıf yazabiliriz? Bu bölümde bu konu ele alınacaktır.
class Sample:
def __iter__(self):
pass
Burada Sample dolaşılabilir (iterable) bir sınıftır. __iter__ metodunun bir
"dolaşım (iterator)" nesnesiyle geri döndürülmesi gerekmektedir.
Bu durumda dolaşılabilir bir sınıf bize bir dolaşım nesnesi vermelidir. Asıl
dolaşım işlemi bu dolaşım nesnesiyle yapılmaktadır. Bu durumda iki sınıf söz konusu
olmaktadır:
Bir sınıfın dolaşılabilir olması için __iter__ metoduna sahip olması gerekir.
Ancak sınıfın dolaşım sınıfı olması için __next__ metoduna sahip olması
gerekmektedir.
Dolaşılabilir sınıf ile dolaşım sınıfları farklı olabilmektedir. Ancak çoğu kez
bunlar aynı sınıf da olabilirler. Bu durumda sınıfın __iter__ ve __next__
metotları bulunur. __iter__ metodu self ile geri döndürülür. Aşağıdaki örnekte
dolaşılabilir sınıf ile dolaşım sınıfı farklı sınıflar olarak oluşturulmuştur.
Dolaşılabilir sınıfın __iter__ metodunda dolaşım sınıfı türünden bir nesne ile
geri dönülmüştür:
class SampleIterator:
def __next__(self):
pass
class SampleIterable:
def __iter__(self):
si = SampleIterator()
return si
class SampleIterable:
def __iter__(self):
return self
def __next__(self):
pass
for x in iterable:
<suite>
iterator = iterable.__iter__()
try:
while True:
x = iterator.__next__()
<suite>
except StopIteration:
pass
class Sample:
def __init__(self, stop):
[Link] = stop
def __iter__(self):
self.i = 0
return self
def __next__(self):
if self.i == [Link]:
raise StopIteration()
self.i += 1
return self.i - 1
s = Sample(5)
for x in s:
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
57. Ders 16/11/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte bir sınıf __init__ metodu ile aldığı parametreleri bir demet
biçiminde nesnenin örnek özniteliğinde saklamaktadır.
Daha sonra her __next__ metodunda sırasuyla bunlardan brini vermektedir.
#----------------------------------------------------------------------------------
--------------------------------------
class Args:
def __init__(self, *args):
[Link] = args
def __iter__(self):
[Link] = -1
return self
def __next__(self):
[Link] += 1
if [Link] == len([Link]):
raise StopIteration()
return [Link][[Link]]
for x in args:
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
İki değer arasında rastgele değer veren dolaşılabilir bir sınıf örneğini
aşağıdaki gibi yazabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
import random
class RandomIterable:
def __init__(self, beg, end, n):
[Link] = beg
[Link] = end
self.n = n
def __iter__(self):
self.i = -1
return self
def __next__(self):
self.i += 1
if self.i == self.n:
raise StopIteration()
return [Link]([Link], [Link])
#----------------------------------------------------------------------------------
--------------------------------------
Dolaşılabilir (iterable) sınıf ile dolaşım (iterator) sınıfı aynı sınıf olmak
zorunda değildir. Ancak Python Language Reference dokümanına göre
dolaşım sınıfı aynı zamanda dolaşılabilir bir sınıf gibi de davranmak
zorundadır. Yani dolaşım sınıfının __iter__ metodu da bulunmalı ve bu __iter__
metodu self ile geri dönmelidir.
import math
class SqrtIterable:
def __init__(self, n):
self.n = n
def __iter__(self):
return SqrtIterator(self)
class SqrtIterator:
def __init__(self, iterable):
[Link] = -1
[Link] = iterable
def __iter__(self):
return self
def __next__(self):
[Link] += 1
if [Link] == [Link].n:
raise StopIteration()
return [Link]([Link])
for x in SqrtIterable(10):
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte orijinal built-in range sınıfının myrange isminde bir benzeri
yazılmıştır.
#----------------------------------------------------------------------------------
--------------------------------------
import math
class myrange:
def __init__(self, start, stop = None, step = 1):
if stop == None:
[Link] = 0
[Link] = start
else:
[Link] = start
[Link] = stop
[Link] = step
def __iter__(self):
self.i = [Link] - [Link]
return self
def __next__(self):
self.i += [Link]
if self.i >= [Link]:
raise StopIteration()
return self.i
def __len__(self):
return [Link](([Link] - [Link]) / [Link])
def __repr__(self):
s = f'myrange({[Link]}, {[Link]}'
if [Link] > 1:
s += f', {[Link]}'
s += ')'
return s
mr = myrange(10)
a = list(mr)
print(a)
mr = myrange(10, 20)
a = list(mr)
print(a)
mr = myrange(10, 20, 3)
a = list(mr)
print(a)
result = mr[-4]
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Tabii dolaşılabilir bir nesne dolaşılırken tek bir değer vermek zorunda
değildir. Örneğin bir demet nesnesi de verebilir.
#----------------------------------------------------------------------------------
--------------------------------------
import math
class SqrtIterable:
def __init__(self, n):
self.n = n
def __iter__(self):
self.i = -1
return self
def __next__(self):
self.i += 1
if self.i == self.n:
raise StopIteration()
return self.i, [Link](self.i)
s = SqrtIterable(10)
d = dict(SqrtIterable(100))
print(d)
#----------------------------------------------------------------------------------
--------------------------------------
Built-in enumerate fonksiyonunu biz de basit bir biçimde aşağıdaki gibi
yazabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
class myenumerate:
def __init__(self, iterable, start = 0):
[Link] = iterable.__iter__()
[Link] = start - 1
def __iter__(self):
return self
def __next__(self):
[Link] += 1
for t in myenumerate(a):
print(t)
#----------------------------------------------------------------------------------
--------------------------------------
Yukarıdaki örnekte olduğu gibi bazen manuel bir biçimde __next__ metodunun
çağrılması gerekebilir. Örneğin elimzde
dolaşılabilir bir nesne olsun. Biz bu nesneyi for döngüsü ile dolaşmak
isteyelim. Ancak ilk elemanı pas geçmek isteyelim.
Ya da elimizde yine bir dolaşılabilir nesne olsun. Biz bu nesnenin elemanlarını
bir list nesnesine yerleştirelim. Ancak ilk elemanı
pas geçmek isteyelim. Dolaşım nesnelerinin de dolaşılabilir nesneler gibi
davrandığını anımsayınız.
#----------------------------------------------------------------------------------
--------------------------------------
iterator = a.__iter__()
iterator.__next__() # ilk elemanı pas geçmek için
for x in iterator:
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
Örneğin dolaşılabilir nbir nesnenin en büyük elemanını bulan bir fonksiyon
yazmak isteyelim. Bilindiği bu fonksiyon aslında built-in
biçimde max ismiyle bulunmaktadır. Biz en büyük elemanı bulurken ilk elemanın
en büyük olduğunu kabul edip sonraki elemanlarla
karşılaştırırız. Bu tür durumlarda manule __next__ işlemi gerekebilmektedir.
Aşağıdaki örnekte böyle bir mymax fonksiyonu yazılmıştır.
(Orijinal max fonksiyonu eğer dolaşılabilir nesnede eleman yoksa ValueError ile
raise işlemi yapmaktadr.)
#----------------------------------------------------------------------------------
--------------------------------------
import random
class RandomIterable:
def __init__(self, beg, end, n):
[Link] = beg
[Link] = end
self.n = n
def __iter__(self):
self.i = -1
return self
def __next__(self):
self.i += 1
if self.i == self.n:
raise StopIteration()
return [Link]([Link], [Link])
def mymax(iterable):
iterator = iterable.__iter__()
try:
maxval = iterator.__next__()
except StopIteration:
raise ValueError('iterable has zero element')
for x in iterator:
if x > maxval:
maxval = x
return maxval
#----------------------------------------------------------------------------------
--------------------------------------
Python standart kütüphanesinde iter ve next isimli iki built-in fonksiyon
vardır. Bu fonksiyonlar aslında parametresiyle verilen
nesne üzerinde __iter__ ve __next__ metotlarını çağırmaktadır. Başka bir
deyişle iterable.__iter__() çağırısı ile iter(iterable)
aynı işlevdedir. Benzer biçimde iterator.__next__() çağrısı ile next(iterator)
çağrısı da anı anlamdadır. Yani bu fonksiyonlar aşağıdaki gibi
yazılmıştır (bazı ayrıntılar söz konusudur):
def iter(i):
return .__iter__()
def next(i):
return i.__next__()
#----------------------------------------------------------------------------------
--------------------------------------
try:
while True:
val = next(iterator) # iterator.__next__()
print(val)
except StopIteration:
pass
#----------------------------------------------------------------------------------
--------------------------------------
Dolaşılabilir nesnelerdeki dolaşım işlemi ileriye doğru yapılmaktadır? Pekiyi
geriye doğru dolaşım yapılamaz mı?
İşte Python'da bunun için built-in global reversed fonksiyonu bulundurulmuştur.
Biz bu fonksiyonu daha önce yüzeysel
biçimde görmüştük. reversed foksiyonu bizden dolaşılabilir bir nesneyi alır,
bize tersten dolaşıma izin veren yeni bir dolaşılabilir
nesne verir. Yani kullanımı şmöyledir:
new_iterable = reversed(iterable)
iterable = reversed(a)
for x in iterable:
print(x)
def reversed(iterable):
return iterable.__reversed__()
import math
class SqrtIterable:
def __init__(self, n):
self.n = n
def __iter__(self):
self.i = -1
return self
def __next__(self):
self.i += 1
if self.i == self.n:
raise StopIteration()
return [Link](self.i)
def __reversed__(self):
return SqrtReverseIterator(self)
class SqrtReverseIterator:
def __init__(self, siter):
[Link] = siter
self.i = siter.n
def __iter__(self):
return self
def __next__(self):
self.i -= 1
if self.i < 0:
raise StopIteration()
return [Link](self.i)
for x in SqrtIterable(4):
print(x)
for x in reversed(SqrtIterable(4)):
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
Geriye doğru dolaşımı sağlamak için __reversed__ metodunda yukarıdaki örnekte
olduğu gibi farklı bir iterator sınıfı oluşturmak
işlemleri kolaylaştırmaktadır. Aynı sınıfın hem ileriye doğru hem de geriye
doğru dolaşım yapabilmesi mümkün olsa da
karmaşık bir tasarımı gerektirir. Aşağıdaki örnek biraz kusurlu olsa da tek bir
sınıfın hem ileriye hem de geriye doğru dolaşımsağlamasının zorluğunu
göstermektedir.
#----------------------------------------------------------------------------------
--------------------------------------
import math
class SqrtIterable:
def __init__(self, n):
self.n = n
[Link] = 1
def __iter__(self):
if [Link] == 1:
self.i = -1
else:
self.i = self.n
return self
def __next__(self):
self.i += [Link]
if [Link] == 1:
if self.i == self.n:
raise StopIteration()
else:
if self.i < 0:
raise StopIteration()
return [Link](self.i)
def __reversed__(self):
[Link] = -1
return self
for x in SqrtIterable(4):
print(x)
for x in reversed(SqrtIterable(4)):
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
Dolaşılabilir (iterable) nesne kavramı ile dolaşım (iterator) nesnesi kavramı
birbirine benzemektedir. Bir nesnenin dolaşılabilir olması için ona ilişkin sınıfta
__iter__ metodunun olması gerekir. Halbuki bir nesne dolaşım nesnesi ise ona
ilişkin sınıfta __next__ metodu olur. Ancak dolaşım nesneleri de dolaşılabilir
nesneler gibi kullamılabilmektedir. Daha önceden de belirttiğimiz gibi dolaşım
nesnelerinin de __iter__ metodu olmak zorundadır. İki kavram arasındaki öenmli fark
şudur: Elimizde bir dolaşılabilir nesne varsa biz onu her defasında yeniden yani
baştan itibaren dolaşabiliriz. Ancak elimizde dolaşım nesnesi varsa biz onu bir kez
dolaştığımızda bitirmiş oluruz. Artık bir daha dolaşamayız. Örneğin list nesnesi
dolaşılabilir bir nesnedir. Biz her defasında o nesnenin elemanlarını baştan
dolaşabiliriz:
a = [1, 2, 3, 4, 5]
for x in a:
print(x, end=' ')
print()
for x in a:
print(x, end=' ')
Ancak list nesnesinden elde ettiğimiz dolaşım nesnesini bir kez dolaşabiliriz.
İkinci dolaştığımızda zaten sona gelindiği için dolaşım yapılamayacaktır.
Örneğin:
a = [1, 2, 3, 4, 5]
for x in iterator:
print(x, end=' ')
print()
for x in iterator:
print(x, end=' ')
İşte bir üretici fonksiyonu çağırdığımızda fonksiyon bize bir dolaşım nesnesi
vermektedir. O nesneyi biz bir kez dolaşabiliriz. Örneğin:
def foo():
for i in range(10):
yield i
g = foo()
for x in g:
print(x, end=' ')
print()
for x in g:
print(x, end=' ')
print()
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi map isimli built-in fonksiyon bizden bir fonksiyon ve
dolaşılabilir nesneyi parametre olarak alıp bize bir dolaşım
nesnesi veriyordu. Biz bu nesneyi dolaştığımızda dolaşılabilir nesnedeki
elemanların bu fonksiyona argüman olarak geçilmesi sonucunda
elde edilen değerleri elde ediyorduk. Örneğin:
def foo(x):
return x ** 2
a = [1, 2, 3, 4, 5]
result = map(foo, a)
for x in result:
print(x, end= ' ')
class mymap:
def __init__(self, f, iterable):
self.f = f
[Link] = iter(iterable)
def __iter__(self):
return self
def __next__(self):
val = next([Link])
return self.f(val)
def foo(x):
return x ** 2
a = [1, 2, 3, 4, 5]
result = mymap(foo, a)
for x in result:
print(x, end= ' ')
#----------------------------------------------------------------------------------
--------------------------------------
58. Ders 21/11/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Üretici fonksiyonlara (generators) özellikle yorumlayıcılarla çalışılan
dillerde karşılaşılmaktadır. Ancak derleyici temelli bazı dillerede de
özellikle son yıllarda üretici fonksiyonlar eklenmiştir. Python'da bir
fonksiyonda en az bir kez yield isimli deyim kullanılırsa artık
o fonksiyona üretici fonksiyon denilmektedir. Örneğin:
def foo():
print('one')
yield 1
print('two')
yield 2
print('three')
yield 3
yield [ifade]
Bir üretici fonksiyonun türü normal bir fonksiyondur. Ancak üretici fonksiyon
çağrıldığında bir "üretici nesne (generator object)"
elde edilmektedir. Bu üretici nesneye Python Language Reference dokümanlarında
"generator iterator" da denilmektedir. Örneğin:
g = foo()
def foo():
print('one')
yield 1
print('two')
yield 2
print('three')
yield 3
g = foo()
#----------------------------------------------------------------------------------
--------------------------------------
Üretici nesneler birer dolaşım (iterator) nesnesidir dolayısıyla da
dolaşılabilir nesnelerdir. Yani biz bir üretici nesnei
for döngüsüyle dolaşabiliriz. İstersek next metodunu sürekli çağırarak da
(__iter__ metodunu çağırmadan) de dolaşabiliriz. Bir üretici
nesne dolaşılmak istendiğinde (next yapıldığında) üretici fonksiyon
çalıştırılır ve yield deyiminin olduğu yerde durur ve yield deyiminin
yanındaki ifadenin değerini verir. Bir daha dolaşılmak istendiğinde (yan inext
yapıldığında) fonksiyon yield deyiminde kaldığı yerden devam
eder ve sonraki yield deyiminde durur. Sonraki yield deyiminin değerini verir.
Yani yield deyimi fonksiyonu bitirmemektedir.
O anki iterasyonu bitirmektedir Programın akışı yield deyimini gördüğünde
durdurulur. Ancak bu durdurma geçici bir durdurmadır.
Her next işleminde fonksiyon durdurulduğu yerden çalışmaya devam eder. Nihayet
fonksiyon bittiğinde (return ile de bitebilir) StopIteration oluşmaktadır.
Bu da dolaşımın bittiği anlamına gelir. Örneğin:
def foo():
print('one')
yield 1
print('two')
yield 2
print('three')
yield 3
g = foo()
result = next(g)
print(result)
result = next(g)
print(result)
result = next(g)
print(result)
Burada g bir üretici nesnedir. Biz next(g) yaptığımızda akış ilk yield deyimine
kadar çalışır onun yanındaki ifadenin değeri
next fonksiyonundan elde edilen değerdir. Sonraki next fonksiyonun kaldığı
yerden devam etmesini sağlar. Fonksiyon bitince dolaşım da bitecektir.
Yukarıki kod çalıştırıldığında ekrana şunlar basılacaktır:
one
1
two
2
three
3
(StopIteration exception)
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
print('one')
yield 1
print('two')
yield 2
print('three')
yield 3
g = foo()
result = next(g)
print(result)
result = next(g)
print(result)
result = next(g)
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Üretici nesneler dolaşılabilir olduğuna göre biz onları for döngüsüyle de
dolaşabiliriz. Örneğin:
def foo():
print('one')
yield 1
print('two')
yield 2
print('three')
yield 3
for x in foo():
print(x)
def foo():
print('one')
yield 1
print('two')
yield 2
print('three')
yield 3
for x in foo():
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
Üretici fonksiyonlar sayesinde biz dolaşılabilir nesneleri çok daha kolay
oluşturabiliriz. Örneğin:
def foo():
for i in range(10):
yield i * i
for x in foo():
print(x)
Burada her yield işlemi bize bir sayının karesin verecektir. Bu durumda ekrana
0'dan 10'a kadar sayıların kareleri yazdırılacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
def foo():
for i in range(10):
yield i * i
for x in foo():
print(x)
#----------------------------------------------------------------------------------
--------------------------------------
Biz range fonksiyonunu dolaşılabilir bir sınıf olarak yazmıştık. Standart
kütüphanedeki range fonksiyonu aslında dolaşılabilir bir sınıftır.
Ancak istersek range fonksiyonunu bir üretici fonksiyon biçiminde de
yazabiliriz. Üretici fonksiyonlar dolaşılabilir nesne oluşturmanın
alternatif ve kolay yollarından biridir.
#----------------------------------------------------------------------------------
--------------------------------------
i = start
while i < stop:
yield i
i += step
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte her asal sayı bulunduğunda akış yield ile durdurulup bulunan
asal sayı verilmektedir. Böylece biz
asal sayıları veren dolaşılabilir bir nesne elde etmiş oluruz.
#----------------------------------------------------------------------------------
--------------------------------------
import math
def get_primes(n):
if n < 2:
raise ValueError('argument ')
def isprime(x):
if x % 2 == 0:
return x == 2
for i in range(3, int([Link](x)) + 1, 2):
if x % i == 0:
return False
return True
if n >= 2:
yield 2
i = 3
while i <= n:
if isprime(i):
yield i
i += 2
Örneğin diskimizdeki tüm dosyalar üzerinde bir işlemler yapmak isteyelim. Yani
kökten başlayarak her dizine tek tek geçip tüm
dosyaları elde etmek isteyelim. Bu çok yorucu ve uzun zaman alana bir işlemdir.
Diskimizde binlerce dosya bulunuyor olabilir.
Bunlarının hepsinin elde edilmesi ve bize bir liste olarak verilmesi oldukça
zordur. Oysa biz istediğimzde bize kalınan yerden
devamedilerek dosyalar verilse bu işlem çok daha anlamlı olur. İşte
dolaşılabilir nesneler bir adım adım yapmak için tercih edilmektedir.
Eğer dizin ağacını dolaşan bir fonksiyonu dolaşılabilir bir nesne yoluyla
gerçekleştirmezsek hem bizim tüm dosyaları bir listede saklamamız gerekir
hem de bu işlem çok uzun zaman alabilir. Gerçekten de os modülü içerisindeki
walk fonksiyonu bir üretici fonksiyon olarak yazılmıştır.
Yani walk fonksiyonu dizin ağacını dolaşırken her yeni girişi bulduğunda yield
onu bize vermekte ve sonra kaldığı yerden devam etmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
import os
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi dolaşılabilir bir nesne elde etmek için dolaşılabilir sınıflar mı yoksa
üretici fonksiyonlar mı tercih edilmelidir?
İşte bu tercih içinde bulunulan duruma göre değişebilmektedir:
- Dolaşılabilir sınıflar her __next__ metodunda bize yeni değeri verirler. Oysa
üretici fonksiyonlar her yield işleminde bize değer verirler.
Üretici fonksiyonun durudulması ve o noktadan çalışma devam ettirilmesi daha
uzun zaman almaktadır. Yani dolaşılabilir sınıflar
daha hızlı olma eğilimindedir.
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi dolaşılabilir nesne kavramı ile dolaşım nesnesi kavramı
birbirinden farklıydı. Bir dolaşım nesnesi dolaşıldığında
artık sona geliniyordu ve bir daha dolaşılamıyordu. İşte bir üretici fonksiyonu
çağırdığımızda fonksiyon bize bir dolaşım nesnesi vermektedir. O nesneyi biz bir
kez dolaşabiliriz. Örneğin:
def foo():
for i in range(10):
yield i
g = foo()
for x in g:
print(x, end=' ')
print()
for x in g:
print(x, end=' ')
print()
Örneğin enumerate fonksiyonu bize dolaşılabilir bir nesne vermez, bir dolaşım
nesnesi verir. Yani enumerate fonksiyonun verdiği nesneyi
biz dolaşırsak bir kez dolaşım yapabiliriz:
a = [1, 2, 3, 4, 5]
e = enumerate(a)
for index, x in e:
print(f'{index} => {x}')
print()
for index, x in e:
print(f'{index} => {x}')
class myenumerate:
def __init__(self, iterable, start=0):
[Link] = iter(iterable)
self.i = start - 1
def __iter__(self):
return self
def __next__(self):
self.i += 1
return self.i, next([Link])
e = myenumerate(a)
for index, x in e:
print(f'{index} => {x}')
print('---------------')
for index, x in e:
print(f'{index} => {x}')
print('---------------')
e = myenumerate(a)
for index, x in e:
print(f'{index} => {x}')
print('---------------')
for index, x in e:
print(f'{index} => {x}')
print('---------------')
#----------------------------------------------------------------------------------
--------------------------------------
Daha önce built-in map fonksiyonunu dolaşılabilir bir sınıf biçiminde
yazmıştık. Şimdi de bu fonksiyonu bir üretici
fonksiyon biçiminde yazalım. (Standart kütüphanede map üretici fonksiyon
biçiminde değil dolaşılabilir sınıf biçiminde yaızlmıştır.)
#----------------------------------------------------------------------------------
--------------------------------------
def foo(x):
return x ** 2
a = [1, 2, 3, 4, 5]
result = mymap(foo, a)
for x in result:
print(x, end= ' ')
#----------------------------------------------------------------------------------
--------------------------------------
Üretici nesneler birer dolaşım nesnesi belirtiyordu. Dolayısıyla o nesnelerle
__next__ metodunu çağırsak ya da eşdeğer olarak next
fonksiyonuna o nesneleri versek akış ilk yield deyimine kadar çalışıyor ve
yield değerini bize veriyordu. Ancak bu işlemin tersi de mümkündür. Yani yield
deyimi de bir değer üretebilmektedir. Default durumda next işlemleri sırasında
yield deyimi None değer üretir. Örneğin:
def foo():
result = yield 1
print(result)
result = yield 2
print(result)
result = yield 3
print(result)
g = foo()
result = g.__next__()
print(result)
result = g.__next__()
print(result)
result = g.__next__()
print(result)
1
None
2
None
3
İşte biz üretici nesne ile send metodunu çağırırsak aslında hem akışı durduğu
yerden devam ettirmiş oluruz hem de yield işleminden
istediğimiz bir değerin elde edilmesini sağlarız. Ancak send metodu ancak
yield'de durmuş akış için kullanılır. Dolayısıyla üretici nesne üzerinde
dolaşım send ile başlatılmaz. Ancak devam ettirilebilir. Örneğin:
def foo():
result = yield 1
print(result)
result = yield 2
print(result)
result = yield 3
print(result)
g = foo()
result = next(g)
print(result)
result = [Link](10)
print(result)
result = [Link](20)
print(result)
Her send işlemi yield'de duran akışı devam ettirir. Ancak next işlemşnden farkı
yield'den bir değer üretilmesini sağlamasıdır.
Yani biz yield'de duran akışı devam ettirirken aynı zamanda yield'den bir değer
de elde edilmesini istiyorsak bu durumda
send uygulamalıyız.
Üretici fonksiyonu next ile kaldığı yerden devam ettirirsek yield None
üretmektedir. Başka bir deyişle:
next(g)
[Link](None)
Ancak built-in bir send fonksiyonu yoktur. Yani send üretici nesneyle
kullanılmalıdır.
#----------------------------------------------------------------------------------
--------------------------------------
Anımsanacağı gibi liste içlemleri, küme içlemleri ve sözlük içlemleri vardı
ancak demet içlemleri yoktu. Bunun ana nedeni
demetlerin değiştirilebilir nesneler olmamasıdır. Ancak demet içlemi sentaksı
Python'da bulunmaktadır fakat başka bir anlama gelmektedir.
Demet içlemi sentaksına Python'da "üretici ifadeler (generator expresssions)"
denilmektedir. Örneğin:
a = [1, 2, 3, 4, 5]
Bir üretici ifade aslında üretici bir fonksiyonun basit bir yazım biçimidir.
Üretici ifade bize aslında bir üretici nesne vermektedir. Yani:
def some_generator_name(dolaşılabilir_nesne):
for x in dolaşılabilir_nesne:
yield ifade
g = some_generator_name()
#----------------------------------------------------------------------------------
--------------------------------------
a = [1, 2, 3, 4, 5]
for x in g:
print(x, end=' ')
print()
#----------------------------------------------------------------------------------
--------------------------------------
59. Ders 23/11/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi üretici ifade de aslında üretici fonksiyon belirttiğine göe arada ne
fark vardır? Yani örneğin:
g = (i * i for i in trange(100))
def foo():
for i in range(100):
yield i * i
g = foo()
import statistics
print(result)
Biz burada üetici ifadeyi doğrudan fonksiyon çağırma işleminde argüman olarak
kullandık. Halbuki aynı işlemi üretici
fonksiyonlarla yapmak isteseydik daha zor olacaktı:
def foo():
for i in range(10):
yield i * i
result = [Link](foo())
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Üretici ifadeler eğer bir fonksiyon ya da metot çağrılırken argüman olarak
kullanılıyorsa ve çağrım işleminde başka da bir argüman bulundurulmuyorsa üretici
ifadelerdeki dıştaki parantezler hiç kullanılmayabilir. Örneğin:
import statistics
print(result)
Burada enumerate fonksiyonu iki argümanla çağrıldığı için üretici ifadedeki dış
parantezler ihmal edilemez. Tabii enumerate fonksiyonu tek argümanla çağrılsaydı
dıştaki parantezler ihmal edilebilirdi:
for index, val in enumerate(i * i for i in range(10)): # geçerli!
print(f'{index} => {val}')
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Aşağıdaki örnekte print fonksiyonuna dolaşılabilir bir üretici ifade *'lı bir
biçimde verilmiştir. Bu durumda bu üretici
ifade dolaşılıp elde edilen değerler ekrana yazdırılacaktır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Bir fonksiyonun bir ifade içerisinde tanımlanarak o ifade içerisinde
kullanılmasına programlama dillerinde "lambda ifadeleri (lambda expressions)"
denilmektedir. Aslında lambda ifadeleri eskiden fonksiyonel dillerde karşımıza
çıkan yapılardı. Ancak son yıllarda
pek çok dile fonksiyonel özellikler katmak için lambda ifadeleri sokulmuştur.
C++, C# ve Java gibi dillerde lambda ifadeleri
daha geniş bir sntaks yapısına dolayısıyla kullanım alanına sahiptir.
Python'daki lambda ifadeleri bu dillerdekilere gör oldukça
kısıtlı ve minimalist bir yapıdadır. Lambda ifadelerinin genel biçimi şöyledir:
Örneğin:
f = lambda a: a * a
result = f(10)
print(result)
>>> f = lambda a: a * a
>>> type(f)
<class 'function'>
>>> f(10)
100
>>> f(5)
25
Bu durumda:
f = lambda a: a * a
for x in m:
print(x, end= ' ')
Burada biz map fonksiyonuna bir fonksiyon oluşturup onu vermiş olduk. Bu
işlemin eşdeğeri şöyledir:
def f(a):
return a * a
for x in m:
print(x, end= ' ')
def foo(val):
for x in map(lambda a: a * val, [1, 2, 3, 4, 5]):
print(x, end=' ')
print()
foo(5)
foo(10)
def foo(val):
def f(a):
return a * val
for x in map(f, [1, 2, 3, 4, 5]):
print(x, end=' ')
print()
foo(5)
foo(10)
f = lambda: 100
result = f()
print(result)
f = lambda a, b: a + b
result = f(10, 20)
print(result) # 30
Lambda ifadelerinde ':' atomunun sağında bir deyim olamaz. (Yani if, for return
gibi deyimler kullanılamaz.) Bir ifade olabilir.
Tabii koşul operatörü bir operatör olduğu için lambda ifadelerinde
kullanılabilir. Örneğin:
print(result) # 20
Mademki aslında metotlar birer sınıf değişkenidir. O halde bir lambda ifadesi
ile de bir metot oluşturabiliriz. Örneğin:
class Sample:
def __init__(self, a):
self.a = a
s = Sample(10)
result = s.get_a()
print(result)
#----------------------------------------------------------------------------------
--------------------------------------
Standart kütüphanede filter isimli ilginç bir fonksiyon vardır. filter
fonksiyonu bizden bir fonksiyon ve dolaşılabilir
nesne alır (tıpkı map gibi) ve bize dolaşım nesnesi verir. Biz filter
fonksiyonunun veridiği dolaşım nesnesini dolaştığımızda aslında
filter fonksiyonuna verdiğimiz dolaşılabilir nesneyi dolaşmış oluruz. Ancak bu
nesnedeki elemanlar önce filer fonksiyonun birinci parametresinde
verdiğimiz fonksiyona sokulacak ve True ile geri dönenler bize verilecektir.
Yani filer fonksiyonu bizim verdiğimiz dolaşılabilir nesnedeki
bazı değerleri atmak için (filtrelemek için) kullanılmaktadır.
f Örneğin.
for x in result:
print(x, end= ' ')
print()
filter fonksiyonu yoluyla bir grup değer içerisinde koşulu sağlayan değerleri
filtreleyebiliriz. Örneğin:
import math
def isprime(a):
if a % 2 == 0:
return a == 2
return True
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
filer fonksiyonu standart kütüphanede dolaşılabilir bir sınıf biçiminde
yazılmıştır. Onu aşağdaıki gibi yazabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
class myfilter:
def __init__(self, f, iterable):
self.f = f
[Link] = iter(iterable)
def __iter__(self):
return self
def __next__(self):
while True:
val = next([Link])
if self.f(val):
return val
#----------------------------------------------------------------------------------
--------------------------------------
Şimdi de filter fonksiyonunu üretici fonksiyon olarak yazalım. Tabii üretici
fonksiyon olarak yazmak çok kolay. Ancak yukarıda da
belirttiğimiz gibi üretici fonksiyonların durdurulup çalıştırılması zaman
kaybına yol açmaktadır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
60. Ders 28/11/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfların diğer bir özel metodu ise __new__ isimli metottur. __new__ metodu
nesneyi tahsis eden asıl metottur. Python'da
__new__ metodu nesneyi tahsis ederken __init__ metodu tahsis edilmiş olan nesne
üzerinde ilk işlemleri yapar. Genellikle
programcılar sınıfları için __new__ metodunu yazmazlar. Ancak __init__ metodunu
yazarlar. __new__ metodua bazı ileri uygulamalarda
gereksinim duyulabilmektedir. Örneğin kurumuzun uygulama kısmında göreceğimiz
"meta sınıflar (meta classes)" konusunda bu metodun
kullanımı üzerinde duracağız.
class Sample:
pass
Programcı tahsisatı eninde sonunda object sınıfının __new__ metodu ile yapar.
Ancak programcı bazen nesne yaratılırken "araya girmek"
isteyebilir. Örneğin:
class Sample:
@staticmethod
def __new__(cls):
print('nesne yaratılırken araya giriyoruz')
return super().__new__(cls)
def __init__(self):
print('__init__ çağrılıyor')
s = Sample() # s = Sample.__new__(Sample)
Aslında burada Sample.__new__(Sample, 10, 20, 30) gibi bir çağırma söz konusu
olacaktır. O halde __new__ metodu her türlü argümanla çağrılabilir olması gerekir.
Onun da yolu *'lı **'lı parametre kullanmaktadır. Örneğin:
class Sample:
@staticmethod
def __new__(cls, *args, **kwargs):
print('nesne yaratılırken araya giriyoruz')
return super().__new__(cls)
Aslında buradaki meslenenin biraz daha karmaşık bir tarafı vardır. Öncelikle bu
tarafı açıklamadan önce bir konuyu anımsatmak istiyoruz.
Anımsayacağınız gibi Python yorumlayıcısı bir sınıf tanımlamasını gördüğünde
type sınıfı türünden bir nesne yaratıyor ve sınıf ismine o type nesnesinin
adresini atıyordu. Örneğin:
class Sample:
pass
O zaman biz yukarıdaki gibi bir sınıf türünden nesne yaratmak istediğimizde
aslında type sınııfnın __call__ metodu çağrılmaktadır. İşte nesnenin yaratılmasına
önayak
olan aslında type sınıfının __call__ metodudur. Örneğin nesne şöyle yaratılmış
olsun:
s = Sample(10, 20)
class type:
def __call__(self, *args, **kwargs):
pass
type sınıfının __call__ metodu önce ilgili türün __new__ metodunu çağırır. Eğer
metodun verdiği nesne belirtilen tür türündense bu sefer o nesne ile
o sınıfın __init__ metodunu çağırmaktadır. Başka bir deyişle aslında type
sınıfının __call__ metodu adeta aşağıdaki gibi yazılmıştır:
class type:
def __call__(self, *args, **kwargs):
instance = self.__new__(self)
if isinstance(instance, self):
instance.__init__(*args, **kwargs)
return instance
class type:
def __call__(self, *args, **kwargs):
instance = self.__new__(self)
if isinstance(instance, self):
instance.__init__(*args, **kwargs)
return instance
s = Sample(10, 20)
gibi bir yaratımda aslında type snıfının __call__ metodunun self parametresi
Sample'dır. 10 ve 20 değerleri ise __call__ metosunun args parametresine aktarılır.
Burada type sınıfının __call__ metodunun içerisinde önce aşağıdaki işlem
yapılmıştır:
instance = self.__new__(self)
Pekiyi burada kimin __new__ metodu çağrlmaktadır? Tabii ki Sample'ın. Pekiyi
Sample sınıfının __new__ metodu yoksa kiminki çağrılacaktır? Tabii ki object'in.
O zaman bu satır Sample sınıfı türünden bir nesnenin yaratılmasına yol
açacaktır. Sonra şu işlemler yapılmıştır:
if isinstance(instance, self):
instance.__init__(*args, **kwargs)
Bunun anlamı şudur: Eğer yaratılan nesne Sample türünden değilse __init__
çağrılmamaktadır. (Zaten böylesi bir durumda Sample sınıfının __init__ metodunun
çağrılması
anlamsızdır)
return instance
Burada önemli nokta biz bir sınıf için __new__ metodunu yazdığımızda eğer bu
metodu o sınıf türüden bir nesneyle geri döndürmezsek bu durumda
o sınıfın __init__ metou çağrılmayacaktır. Aşağıdaki örnekte Sample sınıfının
__init__ metodu çağrılmayacaktır.
class Mample:
def __init__(self):
print('Mample.__init__')
class Sample:
@staticmethod
def __new__(cls, *args, **kwargs):
print('Sample.__new__')
instance = object.__new__(Mample)
return instance
def __init__(self):
print('Sample.__init__')
s = Sample(10, 20)
3) Nesnenin yaratılması her zaman object sınıfının __new__ metodu ile yapılır.
Yani eninde sonunda bir nesne yratılacaksa object sınıfının
__new__ metodu çağrılmış olmalıdır. type sınıfının __call__ metodu nesneyi
kendisi yaratmaz. Yine object sınıfının __new__ metodu yoluyla nesne
yaratılmaktadır.
Özetle bir sınıf türünden bir nesne yaratılmaya çalışıldığında aslında type
sınıfının __call__ metodu çağrılır. type sınıfının __call__ metodu
önce nesne yaratılmak istenen sınıfın __new__ metodunu çağrır sonra da __init__
metodunu çağırır.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
type sınıfı ile object sınıfı arasında mantıksal bir ilişki yoktur. type sınıfı
bir "meta sınıf"tır. Yani bir sınıfın bilgilerini tutan sınıftır.
Meta sözcüğü üst kavram olarak kullanılmaktadır. Sınıf bilgi tutar, ancak
sınıfın kendi bilgileri de bir sınıf tarafından tutulmaktadır.
İşte o type sınıfıdır. Bu durumda type meta bir sınıftır. object sınıfı ise her
sınııfn taban sınıfı görevinde olan nesne yaratma işlemini yapan sınıftır.
Kaldı ki type sınıfı da object sınıfından türetilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi biz neden bir sınıf için __new__ metodunu yazmak isteriz? İşte bunun
nedeni biraz ileri düzey konularla ilgilidir.
Örneğin "singleton" kalıbı mecburen __new__ metodu kullanılarak
oluşturulmaktadır. NYPT'de singleton kalıbı bir sınıf türünden tek bir nesnenin
yaratıldığı (yani kişiler nesne yarattığını sansalar da aslında hep aynı
nesnenin elde edildiği) bir kalıptır. Aşağıda singleton
kalıbına örnek verilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
@staticmethod
def __new__(self):
if not [Link]:
[Link] = super().__new__(Sample)
print('initialize object')
return [Link]
singleton = None
s = Sample()
k = Sample()
m = Sample()
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfların eleman erişiminde kullanılan __getattr__ isimli bir metodu vardır.
Bir sınıf türünden değişkenle o sınıfın olmayan
bir örnek özniteliğine ya da bir metoduna erişildiğinde sınıfın __getattr__
metodu çağrılmaktadır. __getattr__ metodunun self parametresi
dışında olmayan elemanın ismini alan bir name parametresi (ismi name olmak
zorunda değildir) de vardır. __getattr__ metodu
erişilen elemana geri dönmelidir. Örneğin:
class Sample:
def __getattr__(self, name):
print(name)
return 0
s = Sample()
x = [Link]
print(x) # 0
class Sample:
def __init__(self):
[Link] = 10
def __getattr__(self, name):
print(name)
return 0
s = Sample()
x = [Link]
print(x) # 10
Normal olarak eğer sınıfta __getattr__ metodu yoksa biz sınıfın olmayan bir
elemanına erişmeye çalıştığımızda AttributeError
isimli exception oluşur.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfın __getattr__ metodu "property" kavramını oluşturmak amacıyla ve başka
birtakım amaçlarla kullanılabilmektedir.
Property metotların adeta birer örnek özniteliği gibi kullanılmasını sağlayan
bir mekanizmadır. C++ ve Java gibi
dillerde property yoktur. Fakat örneğin C#'ta vardır. Python'da property'ler
__getattr__ metodu yoluyla ya da property
isimli dekoratör yoluyla oluşturulabilmektedir. Örneğin Circle isimli bir sınıf
yazmak isteyelim. Sınıfın area isimli bir örnek
özniteliğinin olmasına gerek yoktur. Çünkü area aslında yarıçaptan hareketle
hesaplanabilmektedir. İşte biz __getattr__ yoluyla sanki area isimli
örnek özniteiği sınıfta varmış gibi bir durum oluşturabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
import math
class Circle:
def __init__(self, x, y, radius):
self.x = x
self.y = y
[Link] = radius
c = Circle(10, 10, 2)
a = [Link]
a = [Link]
print(a)
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfta olmayan elemanların kullanılması teması property dışında başka yerlerde
de karşımıza çıkabilmektedir.
Örneğin biz __getattr__ sayesinde sınıf nesnesi yaratırken verdiğimiz isimli
argümanları sanki onlar birer örnek özniteliğiymiş
gibi kullanabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self, **kwargs):
[Link] = kwargs
print([Link])
print([Link])
print([Link])
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfın __getattr__ metoduna benzer bir de __getattribute__ metodu vardır.
__getattribute__ metodu bir sınıf türünden değişken ile
sınıfın bir elemanına erişilirken eleman olsa da olmasa da çağrılmaktadır.
__getattr__ metodunun eleman yoksa çağrıldığına dikkat ediniz.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
def __init__(self):
self.a = 10
s = Sample()
result = s.x
result = s.y
result = s.a
#----------------------------------------------------------------------------------
--------------------------------------
61. Ders 30/11/2022 - Çarşamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Sınıfın __getattribute__ metodunda sınıfın bir elemanına erişmeye çalışırsak
yeniden __getattribute__ çağrılacağı için sonsuz
döngü oluşabilir. Örneğin:
class Sample:
def __init__(self):
self.a = 10
raise AttributeError()
s = Sample()
result = s.a
class Sample:
def __init__(self):
self._a = 10
raise AttributeError()
s = Sample()
result = s.a
class Sample:
def __init__(self):
self.a = 10
raise AttributeError()
s = Sample()
result = s.a
print(result)
Burada object.__getattribute(self, name) çağrısı ile name isimli örnek
özniteliğinin değerine erişilmiştir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi sınıfın hem __getattr__ hem de __getattribute__ metotları varsa ne olur?
İşte bu durumda __getattr__ hiç devreye girmez.
Olan elemana erişimde zaten __getattr__ devreye girmyecektir. Olmayan elemana
erişimde de __getattr__ devreye girmez. Yani bu durumda
__getattr__ metodunu yazmanın bir anlamı kalmaz. Örneğin:
class Sample:
def __init__(self):
self.a = 10
s = Sample()
s.a
s.b
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
SInıfların __setattr__ metotları bir sınıf nesnesi ile sınıfın olan ya da
olmayan elemanlarına değer atandığı durumda çağrılmaktadır.
Buı anlamda __setattr__ adeta __getattribute__ metodunun set yapan biçimi
gibidir. Ayrıca bir __setattribute__ metodu bulunmamaktadır.
Örneğin:
class Sample:
def __init__(self):
self.a = 10 # dikkat! yine __setattr__ çağrılacak
s = Sample()
class Sample:
def __init__(self):
self.a = 10
s = Sample()
s.a
s.b
class Sample:
def __init__(self):
self.__dict__['a'] = 10 # __setattr__ çağrılmayacak
s = Sample()
s.a = 10 # dikkat! _setattr__ çağrılacak
import math
class Circle:
def __init__(self, x, y, radius):
object.__setattr__(self, 'x', x)
object.__setattr__(self, 'y', y)
object.__setattr__(self, 'radius', radius)
c = Circle(10, 10, 2)
print([Link])
[Link] = [Link]
print([Link])
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Daha önce de bahsettiğimiz gibi property'ler veri elemanı gibi kullanılan
metotlardır. Biz yukarıda Circle sınıfı örneğinde
Circle sınıfıın area isimli bir örnek özniteliğini kullanmıştır. Ancak gerçekte
böyle bir örnek özniteliği sınıfta yoktu. Aslında
bir örnek özniteliğinin sınıfta olmasına gerek de yoktu. Çünkü eğer biz
dairenin yarıçağını biliyorsak zaten alanını hesaplayabiliriz.
İşte yukarıdaki örnekte Circle sınıfında gereksiz bir area örnek özniteliğini
tutmak yerine biz sanki bu örnek özniteliği varmış
gibi bir kod çalıştırmıştık. Bu kod da bize yarıçaptan hareketle daire alanını
vermiştir. Property'lerin kullanılmasının birinci amacı
bir veri elemanı tutmayıp sanki o veri elemanı varmış gibi bir durum
oluşturmaktır. Ancak ikinci amacı (birinci amaçtan daha aşağı bir amaç
değildir) "veri elemanlarının gizlenmesi (data hiding)" prensibinin
uygulanmasını sağlanaktır. Veri elemanlarının gizlenmesi NYPT'nin
önemli prensipkerinden biridir. Genellikle Python'da programcılar bu prensibi
kullanmazlar. Ancak bazı programcılar property'ler yoluyla
bu prensibi kullanabilmektedir.
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Veri elemanlarının gizlenmesi (data hiding) veri elemanlarının dışarıdan
kullanımını engelleyip onlara kodlar yoluyla erişimi
mümkün hale getirmek anlamına gelmektedir. Python.2da dışarıdan erişilmesini
istemedeiğimiz öniteliklerin başın '_' ve '__' ekliyorduk.
Tabii bu eklama erişimi engellemiyordu. Yalnızca dışarıya bir "erişme"
biçiminde mesaj veriyordu. Halbuıki C++, Java ve C# gibi
diğer dillerde dışarıdan erişimin tamamen engellenmesi için sınıflarda
"private" bulundurulmuştur.
class Date:
def __init__(self, day, month, year):
[Link] = day
[Link] = month
[Link] = year
# ...
Burada sınıfın day, month ve year elemanları yerine programcı daha sonra tarih
bilgisini bir yazıy ile tutacak biçimde tek bir eleman kullanırsa
bu sınıfı kullanan kodlar geçersiz hale gelir. Halbuki bu elemanlara doğrudan
erişimi engelleyip onlara kodlarla erişimi
mümkün hale getirirsek bu durumda biz sınıfın elemanları üzerinde değişiklik
yapsak bile bu değişiklikten onları kullkanmış olan kodların
etkilenmesişni engelleyebiliriz.
[Link] = 100
Burda bir gün hiçbir zaman 100 olmaz ancak bir denetim (validation)
uygulanmamıştırç Halbuki bu atama bir kdola yapısaydı oarada
sınır kontrolü uygulanıp duruma göre exception fırlatılabilirdi.
#----------------------------------------------------------------------------------
--------------------------------------
Python'da property kullanımı aslında property isimli bir dekoratör sınıf
yoluyla kolay bir biçimde yapılabilmektedir.
property sınıfı built-in bir sınıftır. Sınıfın __init__ metodunun parametrik
yapısı şöyledir:
import math
class Circle:
def __init__(self, x, y, radius):
self.x = x
self.y = y
[Link] = radius
def __repr__(self):
return f'center = ({self.x},{self.y}), radius = {[Link]}'
def _get_area(self):
return [Link] * [Link] ** 2
c = Circle(10, 10, 2)
print(c)
print([Link])
[Link] = 10
print(c)
#----------------------------------------------------------------------------------
--------------------------------------
class Date:
def __init__(self, day, month, year):
self._day = day
self._month = month
[Link] = year
def __repr__(self):
return f'{[Link]}/{[Link]}/{[Link]}'
def _get_day(self):
return self._day
def _get_month(self):
return self._month
print(date)
print([Link])
[Link] = 12
#----------------------------------------------------------------------------------
--------------------------------------
property sınıfının __init__ metodunun üçüncü parametresi ilgili örnek
özniteliği del deyimi ile silmeye çalıştığımızda çağrılacak
metodu belirtmektedir. Örneğin:
class Sample:
def __init__(self, a):
self._a = a
def _get_a(self):
return self._a
def _del_a(self):
print('delete a')
s = Sample(10)
#----------------------------------------------------------------------------------
--------------------------------------
Aslında property'ler dekaratör sentaksıyla çok daha kolay bir biçimde
oluşturulabilmektedir. Örneğin:
class Sample:
def __init__(self, a):
self._a = a
@property
def a(self):
return self._a
# a = property(a)
s = Sample(10)
print(s.a)
@property
def a(self):
return self._a
a = property(a)
O halde aslında a örnek özniteliği kullanıldığında a metodu çağrılacaktır. a
metodunu çağırmanın başka bir yolu da yoktur.
Pekiyi dekoratör yoluyla set metodu nasıl yazılmaktadır. İşte set için
dekoratör fonksiyonu görevini property sınıfının setter
metodu yapmaktadır. Örneğin:
class Sample:
def __init__(self, a):
self._a = a
@property
def a(self):
return self._a
# a = property(a)
@[Link]
def a(self, val):
self._a = val
# a = [Link](a)
s = Sample(10)
print(s.a)
s.a = 20
print(s.a)
@[Link]
def a(self, val):
self._a = val
a = [Link](a)
Bu eşdeğer koda göre yeni a metodu property sınıfının setter metoduna parametre
yapılmıştır ve bunun geri dönüş değeri
yine a'ya atanmıştır. Bu durumda property sınıfnın setter metodu aslında bu
yeni a metodunu property nesnesine set metodu olarak eklemektedir.
Yani buradaki setter dekoratörü aslında aşağıdaki gibi oluşturulmuştur:
class myproperty:
def __init__(self, fget = None, fset = None, fdel = None, doc = None):
[Link] = fget
[Link] = fset
[Link] = fdel
[Link] = doc
class Sample:
def __init__(self, a):
self._a = a
@myproperty
def a(self):
return self._a
# a = myproperty(a)
@[Link]
def a(self, val):
self._a = val
# a = [Link](a)
s = Sample(10)
print(type(s.a))
@[Link]
def a(self, val):
self._a = val
Eğer a bir metot ismiyse ve yorumlayıcı bunun için bir fonksiyon nesnesi
oluşturup adresine a'ya atayacaksa önce a bozulmayacak mıdır?
İş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:
a = [Link](some_temporary_name)
class Sample:
def __init__(self, a):
self._a = a
@property
def a(self):
return self._a
# a = property(a)
@[Link]
def a(self, val):
self._a = val
# a = [Link](a)
@[Link]
def a(self):
print('a deleter')
s = Sample(10)
print(s.a)
s.a = 20
print(s.a)
del s.a
#----------------------------------------------------------------------------------
--------------------------------------
Şimdi de dekoratör yoluyla Date sınıfı için property yazalım.
#----------------------------------------------------------------------------------
--------------------------------------
class Date:
def __init__(self, day, month, year):
self._day = day
self._month = month
self._year = year
@property
def day(self):
return self._day
@[Link]
def day(self, val):
self._day = val
@property
def month(self):
return self._month
@[Link]
def month(self, val):
self._month = val
@property
def year(self):
return self._year
@[Link]
def year(self, val):
self._year = val
def __repr__(self):
return f'{self._day}/{self._month}/{self._year}'
print(date)
[Link] = 15
[Link] = 8
[Link] = 2009
print(date)
#----------------------------------------------------------------------------------
--------------------------------------
62. Ders 05/12/2022 - Pazartesi
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Betimleyiciler (descriptors) ya da betimleyici sınıflar 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__ ya da
__set__ ya da __delete__ metotlarının bulunuyor olması gerekir. Genellikle
betimleyici sınıflarda __get__ ve __set__ metotları
bulunabilmektedir. Bazı sınıflarda yalnızca __get__ metodu bulunyor olabilir.
Betimleyici bir sınıf başka bir sınıfın sınıf değişkeni durumunda olur.
Betimleyici sınıfların bu kullanım dışında başka anlamlı
kullanımları yoktur. Örneğin:
class Sample:
a = MyDescriptor()
Bir betimleyici sınıf elemanına sahip olan bir sınıf türünden nesne
yaratıldıktan sonra bu sınıf değişkeni ile betimleyici elemana
değer almak amacıyla erişimde betimleyici sınıfın __get__ metodu, değer
yerleştirmek amacıyla erişimde betimleyici sınıfın __set__ metodu
ve del operatörü ile kullanımda da betimleyici sınıfın __delete__ metodu
çağrılmaktadır. Örneğin:
s = Sample()
result = s.a
s.a = 100
del s.a
#----------------------------------------------------------------------------------
--------------------------------------
class MyDescriptor:
def __get__(self, instance, owner):
print('__get__ params:')
print(type(self), type(instance), type(owner), sep=', ')
print(self, instance, owner, sep=',')
def __repr__(self):
return 'MyDescriptor object'
class Sample:
a = MyDescriptor()
def __repr__(self):
return 'Sample object'
s = Sample()
result = s.a
s.a = 100
del s.a
#----------------------------------------------------------------------------------
--------------------------------------
Pekiyi betimleyici sınıflar ve değişkenler neden kullanılmaktadır? İşte bazı
işlemler ancak bu betimleyici konusu ile
yapılabilmektedir. Örneğin daha önceki konumuzda property isimli bir sınıf
görmüştük ve o sınıfı dekoratör olarak da
kullanmıştık. İşte property gibi bir sınıf yazabilmek için mecburen bir
betimleyici sınıfa gereksinimimiz olur.
class Sample:
def __init__(self, a):
self._a = a
def get_a(self):
print('get_a called ')
return self._a
def del_a(self):
print('del_a called')
s = Sample(100)
result = s.a
print(result)
s.a = 20
del s.a
#----------------------------------------------------------------------------------
--------------------------------------
class myproperty:
def __init__(self, fget = None, fset = None, fdel = None):
[Link] = fget
[Link] = fset
[Link] = fdel
class Sample:
def __init__(self, a):
self._a = a
def get_a(self):
print('get_a called ')
return self._a
def del_a(self):
print('del_a called')
s = Sample(100)
result = s.a
print(result)
s.a = 20
del s.a
#----------------------------------------------------------------------------------
--------------------------------------
Paketler (packages) içerisinde birden fazla Python kaynak dosyası bulunan
dizinlere denilmektedir. Genellikle kütüphaneler
tek bir kaynak dosya olarak yazılmaz. Birden fazla dosya biçiminde organize
edilirler. İşte bir paket içerisinde birden fazla
kaynak dosyadan oluşan bir kavramı belirtmektedir. Python'da paketler "dizin
(directory)" biçiminde oluşturulmak zorundadır.
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 dosyaı yerleştirelim.
Dosyanın içi boş olabilir. Biz bir paketi nasıl bir dosyayı import ediyorsak
dosya gibi import edebiliriz. Örneğin:
import mypackage
# __init__.py
print('__init__.py')
import mypackage
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__.py dosyasının yanı sıra [Link] ve [Link]
dosyalşarını da yerleştirelim. Dosyaların içeriği şöyle olsun:
# [Link]
def foo():
print('foo)
# [Link]
def bar():
print('bar')
import mypackage
import mypackage.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 import edililir. 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 [Link]
dosyasının içeriği çalıştırılacaktır. Örneğin:
import mypackage.a
import mypackage.b
__init__.py
this is mypackage.a module
this is mypackage.b module
import mypackage.a
import mypackage.b
[Link]()
[Link]()
import mypackage
[Link]() # error!
[Link]() # error!
import mypackage.a as a
import mypackage.b as b
[Link]()
[Link]()
foo()
bar()
__init__.py
this is mypackage.a module
this is mypackage.b module
foo
bar
print('__init__.py')
import mypackage.a
import mypackage.b
Şimdi biz paketi import edelim:
import mypackage
import mypackage
[Link]()
[Link]()
# __init__. py
print('__init__.py')
import a # error!
import b # error!
import numpy
Burada numpy bir pakettir. random ise bir dosyadır. Bu random dosyasının import
işlemi paketin __init__ dosyasında yapıldığı için biz
randint fonksiyonunu [Link] biçiminde kullanabildik.
Bazen programcı paketin __init__ metodunda from import deyimi ile o paketteki
dosyaların içerisideki değişkenleri paketin içerisine
taşır. Böylece artık yalnızca paket ismi ile o değişkenlere eriebilir. Örneğin
mypackage paketindeki __init__.py dosyasının içeriği
şöyle olsun:
print('__init__.py')
import mypackage
[Link]()
[Link]()
Artık foo ve bar değişkenlerine mypackage ismiyle eriştik. Ekranda şu yazılar
görünecektir:
__init__.py
this is mypackage.a module
this is mypackage.b module
foo
bar
Burada kullandığımız teknik aslıunda ç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
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
isimleri kullanırız. Örneğin:
import pandas
s = [Link]([1, 2, 3, 4, 5])
print(s)
# __init__.py
print('__init__.py')
import mypackage.a as a
#----------------------------------------------------------------------------------
--------------------------------------
63. Ders 07/12/2022 - Carsamba
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Şimfdi yukarıda oluşturmuş olduğumuz mypackage iismli paketin içerisinde util
isimli bir paket daha oluşturalım. util paketinin
içerisinde de __init__.py dosyası ve bir de [Link] dosyası olsun. Bu dosyaların
içeriği de şöyle olsun:
# mypackage/util/__init__.py
print('util.__init__.py')
# mypackage/util/[Link]
def tar():
print('tar')
mypackage
__init__.py
[Link]
[Link]
util
__init__.py
[Link]
import [Link].c
print('__init__.py')
import c
import util.c
import [Link].c
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
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 neesne string elemanlarından oluşmalıdır.
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
"[Link]" isminde bir Python dosyamız olsun ve bu dosyanın içeriği
aşağıdaki gibi olsun:
def foo():
print('foo')
def bar():
print('bar')
def tar():
print('tar')
a = 10
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:
from x import *
foo()
bar()
tar()
print(a)
print(b)
print(c)
def foo():
print('foo')
def bar():
print('bar')
def tar():
print('tar')
a = 10
b = 20
c = 30
Şimdi biz bu modülü *'lı bir biçimde import edersek yalnızca foo, bar, a ve
b'yi kullanabiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
Python'da diğer dillere ve framework'lere kıyasla nispeten zayıf dokümantasyon
vardır. Bu zayıf dokümantasyon diğer dillerden Python'a geçenler
tarafından bazen şaşkınlıkla karşılanmaktadır. Zayıf dokümantasyon adtea bu
dilde doğal karşılanan bir durum haline gelmiştir.
Pek çok Python programcısı yazdığı fonksiyonların ve sınıfların ve modüllerin
dokümantasyonlarını kodun içerisine gömmektedir. Bunu sağlamak için
Python'da "doküman yazıları (document strings)" denilen bir dil özelliği
kullanılmaktadır.
Bir fonksiyon için doküman yazısı fonksyonun hemen suit'inin başında bir string
olarak belirtilir. Bu string tek tırnaklı ya da üç tırnaklı
olabilir. Bilindiği gibi üç tırnaklı string'ler birden fazla satır üzerine
yazılabilmektedir. Örneğin:
def square(a):
"""square fonksiyonu parametresiyle aldığı değerin karesine geri döner"""
return a * a
#----------------------------------------------------------------------------------
--------------------------------------
def square(a):
"""square fonksiyonu parametresiyle aldığı değerin karesine geri döner"""
return a * 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:
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:
>>> help([Link])
Active code page: 65001
Help on built-in function floor in module math:
floor(x, /)
Return the floor of x as an Integral.
#----------------------------------------------------------------------------------
--------------------------------------
Sınıflar için de sınıfların metotları iöçin de dokğman yazıları
oluşturulabilir. Örneğin:
class Sample:
"Bu sınıf çok önemli işlemler yapan metotlara sahiptir."
def foo(self):
"foo metodu falanca işi yapar."
def bar(self):
"bar metodu filanca işi yapar."
Yine bir sınıfın dokğman yazısı sınıfın __doc__ isimli sınıf değişkeni
biçiminde oluşturulmaktadır. Biz bu dokğman yazılarına sınıf isimleriyle
ya da ilgili sınıf türünden değişkenlerle erişebiliriz.
#----------------------------------------------------------------------------------
--------------------------------------
class Sample:
"Bu sınıf çok önemli işlemler yapan metotlara sahiptir."
def foo(self):
"foo metodu falanca işi yapar."
def bar(self):
"bar metodu filanca işi yapar."
print(Sample.__doc__)
print([Link].__doc__)
print([Link].__doc__)
s = Sample()
print(s.__doc__)
print([Link].__doc__)
print([Link].__doc__)
#----------------------------------------------------------------------------------
--------------------------------------
Programcı isterse tüm modüle de doküman yazısı iliştirebilir. Modülün dokğman
yazıları hemen modülün ilk string'i olmak zorundadır. Öneğin:
# [Link]
def foo():
print('foo')
def bar():
print('bar')
Biz yine modülün doküman yazısını modülün __doc__ değişkeni ile elde
edebiliriz. Örneğin:
import x
print(x.__doc__)
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
Dokğüan yazıları için herkes tarafından kabul edilen bir standart yoktur.
NumPy'ın dokğman yazı standardına ilişkin bağlantı aşağıda verilmiştir:
[Link]
[Link]
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
#----------------------------------------------------------------------------------
--------------------------------------
14 2414
13 2134
12 1838
11 1520
10 1214
09 929
08 649
07 443
06 242
05 83
04 12