Bu bilgiler Eric Matthes’in Kapsamlı Python Kursu Programlama Uygulama ve Proje Tabanlı Giriş kitabından derlenmiştir.
Bu çalışma iki temel konuyu ele almaktadır: Fonksiyonlar ve Sınıflar (Nesne Yönelimli Programlama).
Örnekler; bir kütüphane/kitapçı senaryosu (fonksiyonlar bölümü) ile bir ev robotu ve kitap/sesli kitap senaryosu (sınıflar bölümü) etrafında kurgulanmış, tamamen özgün örneklerdir.
Modüle taşıma örnekleri hakkında: “Fonksiyonlarınızı
Modüllerde Saklamak” ve “Sınıfları İçeri Aktarmak” bölümlerinde, bir
programı ayrı bir .py dosyasına taşıyıp oradan
import etmeyi öğreneceksiniz. Bir modülü
import edebilmek için o modülün gerçekten ayrı bir
.py dosyası olarak diskte var olması gerekir. Bu yüzden
ilgili her bölümde önce o dosyanın içeriği gösterilir; ardından bu kodu
kendinizin bir kod düzenleyicisinde (RStudio, VS Code,
Not Defteri vb.) ilgili dosya adıyla kaydedip, üzerinde çalıştığınız
asıl .py dosyanızla aynı klasöre koymanız
gerektiği açıkça belirtilir.
Fonksiyonlar, belirli bir işi yapan ve isim verilmiş kod bloklarıdır. Bir işlemi tekrar tekrar yazmak yerine, o işlemi bir kez bir fonksiyon içine yazar ve ihtiyaç duyduğumuz her yerde bu fonksiyonu çağırarak kullanırız. Bu yaklaşım kodun okunabilirliğini artırır, hata ayıklamayı kolaylaştırır ve tekrar eden kod bloklarını ortadan kaldırır.
Bir fonksiyon def anahtar kelimesi ile tanımlanır.
Fonksiyon adından sonra parantez içine parametreler yazılır ve satır iki
nokta üst üste (:) ile bitirilir. Fonksiyonun gövdesi bir
sonraki satırdan itibaren girintili (indented) olarak yazılır.
Fonksiyonun ilk satırında yer alan ve üç tırnak içine alınan açıklama
metnine docstring denir; bu, fonksiyonun ne işe
yaradığını anlatan bir belgedir.
def yeni_uye_karsila():
"""Kütüphaneye yeni gelen bir üyeyi karşılar."""
print("Kütüphanemize hoş geldiniz!")
yeni_uye_karsila()
## Kütüphanemize hoş geldiniz!
Fonksiyonu daha esnek hale getirmek için parantez içine bir parametre ekleyebiliriz. Böylece fonksiyon her çağrıldığında farklı bir bilgiyle çalışabilir.
def yeni_uye_karsila(isim):
"""Yeni üyeyi ismiyle karşılar."""
print(f"Kütüphanemize hoş geldin, {isim}!")
yeni_uye_karsila("Ayşe")
## Kütüphanemize hoş geldin, Ayşe!
yeni_uye_karsila() fonksiyonunun tanımında yer alan
isim bir parametredir (fonksiyonun işini
yapabilmesi için ihtiyaç duyduğu bilgi). Fonksiyonu
yeni_uye_karsila("Ayşe") şeklinde çağırdığımızda parantez
içine yazdığımız "Ayşe" değeri ise bir
argümandır (fonksiyon çağrılırken parametreye atanan
gerçek değer). Bu iki terim çoğu zaman birbirinin yerine kullanılsa da
aralarındaki fark şudur: parametre fonksiyonun beklediği şey,
argüman ise çağrı sırasında gönderilen şeydir.
def toplam_fiyat_hesapla(birim_fiyat, adet): # birim_fiyat, adet -> parametre
return birim_fiyat * adet
toplam = toplam_fiyat_hesapla(45, 3) # 45, 3 -> argüman
print(toplam)
## 135
Bir fonksiyon birden fazla parametre kabul edebilir ve bu parametrelere değer göndermenin birkaç farklı yolu vardır: konumsal argümanlar, anahtar kelime argümanları ve listeler/sözlükler. Bu bölümde bu yöntemleri sırasıyla inceleyeceğiz.
Python varsayılan olarak argümanları, fonksiyon tanımındaki parametrelerin sırasına göre eşleştirir. Buna konumsal argüman denir.
def kitap_tanit(yazar, baslik):
"""Bir kitap hakkında bilgi gösterir."""
print(f"\n{yazar} yazarına ait bir kitabımız var.")
print(f"Başlığı: {baslik.title()}.")
kitap_tanit("A. Deniz", "sessiz adalar")
##
## A. Deniz yazarına ait bir kitabımız var.
## Başlığı: Sessiz Adalar.
Aynı fonksiyonu, farklı argüman değerleriyle istediğimiz kadar çağırabiliriz. Bu, fonksiyonların en büyük avantajlarından biridir: kod bir kez yazılır, defalarca kullanılır.
kitap_tanit("A. Deniz", "sessiz adalar")
##
## A. Deniz yazarına ait bir kitabımız var.
## Başlığı: Sessiz Adalar.
kitap_tanit("M. Yıldız", "kayıp zaman")
##
## M. Yıldız yazarına ait bir kitabımız var.
## Başlığı: Kayıp Zaman.
kitap_tanit("E. Kaya", "gece yarısı treni")
##
## E. Kaya yazarına ait bir kitabımız var.
## Başlığı: Gece Yarısı Treni.
Konumsal argümanları yanlış sırada göndermek, mantıksal olarak hatalı ama hata mesajı vermeyen (çalışan) bir sonuç üretebilir. Bu tür hatalar fark edilmesi zor olduğu için dikkatli olunmalıdır. Aşağıdaki çağrıda değerler kasıtlı olarak yer değiştirmiştir; çıktının anlamsal olarak nasıl bozulduğuna dikkat edin:
# Yanlış sıralama: değerler yer değiştirdi
kitap_tanit("sessiz adalar", "A. Deniz")
##
## sessiz adalar yazarına ait bir kitabımız var.
## Başlığı: A. Deniz.
Anahtar kelime argümanında, parametre adı ile değeri birlikte
gönderilir (parametre=değer). Bu sayede argümanların sırası
önemini yitirir ve kodun okunurluğu artar.
kitap_tanit(baslik="sessiz adalar", yazar="A. Deniz")
##
## A. Deniz yazarına ait bir kitabımız var.
## Başlığı: Sessiz Adalar.
Bir parametreye fonksiyon tanımında varsayılan bir değer atanabilir. Çağrı sırasında o parametreye ait argüman gönderilmezse Python varsayılan değeri kullanır; gönderilirse varsayılan değerin üzerine yazılır.
def kitap_tanit(baslik, yazar="Bilinmiyor"):
print(f"\n\"{baslik.title()}\" adlı kitabımız var.")
print(f"Yazarı: {yazar}.")
kitap_tanit(baslik="sessiz adalar") # varsayılan değer kullanılır
##
## "Sessiz Adalar" adlı kitabımız var.
## Yazarı: Bilinmiyor.
kitap_tanit(baslik="kayıp zaman", yazar="M. Yıldız") # varsayılan değer değiştirilir
##
## "Kayıp Zaman" adlı kitabımız var.
## Yazarı: M. Yıldız.
Önemli Not: Varsayılan değere sahip parametreler, tanım satırında varsayılan değeri olmayan parametrelerden sonra yazılmalıdır; aksi halde Python hata verir.
Konumsal, anahtar kelime ve varsayılan değer yaklaşımları bir arada kullanılabilir. Aşağıdaki üç çağrı da birbirine eşdeğerdir ve aynı çıktıyı üretir:
def kitap_tanit(baslik, yazar="Bilinmiyor"):
print(f"\n\"{baslik.title()}\" - {yazar}")
# 1) Konumsal
kitap_tanit("sessiz adalar")
##
## "Sessiz Adalar" - Bilinmiyor
# 2) Anahtar kelime
kitap_tanit(baslik="sessiz adalar")
##
## "Sessiz Adalar" - Bilinmiyor
# 3) Konumsal + anahtar kelime karışık
kitap_tanit("sessiz adalar", yazar="Bilinmiyor")
##
## "Sessiz Adalar" - Bilinmiyor
Eksik ya da fazla argüman gönderildiğinde Python bir
TypeError fırlatır. Bu hata mesajları, hangi argümanın
eksik olduğunu genellikle açıkça belirtir; hata izini (traceback)
dikkatlice okumak sorunu hızlıca çözmemize yardımcı olur.
def kitap_tanit(yazar, baslik):
print(f"\n{yazar} yazarına ait \"{baslik}\" adlı kitabımız var.")
kitap_tanit() # Hiç argüman gönderilmedi
## TypeError: kitap_tanit() missing 2 required positional arguments: 'yazar' and 'baslik'
Bir fonksiyon, ekrana bir şey yazdırmak yerine (ya da bunun yanında)
bir değer döndürebilir. return ifadesiyle
döndürülen değer, fonksiyonu çağıran koda aktarılır ve bir değişkende
saklanabilir.
def yazar_adi_olustur(ad, soyad):
"""Ad ve soyadı birleştirip düzgün biçimli yazar adı döndürür."""
tam_isim = f"{ad} {soyad}"
return tam_isim.title()
yazar_adi = yazar_adi_olustur("elif", "kara")
print(yazar_adi)
## Elif Kara
Bir parametreye boş bir varsayılan değer (örneğin boş dize
"") vererek o argümanı isteğe bağlı hale getirebiliriz.
Fonksiyon içinde bir if koşuluyla bu değerin gönderilip
gönderilmediği kontrol edilir.
def yazar_adi_olustur(ad, soyad, mahlas=""):
"""Ad, soyad ve isteğe bağlı mahlası birleştirir."""
if mahlas:
tam_isim = f'{ad} "{mahlas}" {soyad}'
else:
tam_isim = f"{ad} {soyad}"
return tam_isim.title()
print(yazar_adi_olustur("elif", "kara"))
## Elif Kara
print(yazar_adi_olustur("elif", "kara", "e.k."))
## Elif "E.K." Kara
Bir fonksiyon, herhangi bir veri türünü -bir sayı, bir dize, bir liste ya da bir sözlük- döndürebilir. Aşağıdaki örnekte kitap bilgileri bir sözlük olarak döndürülüyor.
def kitap_bilgisi_olustur(baslik, yazar, sayfa_sayisi=None):
"""Bir kitapla ilgili bilgileri içeren bir sözlük döndürür."""
kitap = {"baslik": baslik, "yazar": yazar}
if sayfa_sayisi:
kitap["sayfa_sayisi"] = sayfa_sayisi
return kitap
yeni_kitap = kitap_bilgisi_olustur("Gece Yarısı Treni", "E. Kaya", sayfa_sayisi=284)
print(yeni_kitap)
## {'baslik': 'Gece Yarısı Treni', 'yazar': 'E. Kaya', 'sayfa_sayisi': 284}
Fonksiyonlar while döngüleriyle birlikte kullanılabilir;
örneğin kullanıcıdan tekrar tekrar giriş almak ve bu girdiyi bir
fonksiyonla işlemek için. Bu belge otomatik olarak (knit ile)
derlendiğinde gerçek bir klavyeden girdi alınamayacağı için,
input() yerine önceden hazırlanmış bir girdi kuyruğu
kullanılmıştır; kendi .py dosyanızda bu satırları doğrudan
input() ile değiştirebilirsiniz.
def yazar_adi_olustur(ad, soyad):
return f"{ad} {soyad}".title()
girdi_kuyrugu = ["elif", "kara", "burak", "aydın", "q"]
while True:
print("\nLütfen yazar adını giriniz ('q' çıkmak için):")
ad = girdi_kuyrugu.pop(0) # input("Ad: ") yerine
print(f"Ad: {ad}")
if ad == "q":
break
soyad = girdi_kuyrugu.pop(0) # input("Soyad: ") yerine
print(f"Soyad: {soyad}")
if soyad == "q":
break
yazar = yazar_adi_olustur(ad, soyad)
print(f"\nKataloğa eklendi: {yazar}")
##
## Lütfen yazar adını giriniz ('q' çıkmak için):
## Ad: elif
## Soyad: kara
##
## Kataloğa eklendi: Elif Kara
##
## Lütfen yazar adını giriniz ('q' çıkmak için):
## Ad: burak
## Soyad: aydın
##
## Kataloğa eklendi: Burak Aydın
##
## Lütfen yazar adını giriniz ('q' çıkmak için):
## Ad: q
Bir fonksiyona parametre olarak bir liste gönderebiliriz; bu sayede
fonksiyon, listedeki elemanlar üzerinde -örneğin bir for
döngüsüyle- işlem yapabilir.
def kitaplari_duyur(kitap_listesi):
"""Listedeki her kitap için bir duyuru mesajı yazdırır."""
for kitap in kitap_listesi:
mesaj = f"Yeni geldi: {kitap.title()}!"
print(mesaj)
yeni_gelenler = ["rüzgarın izinde", "unutulan şehir", "kayıp zaman"]
kitaplari_duyur(yeni_gelenler)
## Yeni geldi: Rüzgarın Izinde!
## Yeni geldi: Unutulan Şehir!
## Yeni geldi: Kayıp Zaman!
Bir listeyi bir fonksiyona gönderdiğimizde, fonksiyon o listeyi
kalıcı olarak değiştirebilir. Bu, büyük miktarda veriyi verimli bir
şekilde işlemek için kullanışlıdır. Aşağıdaki örnekte
bekleyenler listesinin fonksiyon çağrısından sonra
gerçekten boşaldığına dikkat edin:
def kitaplari_rafla(bekleyen_kitaplar, raflanan_kitaplar):
"""Her kitabı işleyip raflanan kitaplar listesine taşır."""
while bekleyen_kitaplar:
mevcut_kitap = bekleyen_kitaplar.pop()
print(f"{mevcut_kitap} rafa kaldırılıyor...")
raflanan_kitaplar.append(mevcut_kitap)
def raflanan_kitaplari_goster(raflanan_kitaplar):
print("\nAşağıdaki kitaplar rafa kaldırıldı:")
for kitap in raflanan_kitaplar:
print(f"- {kitap}")
bekleyenler = ["sessiz adalar", "kayıp zaman", "gece yarısı treni"]
raflananlar = []
kitaplari_rafla(bekleyenler, raflananlar)
## gece yarısı treni rafa kaldırılıyor...
## kayıp zaman rafa kaldırılıyor...
## sessiz adalar rafa kaldırılıyor...
raflanan_kitaplari_goster(raflananlar)
##
## Aşağıdaki kitaplar rafa kaldırıldı:
## - gece yarısı treni
## - kayıp zaman
## - sessiz adalar
print(f"\nOrijinal 'bekleyenler' listesi artık boş: {bekleyenler}")
##
## Orijinal 'bekleyenler' listesi artık boş: []
Fonksiyonun orijinal listeyi değiştirmesini istemiyorsak, listenin
bir kopyasını gönderebiliriz. Bu,
liste_adi[:] dilimleme (slice) sözdizimiyle yapılır. Bu kez
bekleyenler listesinin çağrıdan sonra da dolu kaldığına
dikkat edin:
def kitaplari_rafla(bekleyen_kitaplar, raflanan_kitaplar):
while bekleyen_kitaplar:
mevcut_kitap = bekleyen_kitaplar.pop()
print(f"{mevcut_kitap} rafa kaldırılıyor...")
raflanan_kitaplar.append(mevcut_kitap)
bekleyenler = ["sessiz adalar", "kayıp zaman", "gece yarısı treni"]
raflananlar = []
# Listenin kopyasını göndererek orijinal listeyi koruyoruz
kitaplari_rafla(bekleyenler[:], raflananlar)
## gece yarısı treni rafa kaldırılıyor...
## kayıp zaman rafa kaldırılıyor...
## sessiz adalar rafa kaldırılıyor...
print(f"\nOrijinal 'bekleyenler' listesi hâlâ dolu: {bekleyenler}")
##
## Orijinal 'bekleyenler' listesi hâlâ dolu: ['sessiz adalar', 'kayıp zaman', 'gece yarısı treni']
print(f"Raflananlar: {raflananlar}")
## Raflananlar: ['gece yarısı treni', 'kayıp zaman', 'sessiz adalar']
Önemli Not: Kopya göndermek her zaman gerekli olmadığı sürece tercih edilmemelidir; çünkü büyük listelerde kopya oluşturmak zaman ve bellek açısından maliyetlidir. Orijinal listeye ihtiyaç yoksa doğrudan listeyi göndermek daha verimlidir.
Bazen bir fonksiyonun kaç argüman alacağını önceden bilemeyiz.
Python, parametre önüne bir yıldız (*) koyarak fonksiyona
keyfi (esnek) sayıda argüman gönderilmesine izin verir.
Bu argümanlar fonksiyon içinde bir demet (tuple) olarak
toplanır.
def kitap_sepeti_olustur(*kitaplar):
"""Sepete eklenen kitapları listeler."""
print("\nSepete şu kitaplar ekleniyor:")
for kitap in kitaplar:
print(f"- {kitap}")
kitap_sepeti_olustur("Sessiz Adalar")
##
## Sepete şu kitaplar ekleniyor:
## - Sessiz Adalar
kitap_sepeti_olustur("Kayıp Zaman", "Gece Yarısı Treni", "Unutulan Şehir")
##
## Sepete şu kitaplar ekleniyor:
## - Kayıp Zaman
## - Gece Yarısı Treni
## - Unutulan Şehir
Keyfi sayıda argüman kabul eden parametre, fonksiyon tanımında en sona yazılmalıdır. Python önce diğer konumsal ve anahtar kelime argümanlarını eşleştirir, kalan tüm değerleri yıldızlı parametreye toplar.
def siparis_olustur(paket_turu, *kitaplar):
"""Paket türü ve sipariş edilen kitapları gösterir."""
print(f"\n{paket_turu} hazırlanıyor:")
for kitap in kitaplar:
print(f"- {kitap}")
siparis_olustur("Hediye Paketi", "Sessiz Adalar")
##
## Hediye Paketi hazırlanıyor:
## - Sessiz Adalar
siparis_olustur("Standart Paket", "Kayıp Zaman", "Gece Yarısı Treni", "Unutulan Şehir")
##
## Standart Paket hazırlanıyor:
## - Kayıp Zaman
## - Gece Yarısı Treni
## - Unutulan Şehir
Bazen kaç tane anahtar kelime argümanının geleceğini de önceden
bilemeyiz. İki yıldız (**) kullanılarak tanımlanan
parametre, gönderilen tüm anahtar kelime argümanlarını bir
sözlük olarak toplar.
def uye_profili_olustur(ad, soyad, **ekstra_bilgi):
"""Bir kütüphane üyesi sözlüğü oluşturur."""
profil = {"ad": ad, "soyad": soyad}
for anahtar, deger in ekstra_bilgi.items():
profil[anahtar] = deger
return profil
uye = uye_profili_olustur(
"Deniz", "Aksoy",
favori_tur="Bilim Kurgu", uye_no=1042
)
print(uye)
## {'ad': 'Deniz', 'soyad': 'Aksoy', 'favori_tur': 'Bilim Kurgu', 'uye_no': 1042}
Fonksiyonları ana programdan ayrı, .py uzantılı bir
dosyada (modül) saklamak kodu daha düzenli ve tekrar
kullanılabilir hale getirir. Aşağıda, bu bölümde kullanacağımız
siparis.py modülünün içeriğini görüyorsunuz (bu blok
yalnızca referans amaçlıdır, çalıştırılabilir değildir):
# siparis.py dosyası (içerik referansı)
def siparis_olustur(paket_turu, *kitaplar):
print(f"{paket_turu} hazırlanıyor:")
for kitap in kitaplar:
print(f"- {kitap}")
Bu kodu kendiniz oluşturmalısınız. Yukarıdaki kodu
bir kod düzenleyicisinde yeni bir dosyaya yapıştırıp
siparis.py adıyla kaydedin ve bu dosyayı,
üzerinde çalıştığınız asıl Python dosyanızla (ya da bu ders notunu takip
ettiğiniz .py/Jupyter dosyanızla) aynı
klasöre koyun. Aşağıdaki import örnekleri,
siparis.py dosyasının aynı klasörde, elinizin altında hazır
olduğunu varsayar; dosyayı oluşturmadan bu örnekleri kendi ortamınızda
çalıştırmaya çalışırsanız ModuleNotFoundError hatası
alırsınız.
import module_adi yazarak, modül içindeki tüm
fonksiyonları kullanılabilir hale getiririz. Fonksiyonlara erişmek için
module_adi.fonksiyon_adi() sözdizimi kullanılır.
import siparis
siparis.siparis_olustur("Hediye Paketi", "Sessiz Adalar")
## Hediye Paketi hazırlanıyor:
## - Sessiz Adalar
siparis.siparis_olustur("Standart Paket", "Kayıp Zaman", "Gece Yarısı Treni")
## Standart Paket hazırlanıyor:
## - Kayıp Zaman
## - Gece Yarısı Treni
Modüldeki fonksiyonlardan yalnızca belirli olanları
from module_adi import fonksiyon_adi sözdizimiyle içeri
aktarabiliriz. Bu durumda modül öneki olmadan doğrudan fonksiyon adını
kullanabiliriz.
from siparis import siparis_olustur
siparis_olustur("Hediye Paketi", "Sessiz Adalar")
## Hediye Paketi hazırlanıyor:
## - Sessiz Adalar
Fonksiyon adı çok uzunsa, başka bir isimle çakışıyorsa ya da sadece
daha kısa bir isim tercih ediliyorsa, as anahtar
kelimesiyle bir takma isim (alias) verilebilir.
from siparis import siparis_olustur as yeni_siparis
yeni_siparis("Hediye Paketi", "Sessiz Adalar")
## Hediye Paketi hazırlanıyor:
## - Sessiz Adalar
Aynı şekilde, modülün kendisine de bir takma isim verilebilir. Bu,
özellikle uzun modül isimlerini kısaltmak için sık kullanılır (örneğin
pandas kütüphanesinin geleneksel olarak pd
şeklinde içeri aktarılması gibi).
import siparis as sp
sp.siparis_olustur("Hediye Paketi", "Sessiz Adalar")
## Hediye Paketi hazırlanıyor:
## - Sessiz Adalar
Yıldız (*) operatörü ile bir modüldeki
tüm fonksiyonlar, önek kullanmadan içeri
aktarılabilir.
from siparis import *
siparis_olustur("Hediye Paketi", "Sessiz Adalar")
## Hediye Paketi hazırlanıyor:
## - Sessiz Adalar
Uyarı: Bu yöntem genellikle önerilmez. Modülde çok sayıda fonksiyon varsa ya da kendi programınızdaki isimlerle çakışan fonksiyon adları varsa beklenmedik hatalara yol açabilir. Okunabilirlik açısından da
import module_adiya dafrom module_adi import fonksiyon_adibiçimleri tercih edilmelidir.
Python topluluğunun benimsediği stil rehberi PEP 8’e göre fonksiyon yazarken dikkat edilmesi gereken bazı noktalar:
_) kullanılarak yazılmalıdır (snake_case):
kitap_tanit() gibi.=) etrafında boşluk bırakılmamalıdır:
def kitap_tanit(baslik, yazar="Bilinmiyor"):import ifadeleri, dosyanın en başında,
docstring’lerden sonra yer almalıdır.def detayli_kitap_bilgisi_olustur(
baslik, yazar, sayfa_sayisi, yayin_yili):
"""PEP 8'e uygun çok satırlı parametre örneği."""
print(baslik, yazar)
detayli_kitap_bilgisi_olustur("Sessiz Adalar", "A. Deniz", 320, 2019)
## Sessiz Adalar A. Deniz
Bu bölümde şunları öğrendik:
def, docstring,
girinti).return ile bir değer (sayı, dize, liste, sözlük)
döndürmek.*args ve **kwargs ile keyfi sayıda argüman
kabul eden fonksiyonlar yazmak.import
yöntemleriyle içeri aktarmak.Nesne yönelimli programlama (OOP), yazılım geliştirmenin en etkili yaklaşımlarından biridir. Bir sınıf (class), belirli bir türdeki nesnelerin nasıl bilgi taşıyacağını (nitelikler/attributes) ve nasıl davranacağını (metotlar/methods) tanımlayan bir şablondur. Bir sınıftan üretilen her bir nesneye örnek (instance) denir.
Bir sınıf, class anahtar kelimesiyle tanımlanır. Python
geleneğine göre sınıf isimleri BüyükHarfleBaşlar
(CapWords / PascalCase) biçiminde yazılır.
class Robot:
"""Basit bir ev robotunu temsil etmek için bir girişim."""
def __init__(self, isim, seri_no):
"""İsim ve seri numarası niteliklerini başlatır."""
self.isim = isim
self.seri_no = seri_no
def calismaya_basla(self):
"""Robotu çalışmaya başlar şekilde simüle eder."""
print(f"{self.isim} çalışmaya başladı.")
def sarj_ol(self):
"""Robotu şarj olur şekilde simüle eder."""
print(f"{self.isim} şimdi şarj oluyor.")
__init__() metodu, bir sınıftan yeni bir örnek
oluşturulduğunda Python tarafından otomatik olarak
çalıştırılan özel bir metottur (yapıcı metot / constructor). Başındaki
ve sonundaki çift alt çizgiler, bu ismin Python tarafından özel bir amaç
için ayrıldığını gösterir. Metodun ilk parametresi her zaman
self olmalıdır; bu parametre, oluşturulacak örneğin
kendisine referans verir ve Python tarafından otomatik olarak
gönderilir; çağrı sırasında bizim bunu belirtmemize gerek yoktur.
self.isim ve self.seri_no gibi
self ile başlayan değişkenlere nitelik
(attribute) denir ve bu nitelikler sınıftan üretilen her örnek
tarafından erişilebilir.
Bir sınıf tanımlandıktan sonra, sınıf adını fonksiyon çağrısı gibi
kullanarak yeni bir örnek oluşturabiliriz. Niteliklere
nokta gösterimi (dot notation) ile, metotlara ise yine
nokta gösterimiyle parantez ekleyerek erişilir.
class Robot:
def __init__(self, isim, seri_no):
self.isim = isim
self.seri_no = seri_no
def calismaya_basla(self):
print(f"{self.isim} çalışmaya başladı.")
def sarj_ol(self):
print(f"{self.isim} şimdi şarj oluyor.")
ev_robotum = Robot("Arya", "RB-204")
print(f"Robotumun adı {ev_robotum.isim}.")
## Robotumun adı Arya.
print(f"Seri numarası {ev_robotum.seri_no}.")
## Seri numarası RB-204.
ev_robotum.calismaya_basla()
## Arya çalışmaya başladı.
ev_robotum.sarj_ol()
## Arya şimdi şarj oluyor.
Sınıflar genellikle gerçek dünyadaki nesneleri (bir kitap, bir
kullanıcı, bir cihaz vb.) modellemek için kullanılır. Bu bölümde bir
Kitap sınıfı üzerinden nitelikleri kullanmayı ve
değiştirmeyi inceleyeceğiz.
class Kitap:
"""Bir kitabı temsil etmek için basit bir girişim."""
def __init__(self, baslik, yazar, sayfa_sayisi):
"""Kitabı tanımlayan temel nitelikleri başlatır."""
self.baslik = baslik
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
def tanitici_bilgi_al(self):
"""Düzgün biçimlendirilmiş tanıtıcı bilgi döndürür."""
tanitici_bilgi = f"{self.baslik} - {self.yazar}"
return tanitici_bilgi.title()
kitabim = Kitap("sessiz adalar", "a. deniz", 320)
print(kitabim.tanitici_bilgi_al())
## Sessiz Adalar - A. Deniz
Bir sınıftan üretilen tüm örnekler, aynı başlangıç değerine sahip bir
nitelikle başlayabilir. Bu tür bir nitelik __init__()
içinde, parametre olarak alınmadan doğrudan tanımlanır.
class Kitap:
def __init__(self, baslik, yazar, sayfa_sayisi):
self.baslik = baslik
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
self.okunan_sayfa = 0 # varsayılan değer
def tanitici_bilgi_al(self):
return f"{self.baslik} - {self.yazar}".title()
def okuma_durumunu_goster(self):
print(f"Şu ana kadar {self.okunan_sayfa} sayfa okundu.")
kitabim = Kitap("sessiz adalar", "a. deniz", 320)
kitabim.okuma_durumunu_goster()
## Şu ana kadar 0 sayfa okundu.
Bir örneğin nitelik değeri üç farklı şekilde değiştirilebilir: doğrudan örnek üzerinden, bir metot aracılığıyla ya da yine bir metot aracılığıyla mevcut değeri artırarak.
class Kitap:
def __init__(self, baslik, yazar, sayfa_sayisi):
self.baslik = baslik
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
self.okunan_sayfa = 0
def okuma_durumunu_goster(self):
print(f"Şu ana kadar {self.okunan_sayfa} sayfa okundu.")
def sayfa_ayarla(self, sayfa):
"""Okunan sayfa sayısını verilen değere ayarlar (geri saymayı engeller)."""
if sayfa >= self.okunan_sayfa:
self.okunan_sayfa = sayfa
else:
print("Okunan sayfa sayısı geri alınamaz!")
def sayfa_ilerlet(self, miktar):
"""Okunan sayfa sayısına belirtilen miktarı ekler."""
if miktar >= 0:
self.okunan_sayfa += miktar
else:
print("Okunan sayfaya negatif değer eklenemez!")
kitabim = Kitap("kayıp zaman", "m. yıldız", 410)
# 1) Doğrudan değiştirme
kitabim.okunan_sayfa = 15
# 2) Metot aracılığıyla ayarlama
kitabim.sayfa_ayarla(80)
# 3) Metot aracılığıyla artırma
kitabim.sayfa_ilerlet(40)
kitabim.okuma_durumunu_goster()
## Şu ana kadar 120 sayfa okundu.
Bir sınıf, başka bir sınıfın niteliklerini ve metotlarını devralabilir. Bu işleme kalıtım (inheritance) denir. Yeni sınıfa alt sınıf (child/subclass), devraldığı sınıfa ise üst sınıf (parent/superclass) adı verilir. Alt sınıf, üst sınıfın tüm nitelik ve metotlarını miras alır; buna ek olarak kendi nitelik ve metotlarını da tanımlayabilir.
Bir alt sınıf oluştururken, Python’un üst sınıfın nitelik ve
metotlarını otomatik olarak devralabilmesi için
super().__init__() çağrısı kullanılır.
super(), üst sınıfa özel bir bağlantı sağlayan Python
fonksiyonudur.
class Kitap:
def __init__(self, baslik, yazar, sayfa_sayisi):
self.baslik = baslik
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
def tanitici_bilgi_al(self):
return f"{self.baslik} - {self.yazar}".title()
class SesliKitap(Kitap):
"""Sesli kitaplara özgü nitelikleri temsil eder."""
def __init__(self, baslik, yazar, sayfa_sayisi):
"""Üst sınıfın niteliklerini başlatır."""
super().__init__(baslik, yazar, sayfa_sayisi)
kayip_zaman_sesli = SesliKitap("kayıp zaman", "m. yıldız", 410)
print(kayip_zaman_sesli.tanitici_bilgi_al())
## Kayıp Zaman - M. Yıldız
Bir alt sınıf, üst sınıfa ait olmayan, kendine özgü nitelik ve metotlarla genişletilebilir. Bu nitelikler yalnızca alt sınıftan üretilen örneklerde bulunur.
class Kitap:
def __init__(self, baslik, yazar, sayfa_sayisi):
self.baslik = baslik
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
def tanitici_bilgi_al(self):
return f"{self.baslik} - {self.yazar}".title()
class SesliKitap(Kitap):
def __init__(self, baslik, yazar, sayfa_sayisi):
super().__init__(baslik, yazar, sayfa_sayisi)
self.sure_dakika = 560 # yalnızca SesliKitap'a özgü
def dinleme_suresini_goster(self):
saat = self.sure_dakika // 60
dakika = self.sure_dakika % 60
print(f"Bu sesli kitap {saat} saat {dakika} dakika sürüyor.")
kayip_zaman_sesli = SesliKitap("kayıp zaman", "m. yıldız", 410)
kayip_zaman_sesli.dinleme_suresini_goster()
## Bu sesli kitap 9 saat 20 dakika sürüyor.
Alt sınıf, üst sınıftan devraldığı bir metodu, kendi ihtiyacına göre yeniden tanımlayarak (override) değiştirebilir. Alt sınıfta üst sınıftaki ile aynı isimde bir metot tanımlandığında, Python üst sınıftaki metodu göz ardı eder ve alt sınıftaki tanımı kullanır.
class Kitap:
def __init__(self, baslik, yazar, sayfa_sayisi):
self.baslik = baslik
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
def tanitici_bilgi_al(self):
return f"{self.baslik} - {self.yazar}".title()
def tuketim_bicimi(self):
print("Bu kitap sayfalar hâlinde okunur.")
class SesliKitap(Kitap):
def __init__(self, baslik, yazar, sayfa_sayisi):
super().__init__(baslik, yazar, sayfa_sayisi)
def tuketim_bicimi(self):
"""Sesli kitaplar dinlenerek tüketilir."""
print("Bu kitap dinlenerek tüketilir.")
kayip_zaman_sesli = SesliKitap("kayıp zaman", "m. yıldız", 410)
kayip_zaman_sesli.tuketim_bicimi()
## Bu kitap dinlenerek tüketilir.
Bir sınıfın parçalarını -daha karmaşık hale geldikçe- ayrı bir sınıfa taşımak faydalı olabilir. Bu yaklaşımda, bir sınıfın örneği, başka bir sınıfın niteliği olarak kullanılır (buna kompozisyon, composition denir). Bu, “her SesliKitap bir Anlatıcı’ya sahiptir” ilişkisini modellemenin doğal bir yoludur.
class Kitap:
def __init__(self, baslik, yazar, sayfa_sayisi):
self.baslik = baslik
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
def tanitici_bilgi_al(self):
return f"{self.baslik} - {self.yazar}".title()
class Anlatici:
"""Bir sesli kitap anlatıcısını temsil etmek için basit bir sınıf."""
def __init__(self, isim, deneyim_yili=1):
self.isim = isim
self.deneyim_yili = deneyim_yili
def deneyim_seviyesi_goster(self):
if self.deneyim_yili >= 10:
seviye = "Usta"
elif self.deneyim_yili >= 3:
seviye = "Deneyimli"
else:
seviye = "Yeni"
print(f"{self.isim}: {seviye} anlatıcı ({self.deneyim_yili} yıl).")
class SesliKitap(Kitap):
def __init__(self, baslik, yazar, sayfa_sayisi):
super().__init__(baslik, yazar, sayfa_sayisi)
self.anlatici = Anlatici("Naz Tekin", deneyim_yili=6) # Anlatici örneği bir nitelik olarak eklendi
kayip_zaman_sesli = SesliKitap("kayıp zaman", "m. yıldız", 410)
print(kayip_zaman_sesli.tanitici_bilgi_al())
## Kayıp Zaman - M. Yıldız
kayip_zaman_sesli.anlatici.deneyim_seviyesi_goster()
## Naz Tekin: Deneyimli anlatıcı (6 yıl).
Sınıflar ve kalıtım hiyerarşisi tasarlanırken kendinize şu soruları sormanız faydalı olur: Modellediğim gerçek nesne hangi bilgileri taşımalı? Bu nesne hangi davranışları (metotları) sergilemeli? Ortak nitelik/metotları paylaşan başka nesne türleri var mı (kalıtım için aday mı)? Kod her zaman gerçek dünyayı birebir yansıtmak zorunda değildir; amaç, probleminizi verimli şekilde çözecek kadar gerçekçi bir soyutlama kurmaktır. Sınıflar başlangıçta basit tutulmalı, ihtiyaç ortaya çıktıkça yeni nitelik ve metotlarla genişletilmelidir.
Program büyüdükçe, birden fazla sınıfı ayrı bir dosyada
(modül) saklamak ve gerektiğinde ana programa içeri
aktarmak, kodu daha düzenli hale getirir. Aşağıda, bu bölümde
kullanacağımız kitap.py modülünün içeriğini görüyorsunuz
(bu blok yalnızca referans amaçlıdır, çalıştırılabilir değildir):
# kitap.py dosyası (içerik referansı)
class Kitap:
def __init__(self, baslik, yazar, sayfa_sayisi):
self.baslik = baslik
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
def tanitici_bilgi_al(self):
return f"{self.baslik} - {self.yazar}".title()
class Anlatici:
def __init__(self, isim, deneyim_yili=1):
self.isim = isim
self.deneyim_yili = deneyim_yili
def deneyim_seviyesi_goster(self):
if self.deneyim_yili >= 10:
seviye = "Usta"
elif self.deneyim_yili >= 3:
seviye = "Deneyimli"
else:
seviye = "Yeni"
print(f"{self.isim}: {seviye} anlatıcı ({self.deneyim_yili} yıl).")
class SesliKitap(Kitap):
def __init__(self, baslik, yazar, sayfa_sayisi):
super().__init__(baslik, yazar, sayfa_sayisi)
self.anlatici = Anlatici("Naz Tekin", deneyim_yili=6)
Bu kodu kendiniz oluşturmalısınız. Yukarıdaki kodu
bir kod düzenleyicisinde yeni bir dosyaya yapıştırıp
kitap.py adıyla kaydedin ve bu dosyayı,
üzerinde çalıştığınız asıl Python dosyanızla aynı
klasöre koyun. Aşağıdaki import örnekleri,
kitap.py dosyasının aynı klasörde hazır olduğunu
varsayar.
from kitap import Kitap
kitabim = Kitap("sessiz adalar", "a. deniz", 320)
print(kitabim.tanitici_bilgi_al())
## Sessiz Adalar - A. Deniz
Yukarıdaki kitap.py dosyasında görüldüğü gibi, ilişkili
birden fazla sınıf (Kitap, Anlatici,
SesliKitap) aynı modül içinde birlikte saklanabilir.
Bir modülden birden fazla sınıfı, virgülle ayırarak tek bir
from...import ifadesiyle içeri aktarabiliriz.
from kitap import Kitap, SesliKitap
kitabim = Kitap("sessiz adalar", "a. deniz", 320)
print(kitabim.tanitici_bilgi_al())
## Sessiz Adalar - A. Deniz
kayip_zaman_sesli = SesliKitap("kayıp zaman", "m. yıldız", 410)
print(kayip_zaman_sesli.tanitici_bilgi_al())
## Kayıp Zaman - M. Yıldız
import module_adi sözdizimiyle, modüldeki tüm sınıflara
module_adi.SinifAdi biçiminde erişilebilir.
import kitap
kitabim = kitap.Kitap("sessiz adalar", "a. deniz", 320)
kayip_zaman_sesli = kitap.SesliKitap("kayıp zaman", "m. yıldız", 410)
print(kitabim.tanitici_bilgi_al())
## Sessiz Adalar - A. Deniz
print(kayip_zaman_sesli.tanitici_bilgi_al())
## Kayıp Zaman - M. Yıldız
from kitap import *
kitabim = Kitap("sessiz adalar", "a. deniz", 320)
print(kitabim.tanitici_bilgi_al())
## Sessiz Adalar - A. Deniz
Uyarı: Bu yöntem sınıflar için de önerilmez; hangi sınıfların nereden geldiği belirsizleşir ve isim çakışmaları oluşabilir.
import kitapya da açıkçafrom kitap import Kitap, SesliKitapbiçimleri tercih edilmelidir.
Bazen bir sınıf, başka bir modüldeki sınıfı kullanır; bu durumda o
modülün de kendi dosyasının başında içeri aktarılması gerekir. Örneğin
SesliKitap sınıfını ayrı bir sesli_kitap.py
dosyasına taşıdığımızı düşünelim; bu dosyanın içeriği şöyle olurdu (bu
blok da yalnızca referans amaçlıdır):
# sesli_kitap.py dosyası (içerik referansı)
from kitap import Kitap # başka bir modülden içeri aktarma
class Anlatici:
def __init__(self, isim, deneyim_yili=1):
self.isim = isim
self.deneyim_yili = deneyim_yili
class SesliKitap(Kitap):
def __init__(self, baslik, yazar, sayfa_sayisi):
super().__init__(baslik, yazar, sayfa_sayisi)
self.anlatici = Anlatici("Naz Tekin", deneyim_yili=6)
Bu kodu da kendiniz oluşturmalısınız. Yukarıdaki
kodu bir kod düzenleyicisinde yeni bir dosyaya yapıştırıp
sesli_kitap.py adıyla kaydedin ve bu
dosyayı da, kitap.py ile ve asıl çalıştığınız Python
dosyanızla aynı klasöre koyun;
sesli_kitap.py içindeki
from kitap import Kitap satırının çalışabilmesi için
kitap.py dosyasının da aynı klasörde bulunması gerekir.
Ana programda ise her iki modülü de içeri aktarabiliriz:
from kitap import Kitap
from sesli_kitap import SesliKitap
kitabim = Kitap("sessiz adalar", "a. deniz", 320)
kayip_zaman_sesli = SesliKitap("kayıp zaman", "m. yıldız", 410)
print(kitabim.tanitici_bilgi_al())
## Sessiz Adalar - A. Deniz
print(kayip_zaman_sesli.tanitici_bilgi_al())
## Kayıp Zaman - M. Yıldız
Fonksiyonlarda olduğu gibi, sınıflara da as anahtar
kelimesiyle takma isim verilebilir. Bu, uzun sınıf isimlerini kısaltmak
ya da isim çakışmalarını önlemek için kullanılır.
from sesli_kitap import SesliKitap as SK
kayip_zaman_sesli = SK("kayıp zaman", "m. yıldız", 410)
print(kayip_zaman_sesli.tanitici_bilgi_al())
## Kayıp Zaman - M. Yıldız
Kod dosyalarını nasıl organize edeceğiniz konusunda tek bir doğru yol yoktur; önemli olan, projeniz büyüdükçe kodun anlaşılır ve yönetilebilir kalmasını sağlamaktır. Genel bir öneri olarak: önce çalışan basit bir sürümle başlayın, gerektikçe sınıfları ayrı modüllere taşıyın ve kodu düzenli aralıklarla test edin. Zamanla, hangi sınıfların birlikte hangilerinin ayrı dosyalarda durması gerektiğine dair kendi sezginizi geliştireceksiniz.
Python; kendi kurduğunuz modüllerin yanı sıra, kurulumla birlikte
gelen standart kütüphane (standard library) adında
zengin bir modül koleksiyonuyla birlikte gelir. Bu modüller ek bir
kurulum gerektirmeden import edilebilir. Örneğin
random modülü rastgele değer üretmek,
collections modülü ise ek veri yapıları sunmak için
kullanılır. (Önerilen kitap her knit işleminde değişebilir, çünkü
gerçekten rastgele seçilir.)
from random import choice
kitap_onerileri = ["Sessiz Adalar", "Kayıp Zaman", "Gece Yarısı Treni", "Unutulan Şehir"]
# Listeden rastgele bir kitap önerisi seç
onerilen_kitap = choice(kitap_onerileri)
print(f"Bugünün önerisi: {onerilen_kitap}")
## Bugünün önerisi: Gece Yarısı Treni
from collections import OrderedDict
kutuphane_kurallari = OrderedDict()
kutuphane_kurallari["İade süresi"] = "14 gün"
kutuphane_kurallari["Üyelik ücreti"] = "Ücretsiz"
kutuphane_kurallari["Günlük gecikme cezası"] = "1 TL"
for kural, deger in kutuphane_kurallari.items():
print(f"{kural}: {deger}")
## İade süresi: 14 gün
## Üyelik ücreti: Ücretsiz
## Günlük gecikme cezası: 1 TL
Sınıflar için de PEP 8 stil rehberine uymak önerilir:
SesliKitap,
Anlatici gibi. Örnek ve fonksiyon isimleri ise küçük harf
ve alt çizgi (snake_case) ile yazılır.class Kitap:
def __init__(self, baslik, yazar, sayfa_sayisi):
self.baslik = baslik
self.yazar = yazar
self.sayfa_sayisi = sayfa_sayisi
class Anlatici:
def __init__(self, isim, deneyim_yili=1):
self.isim = isim
self.deneyim_yili = deneyim_yili
class SesliKitap(Kitap):
"""Sesli kitaplara özgü nitelikleri temsil eder."""
def __init__(self, baslik, yazar, sayfa_sayisi):
"""Üst sınıfın niteliklerini başlatıp anlatıcıyı ekler."""
super().__init__(baslik, yazar, sayfa_sayisi)
self.anlatici = Anlatici("Naz Tekin", deneyim_yili=6)
def anlatici_bilgisi_goster(self):
"""Anlatıcının temel bilgilerini yazdırır."""
print(f"Anlatıcı: {self.anlatici.isim} ({self.anlatici.deneyim_yili} yıl deneyim)")
kayip_zaman_sesli = SesliKitap("kayıp zaman", "m. yıldız", 410)
kayip_zaman_sesli.anlatici_bilgisi_goster()
## Anlatıcı: Naz Tekin (6 yıl deneyim)
Bu bölümde şunları öğrendik:
__init__() metodu,
self parametresi, nitelikler ve metotlar.super().__init__(), alt sınıfa özgü
nitelik/metot ekleme, üst sınıf metotlarını geçersiz kılma.import
yöntemleriyle içeri aktarmak. Bunu yaparken, importun
çalışabilmesi için ilgili .py dosyasının gerçekten diskte
-üzerinde çalıştığınız dosyayla aynı klasörde- bulunması gerektiğini
gördük.random,
collections gibi modüllerden örnekler.Matthes, E. Python Crash Course: A Hands-On, Project-Based Introduction to Programming (Kapsamlı Python Kursu: Programlama Uygulama ve Proje Tabanlı Giriş). No Starch Press.