Python: Okunabilirliği ve Gücü Birleştiren Dil

Günümüzün hızla dijitalleşen dünyasında, programlama dilleri teknolojik yeniliklerin temelini oluşturuyor. Bu diller arasında, özellikle son yıllarda popülerliği katlanarak artan Python, okunabilirliği, basitliği ve çok yönlülüğü ile öne çıkıyor. Guido van Rossum tarafından 1990'ların başında geliştirilmeye başlanan Python, yüksek seviyeli, yorumlanan, genel amaçlı ve dinamik tipli bir dil olarak, başlangıç seviyesinden uzman seviyesine kadar geniş bir geliştirici kitlesine hitap ediyor. Temiz ve anlaşılır sözdizimi, onu öğrenmesi en kolay dillerden biri yaparken, devasa standart kütüphanesi ve zengin üçüncü parti paket ekosistemi sayesinde web geliştirmeden veri bilimine, yapay zekadan otomasyona kadar sayısız alanda güçlü çözümler sunuyor.

Python'un bu kadar sevilmesinin temel nedenlerinden biri "Python Felsefesi" veya "Zen of Python" olarak bilinen ilkeler bütünüdür. "Okunabilirlik önemlidir", "Basit, karmaşıktan iyidir", "Karmaşık, iç içe geçmişten iyidir" gibi ilkeler, Python kodunun sadece yazılmasını değil, aynı zamanda okunmasını ve bakımının yapılmasını da kolaylaştırmayı hedefler. Girintileme (indentation) kullanarak kod bloklarını tanımlama zorunluluğu, bu okunabilirliği pekiştiren önemli bir tasarım kararıdır. Dinamik tipleme özelliği, değişkenlerin tipini açıkça belirtme zorunluluğunu ortadan kaldırarak daha hızlı prototipleme imkanı sunar, ancak büyük projelerde dikkatli kullanılmayı gerektirir.

Python'un kullanım alanları oldukça geniştir. Django ve Flask gibi güçlü framework'ler sayesinde modern ve ölçeklenebilir web uygulamaları geliştirmek mümkündür. Veri bilimi ve makine öğrenmesi alanında ise Pandas, NumPy, Scikit-learn, TensorFlow ve PyTorch gibi kütüphanelerle adeta bir standart haline gelmiştir. Sistem yönetimi görevlerini otomatikleştirmek, bilimsel hesaplamalar yapmak, masaüstü uygulamaları oluşturmak (PyQt, Kivy ile) veya oyun geliştirmek (Pygame ile) Python ile yapılabileceklerden sadece birkaçıdır. Bu rehber, Python'un temel sözdiziminden başlayarak veri yapılarına, fonksiyonlara, nesne yönelimli programlamaya, modüllere ve popüler kullanım alanlarına kadar uzanan geniş bir yelpazede bilgi sunarak, bu güçlü ve keyifli dili öğrenme yolculuğunuzda size sağlam bir temel ve yol haritası sağlamayı amaçlamaktadır.

Python Temelleri: İlk Adımlar

Python dünyasına giriş yapmak için dilin temel kurallarını, kurulumunu ve basit işlemleri nasıl yapacağınızı öğrenmekle başlayabiliriz.

Kurulum ve Ortam Hazırlığı

Python kullanmaya başlamak için bilgisayarınızda yüklü olması gerekir.

  • Python İndirme: Resmi Python web sitesi (python.org) üzerinden işletim sisteminize uygun (Windows, macOS, Linux) en son kararlı sürümü indirebilirsiniz. Kurulum sırasında "Add Python to PATH" (Python'ı PATH'e ekle) seçeneğini işaretlemek, komut satırından Python'a kolayca erişmenizi sağlar.
  • Kurulumu Doğrulama: Komut istemcisini (Terminal veya Command Prompt) açıp python --version veya python3 --version komutunu çalıştırarak Python'un başarıyla kurulup kurulmadığını ve sürümünü kontrol edebilirsiniz.
  • Pip (Paket Yöneticisi): Python ile birlikte gelen standart paket yöneticisidir. Üçüncü parti kütüphaneleri (paketleri) indirmek ve kurmak için kullanılır (örn: pip install requests). pip --version ile kontrol edilebilir ve pip install --upgrade pip ile güncellenebilir.
  • Sanal Ortamlar (Virtual Environments - venv): Farklı projelerin farklı kütüphane bağımlılıklarına sahip olması yaygındır. Bu bağımlılıkların birbirine karışmasını önlemek ve proje bazında izole ortamlar oluşturmak için sanal ortamlar kullanılır. Python 3 ile birlikte gelen venv modülü bunun için standart bir yoldur.
    1. Ortam Oluşturma: Proje klasörünüzde python -m venv env_adi (örn: python -m venv .venv) komutuyla bir sanal ortam oluşturulur.
    2. Aktifleştirme:
      • Windows (cmd.exe): .\env_adi\Scripts\activate.bat
      • Windows (PowerShell): .\env_adi\Scripts\Activate.ps1 (ExecutionPolicy ayarı gerekebilir)
      • macOS/Linux (bash/zsh): source env_adi/bin/activate
      Aktifleştirildiğinde, komut satırı başında genellikle (env_adi) ifadesi görünür ve bu ortama kurulan paketler sadece bu proje için geçerli olur.
    3. Devre Dışı Bırakma: deactivate komutu kullanılır.
    Her yeni Python projesine bir sanal ortamla başlamak iyi bir pratiktir.

Temel Sözdizimi, "Merhaba Dünya" ve Yorumlar

Python'un sözdizimi okunabilirliğe odaklanır. En önemli özelliklerinden biri, kod bloklarını (if, for, fonksiyon gövdeleri vb.) tanımlamak için süslü parantezler yerine girintileme (indentation) kullanmasıdır. Genellikle 4 boşluk kullanılır ve tutarlılık çok önemlidir.

Klasik "Merhaba Dünya" örneği:


print("Merhaba Dünya!")
                    

Bu kadar basit! print() fonksiyonu, verilen değeri konsola yazdırır.

Yorumlar: Tek satır yorumları diyez (#) işareti ile başlar ve satır sonuna kadar devam eder. Python'da C# veya Java'daki gibi çok satırlı yorum bloğu (/* ... */) yoktur, ancak çok satırlı string literalleri (üç tırnak ile """ ... """ veya ''' ... ''') bazen bu amaçla kullanılsa da, asıl amaçları docstring oluşturmaktır.


# Bu tek satırlık bir yorumdur.
print("Bu kod çalışacak") # Bu da satır sonu yorumu

"""
Bu çok satırlı bir string literalidir.
Genellikle fonksiyon veya sınıf başında açıklama (docstring)
olarak kullanılır, ancak bazen yorum bloğu gibi de
kullanıldığı görülebilir.
"""
print("Yorumlardan sonra")
                    

Değişkenler ve Atama (Dinamik Tipleme)

Değişkenler, değerleri saklamak için kullanılan isimlendirilmiş yer tutuculardır. Python'da değişken tanımlarken tür belirtmeye gerek yoktur (dinamik tipleme). Değişkenin tipi, ona atanan değere göre otomatik olarak belirlenir.

Atama işlemi = operatörü ile yapılır.


mesaj = "Merhaba Python" # mesaj değişkeni şimdi bir string
sayi = 10              # sayi değişkeni şimdi bir integer
pi_degeri = 3.14       # pi_degeri şimdi bir float
aktif_mi = True          # aktif_mi değişkeni şimdi bir boolean

print(mesaj)
print(sayi)

# Değişkenin tipi sonradan değiştirilebilir (dikkatli kullanılmalı!)
sayi = "Artık metin"
print(sayi)

# Aynı anda birden fazla değişkene atama
x, y, z = 1, "iki", False
print(x, y, z)

# Değişken isimlendirme kuralları:
# - Harf veya alt çizgi (_) ile başlamalıdır.
# - Harf, sayı veya alt çizgi içerebilir.
# - Büyük/küçük harfe duyarlıdır (yas ile Yas farklıdır).
# - Python anahtar kelimeleri (if, for, while, class vb.) kullanılamaz.
# - Okunabilirlik için genellikle küçük harf ve kelimeler arası alt çizgi (snake_case) kullanılır.
kullanici_adi = "testuser"
ilk_sayi = 5
                    

Dinamik tipleme esneklik sağlasa da, değişkenin hangi türde veri tuttuğunu takip etmek önemlidir.

Temel Veri Tipleri

Python'da yerleşik olarak bulunan temel veri tipleri şunlardır:

  • int (Integer): Tam sayıları temsil eder (pozitif, negatif veya sıfır). Boyut sınırı pratikte yoktur (bellek yettiği sürece).
    yas = 30; sicaklik = -5; sayac = 0
  • float (Floating Point Number): Ondalıklı sayıları temsil eder.
    pi = 3.14; fiyat = 99.99; oran = 0.5
  • bool (Boolean): Mantıksal değerleri temsil eder: True veya False (Baş harfleri büyük!).
    giris_yapildi = True; hata_var = False
  • str (String): Metin dizilerini temsil eder. Tek (' '), çift (" ") veya üç tırnak (''' ''' veya """ """ - çok satırlı stringler için) içine alınır. String'ler değiştirilemez (immutable).
    
    isim = "Abdulkadir"
    mesaj = 'Python öğreniyorum.'
    siir = """
    Bu bir
    çok satırlı
    şiirdir.
    """
                                
  • NoneType: Özel bir tiptir ve sadece tek bir değere sahiptir: None. Bir değerin olmadığını veya boş olduğunu belirtmek için kullanılır (diğer dillerdeki null'a benzer).
    sonuc = None

Bir değişkenin tipini öğrenmek için type() fonksiyonu kullanılabilir:


print(type(isim))      # <class 'str'>
print(type(yas))       # <class 'int'>
print(type(pi))        # <class 'float'>
print(type(aktif_mi)) # <class 'bool'>
print(type(sonuc))     # <class 'NoneType'>
                    

Tip Dönüşümleri

Farklı veri tipleri arasında dönüşüm yapmak için yerleşik fonksiyonlar kullanılır:

  • int(deger): String veya float'ı integer'a çevirir (float'ın ondalık kısmı atılır).
  • float(deger): String veya integer'ı float'a çevirir.
  • str(deger): Herhangi bir tipi string'e çevirir.
  • bool(deger): Bir değeri boolean karşılığına çevirir. Genellikle 0, None, boş string (""), boş liste ([]), boş sözlük ({}) vb. False; diğer değerler True olur.

sayi_str = "123"
sayi_int = int(sayi_str)
print(sayi_int + 5) # 128

ondalik_str = "99.5"
ondalik_float = float(ondalik_str)
print(ondalik_float / 2) # 49.75

rakam = 42
rakam_str = str(rakam)
print("Rakam: " + rakam_str) # "Rakam: 42"

print(bool(0))    # False
print(bool(10))   # True
print(bool(""))   # False
print(bool("abc"))# True
print(bool(None)) # False
print(bool([]))   # False
                    

Dönüşüm her zaman mümkün olmayabilir (örn: int("abc") ValueError verir).

Operatörler

Değerler üzerinde işlem yapmak için kullanılır:

  • Aritmetik Operatörler: +, -, *, / (gerçek bölme - sonuç float), // (tam sayı bölmesi - taban bölme), % (modülüs - kalan), ** (üs alma).
  • Atama Operatörleri: =, +=, -=, *=, /=, //=, %=, **=.
  • Karşılaştırma Operatörleri: == (eşit), != (eşit değil), <, >, <=, >=. Sonuç True veya False döner.
  • Mantıksal Operatörler: and (VE), or (VEYA), not (DEĞİL). Boolean değerler üzerinde çalışır.
  • Üyelik Operatörleri: Bir değerin bir dizi, liste, tuple, set veya string gibi bir koleksiyon içinde olup olmadığını kontrol eder.
    • in: Eleman koleksiyonda varsa True döner.
    • not in: Eleman koleksiyonda yoksa True döner.
  • Kimlik Operatörleri: İki değişkenin bellekteki aynı nesneyi işaret edip etmediğini kontrol eder.
    • is: İki değişken aynı nesneyse True döner.
    • is not: İki değişken farklı nesnelerse True döner.
    • Not: == değer eşitliğini kontrol ederken, is kimlik (bellek adresi) eşitliğini kontrol eder. Küçük sayılar ve string'ler için Python optimizasyon yapabilir ve is bazen beklenmedik şekilde True dönebilir, ancak genel olarak nesne kimliği için kullanılır.

# Aritmetik
print(10 / 3)   # 3.333...
print(10 // 3)  # 3
print(10 % 3)   # 1
print(2 ** 4)   # 16

# Karşılaştırma
print(5 == 5.0) # True (değerler eşit)
print(5 != '5') # True (tipler farklı)

# Mantıksal
x = 10
print(x > 5 and x < 20) # True and True => True
print(x < 0 or x == 10)   # False or True => True
print(not (x == 10))     # not True => False

# Üyelik
liste = [1, 2, 3, 4]
print(3 in liste)      # True
print(5 not in liste) # True
print('a' in 'banana') # True

# Kimlik
a = [1, 2]
b = [1, 2]
c = a
print(a == b) # True (içerikleri aynı)
print(a is b) # False (bellekte farklı nesneler)
print(a is c) # True (aynı nesneyi işaret ediyorlar)
                      

Girdi ve Çıktı (input(), print(), f-string)

  • input(prompt): Kullanıcıdan veri almak için kullanılır. prompt (isteğe bağlı) kullanıcıya gösterilecek mesajdır. Kullanıcının girdiği değer her zaman string olarak döndürülür. Sayısal işlem yapılacaksa tip dönüşümü (int(), float()) gereklidir.
  • print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False): Değerleri konsola veya belirtilen dosyaya yazdırır.
    • *objects: Yazdırılacak bir veya daha fazla değer (virgülle ayrılır).
    • sep: Değerler arasına konulacak ayırıcı (varsayılanı boşluk).
    • end: Yazdırma işlemi sonuna eklenecek karakter (varsayılanı yeni satır \n).
    • file: Yazdırılacak yer (varsayılanı standart çıktı - konsol).
    • flush: Çıktı tamponunun hemen boşaltılıp boşaltılmayacağı.
  • f-string (Formatted String Literals - Python 3.6+): String içine değişken veya ifadeleri kolayca yerleştirmenin modern ve okunabilir yoludur. String'in başına f veya F harfi konur ve değişkenler/ifadeler süslü parantez {} içine yazılır. Biçimlendirme seçenekleri de eklenebilir.

# Kullanıcıdan girdi alma
isim = input("Adınızı girin: ")
yas_str = input("Yaşınızı girin: ")

# Tip dönüşümü
try:
    yas = int(yas_str)
except ValueError:
    yas = 0
    print("Geçersiz yaş girdiniz!")

# Çıktı yazdırma
print("Merhaba", isim, "! Yaşınız:", yas) # Virgülle ayırarak (aralara boşluk koyar)
print("Merhaba " + isim + "! Yaşınız: " + str(yas)) # String birleştirme ile

# f-string kullanımı (Önerilen)
print(f"Merhaba {isim}! Gelecek yıl {yas + 1} yaşında olacaksınız.")

# f-string ile biçimlendirme
fiyat = 123.4567
print(f"Ürün Fiyatı: {fiyat:.2f} TL") # Ondalık kısmı 2 basamakla sınırla

# print parametreleri
print("Elma", "Armut", "Muz", sep=", ", end=".\n") # Çıktı: Elma, Armut, Muz.
                      

Kontrol Yapıları: Program Akışını Yönetme

Kontrol yapıları, kodun hangi koşullarda veya kaç kez çalışacağını belirleyerek programın akışını yönlendirir.

Koşullu İfadeler (if, elif, else)

Belirli bir koşulun doğru olup olmadığını kontrol eder ve buna göre farklı kod bloklarını çalıştırır.

  • if: Koşul doğruysa (True ise) altındaki girintili blok çalışır.
  • elif (else if): Önceki if veya elif koşulları yanlışsa ve kendi koşulu doğruysa çalışır. Birden fazla elif olabilir.
  • else: Yukarıdaki hiçbir if veya elif koşulu doğru değilse çalışır. İsteğe bağlıdır ve en sonda yer alır.

Koşullar genellikle karşılaştırma ve mantıksal operatörler kullanılarak oluşturulur.


puan = int(input("Notunuzu girin (0-100): "))

if puan >= 90 and puan <= 100:
    harf_notu = "A"
    print("Harika!")
elif puan >= 80:
    harf_notu = "B"
elif puan >= 70:
    harf_notu = "C"
elif puan >= 60:
    harf_notu = "D"
elif puan >= 0:
    harf_notu = "F"
    print("Daha çok çalışmalısın.")
else:
    harf_notu = "Geçersiz"
    print("Lütfen 0-100 arası bir değer girin.")

if harf_notu != "Geçersiz":
    print(f"Harf notunuz: {harf_notu}")

# Tek satır if (Ternary benzeri ama tam değil)
durum = "Geçti" if puan >= 60 else "Kaldı"
if harf_notu != "Geçersiz":
    print(f"Durum: {durum}")
                     

Unutmayın, Python'da bloklar girintileme ile belirlenir.

Döngüler (for, while)

Belirli kod bloklarını tekrar tekrar çalıştırmak için kullanılır.

  • for Döngüsü: Bir koleksiyon (liste, tuple, string, set, dictionary keys/values/items) veya yinelenebilir (iterable) başka bir nesne üzerindeki her bir eleman için döner.
    • range(start, stop, step) Fonksiyonu: Belirli bir aralıkta sayılar üretmek için sıkça for ile kullanılır. start dahil, stop hariçtir. start verilmezse 0'dan başlar, step verilmezse 1 artar.
    
    meyveler = ["elma", "armut", "muz"]
    print("Meyveler:")
    for meyve in meyveler:
        print(f"- {meyve}")
    
    print("\nSayılar (0-4):")
    for i in range(5): # 0, 1, 2, 3, 4
        print(i, end=" ") # 0 1 2 3 4 .
    print()
    
    print("\nSayılar (2-8, 2şer):")
    for i in range(2, 9, 2): # 2, 4, 6, 8
        print(i, end=" ") # 2 4 6 8 .
    print()
    
    metin = "Python"
    for harf in metin:
        print(harf)
    
    # Sözlük üzerinde döngü
    urun_fiyatlari = {"kalem": 5, "defter": 15, "silgi": 3}
    print("\nÜrünler:")
    for urun in urun_fiyatlari: # Sadece anahtarlar üzerinde döner
        print(urun)
    print("\nÜrün ve Fiyatlar:")
    for urun, fiyat in urun_fiyatlari.items(): # items() ile anahtar ve değer alınır
        print(f"{urun}: {fiyat} TL")
                                 
  • while Döngüsü: Belirtilen koşul True olduğu sürece dönmeye devam eder. Koşulun döngü içinde bir noktada False olacak şekilde güncellenmesi gerekir, aksi takdirde sonsuz döngü oluşabilir.
    
    sayac = 0
    while sayac < 5:
        print(f"While sayaç: {sayac}")
        sayac += 1 # Koşulu değiştiren güncelleme
    
    print("While döngüsü bitti.")
    
    # Kullanıcı belirli bir girdi yapana kadar dönme
    # sifre = ""
    # while sifre != "12345":
    #     sifre = input("Şifreyi girin: ")
    # print("Şifre doğru!")
                                 

Döngü Kontrol İfadeleri (break, continue, pass)

  • break: İçinde bulunduğu en yakın döngüyü (for veya while) hemen sonlandırır. Döngünün geri kalan adımları ve varsa else bloğu çalıştırılmaz.
  • continue: Döngünün mevcut adımını atlar ve bir sonraki adıma geçer. Döngü sonlanmaz.
  • pass: Hiçbir şey yapmayan bir ifadedir. Sözdizimsel olarak bir blok gerektiği (örn: if, for, fonksiyon tanımı) ancak mantıksal olarak hiçbir işlem yapılmayacağı durumlarda yer tutucu olarak kullanılır.
  • Döngülerle else Bloğu: Python'da for ve while döngülerinden sonra bir else bloğu kullanılabilir. Bu blok, döngü normal bir şekilde tamamlanırsa (yani bir break ifadesi ile çıkılmazsa) çalıştırılır.

print("Break Örneği:")
for i in range(10):
    if i == 5:
        print("5 bulundu, döngüden çıkılıyor.")
        break # Döngüyü sonlandır
    print(i, end=" ") # 0 1 2 3 4
else:
    print("Döngü normal bitti (break olmadı).") # Bu satır çalışmaz
print("\nDöngü sonrası")

print("\nContinue Örneği:")
for i in range(10):
    if i % 2 == 0: # Eğer sayı çiftse
        continue # Bu adımı atla, sonraki adıma geç
    print(i, end=" ") # 1 3 5 7 9 (Sadece tek sayılar yazılır)
else:
    print("\nDöngü normal bitti (break olmadı).") # Bu satır çalışır
print("\nDöngü sonrası")

print("\nPass Örneği:")
x = 150 # x'e bir değer atayalım
for i in range(3):
    if i == 1:
        pass # Henüz ne yapacağıma karar vermedim ama blok boş kalamaz
        print("(i=1 için pass geçildi)")
    else:
        print(i)

if x > 100:
    pass # Şimdilik bir şey yapma
else:
    print("x 100'den küçük veya eşit")
                       

Python Veri Yapıları: Koleksiyonlar

Python, birden fazla değeri bir arada tutmak ve yönetmek için güçlü ve esnek yerleşik veri yapıları (koleksiyonlar) sunar: Listeler, Demetler, Sözlükler ve Kümeler.

Listeler (Lists)

Sıralı, değiştirilebilir (mutable) ve farklı veri tiplerinden elemanlar içerebilen bir koleksiyondur. Köşeli parantezler [ ] ile oluşturulur.

  • Oluşturma: liste = [1, "iki", 3.0, True]
  • Erişim (Indexing): İlk eleman 0. index'tedir. Negatif indexler sondan başlar (-1 son eleman). liste[0], liste[-1].
  • Dilimleme (Slicing): liste[start:stop:step] ile alt listeler oluşturulur. start dahil, stop hariçtir. Orijinal listeyi değiştirmez. liste[1:3] (1. ve 2. index), liste[:2] (baştan 2. indexe kadar), liste[1:] (1. indexten sona kadar), liste[:] (tam kopya).
  • Değiştirme: liste[index] = yeni_deger
  • Eleman Ekleme:
    • append(eleman): Listenin sonuna ekler.
    • insert(index, eleman): Belirtilen index'e ekler.
    • extend(baska_liste): Başka bir listenin elemanlarını sona ekler.
  • Eleman Silme:
    • remove(deger): Belirtilen değere sahip ilk elemanı siler.
    • pop(index=-1): Belirtilen index'teki (varsayılan son) elemanı siler ve döndürür.
    • del liste[index]: Belirtilen index'teki elemanı siler.
    • clear(): Listenin tüm içeriğini siler.
  • Diğer Metotlar: index(deger), count(deger), sort() (yerinde sıralar), reverse() (yerinde ters çevirir), copy() (yüzeysel kopya oluşturur).
  • Operatörler: + (birleştirme), * (tekrarlama).
  • len(liste): Eleman sayısını verir.

rakamlar = [1, 5, 2, 8, 2]
print(f"Liste: {rakamlar}")
print(f"İlk eleman: {rakamlar[0]}")        # 1
print(f"Son eleman: {rakamlar[-1]}")       # 2
print(f"Dilim [1:4]: {rakamlar[1:4]}")    # [5, 2, 8]

rakamlar[1] = 99 # Değiştirme
print(f"Değiştirilmiş: {rakamlar}")       # [1, 99, 2, 8, 2]

rakamlar.append(10)
print(f"Append(10): {rakamlar}")         # [1, 99, 2, 8, 2, 10]
rakamlar.insert(2, 55)
print(f"Insert(2, 55): {rakamlar}")      # [1, 99, 55, 2, 8, 2, 10]

rakamlar.remove(2) # İlk bulunan 2'yi siler
print(f"Remove(2): {rakamlar}")          # [1, 99, 55, 8, 2, 10]
son_eleman = rakamlar.pop()
print(f"Pop(): {son_eleman}, Liste: {rakamlar}") # 10, [1, 99, 55, 8, 2]
del rakamlar[0]
print(f"del [0]: {rakamlar}")            # [99, 55, 8, 2]

rakamlar.sort() # Yerinde sıralar
print(f"Sort(): {rakamlar}")             # [2, 8, 55, 99]
rakamlar.reverse() # Yerinde ters çevirir
print(f"Reverse(): {rakamlar}")          # [99, 55, 8, 2]

print(f"Eleman sayısı: {len(rakamlar)}") # 4
                      

Demetler (Tuples)

Listelere benzer şekilde sıralı koleksiyonlardır ancak değiştirilemezler (immutable). Yani bir demet oluşturulduktan sonra elemanları eklenemez, silinemez veya değiştirilemez. Normal parantezler ( ) ile oluşturulurlar.

  • Oluşturma: demet = (1, "iki", 3.0). Tek elemanlı demet oluştururken sondaki virgül önemlidir: tek_elemanli = (5,).
  • Erişim ve Dilimleme: Listelerle aynıdır. demet[0], demet[-1], demet[1:3].
  • Neden Kullanılır?
    • Verilerin yanlışlıkla değiştirilmesini önlemek için (güvenlik).
    • Sözlük anahtarı olarak kullanılabilirler (listeler kullanılamaz çünkü değiştirilebilirler).
    • Listelere göre genellikle biraz daha hızlıdırlar (nadiren önemli bir fark yaratır).
    • Fonksiyonlardan birden fazla değer döndürmek için sıkça kullanılırlar (yapı bozma ile kolayca alınabilir).
  • Metotlar: Sadece count() ve index() gibi içeriği değiştirmeyen metotları vardır.

koordinatlar = (10.5, 25.0, 100.0)
print(f"Koordinatlar: {koordinatlar}")
print(f"X: {koordinatlar[0]}")

# koordinatlar[0] = 11.0 # Hata! TypeError: 'tuple' object does not support item assignment

# Tek elemanlı tuple
tek = ("yalnız",)
print(type(tek)) # <class 'tuple'>

# Demet üzerinde döngü
for k in koordinatlar:
    print(k)

# Fonksiyondan birden fazla değer döndürme
def min_max_bul(liste):
    return min(liste), max(liste) # Otomatik olarak tuple döndürür

sayilar = [4, 1, 9, -2, 5]
en_kucuk, en_buyuk = min_max_bul(sayilar) # Yapı bozma ile alma
print(f"En Küçük: {en_kucuk}, En Büyük: {en_buyuk}")
                      

Sözlükler (Dictionaries / dict)

Sırasız (Python 3.7+ sürümlerinde eklenme sırasını korur), değiştirilebilir ve anahtar-değer (key-value) çiftlerinden oluşan bir koleksiyondur. Anahtarlar benzersiz ve değiştirilemez tipte (genellikle string veya tuple) olmalıdır. Değerler ise herhangi bir tipte olabilir. Süslü parantezler { } ile ve anahtar: deger formatıyla oluşturulur.

  • Oluşturma: sozluk = {"ad": "Ali", "yas": 30, "sehir": "İstanbul"} veya sozluk = dict(ad="Ali", yas=30).
  • Erişim: Anahtarlar kullanılarak erişilir: sozluk["ad"]. Anahtar yoksa KeyError verir.
  • get(anahtar, varsayilan=None) Metodu: Anahtarla değer alır, anahtar yoksa hata vermek yerine varsayilan değeri (varsayılanı None) döndürür.
  • Ekleme/Güncelleme: sozluk[yeni_anahtar] = yeni_deger veya sozluk.update({anahtar1: deger1, anahtar2: deger2}).
  • Silme:
    • pop(anahtar, varsayilan=HATA): Anahtara ait değeri siler ve döndürür. Anahtar yoksa ve varsayılan değer verilmemişse KeyError verir.
    • del sozluk[anahtar]: Anahtar-değer çiftini siler. Anahtar yoksa KeyError verir.
    • popitem(): Son eklenen anahtar-değer çiftini (Python 3.7+) siler ve tuple olarak döndürür.
    • clear(): Sözlüğün içeriğini temizler.
  • Anahtarları, Değerleri, Öğeleri Alma:
    • keys(): Anahtarların bir görünümünü (view) döndürür.
    • values(): Değerlerin bir görünümünü döndürür.
    • items(): Anahtar-değer çiftlerinin (tuple olarak) bir görünümünü döndürür. Bu görünümler genellikle döngülerde kullanılır.
  • len(sozluk): Anahtar-değer çifti sayısını verir.

ogrenci = {
    "ad": "Zeynep",
    "numara": 123,
    "bolum": "Bilgisayar Müh.",
    "notlar": [85, 90, 78]
}

print(f"Öğrenci Adı: {ogrenci['ad']}")
print(f"Öğrenci Bölümü: {ogrenci.get('bolum')}")
print(f"Öğrenci Mezun Mu: {ogrenci.get('mezun_mu', False)}") # Anahtar yok, varsayılan False döner

# Güncelleme/Ekleme
ogrenci["numara"] = 456
ogrenci["sehir"] = "Ankara" # Yeni anahtar-değer ekleme
print(f"Güncellenmiş Öğrenci: {ogrenci}")

# Silme
cikarilan_notlar = ogrenci.pop("notlar")
print(f"Çıkarılan Notlar: {cikarilan_notlar}, Sözlük: {ogrenci}")
del ogrenci["sehir"]
print(f"Şehir Silindi: {ogrenci}")

# Döngü ile gezinme
print("\nAnahtarlar:")
for anahtar in ogrenci.keys():
    print(anahtar)

print("\nDeğerler:")
for deger in ogrenci.values():
    print(deger)

print("\nAnahtar-Değer Çiftleri:")
for key, value in ogrenci.items():
    print(f"{key} -> {value}")

print(f"Eleman sayısı: {len(ogrenci)}")
                      

Kümeler (Sets)

Sırasız, değiştirilebilir ve benzersiz elemanlardan oluşan bir koleksiyondur. Matematikteki küme kavramına benzer. Süslü parantezler { } ile veya set() fonksiyonu ile oluşturulur. Boş küme oluşturmak için {} yerine set() kullanılmalıdır ({} boş sözlük oluşturur).

  • Oluşturma: kume = {1, 2, 3, "a", "b", 2} (Sonuç: {1, 2, 3, 'a', 'b'} - tekrarlar otomatik atılır). kume = set([1, 2, 2, 3]).
  • Eleman Ekleme: add(eleman).
  • Eleman Silme:
    • remove(eleman): Elemanı siler, eleman yoksa KeyError verir.
    • discard(eleman): Elemanı siler, eleman yoksa hata vermez.
    • pop(): Kümeden rastgele (aslında genellikle belirli bir sıraya göre ama garanti değil) bir elemanı siler ve döndürür.
    • clear(): Kümeyi boşaltır.
  • Küme İşlemleri:
    • Birleşim: kume1 | kume2 veya kume1.union(kume2)
    • Kesişim: kume1 & kume2 veya kume1.intersection(kume2)
    • Fark: kume1 - kume2 veya kume1.difference(kume2) (kume1'de olup kume2'de olmayanlar)
    • Simetrik Fark: kume1 ^ kume2 veya kume1.symmetric_difference(kume2) (sadece birinde olanlar)
  • Diğer Metotlar: issubset() (alt küme mi?), issuperset() (üst küme mi?), isdisjoint() (kesişim boş mu?).
  • Üyelik Kontrolü (in): Kümelerde eleman arama (eleman in kume) listelere göre çok daha hızlıdır.

renkler1 = {"kırmızı", "yeşil", "mavi"}
renkler2 = set(["yeşil", "sarı", "kırmızı"]) # Listeden set oluşturma

print(f"Renkler 1: {renkler1}")
print(f"Renkler 2: {renkler2}")

renkler1.add("mor")
renkler2.discard("turuncu") # Hata vermez
# renkler2.remove("turuncu") # KeyError verir
print(f"Add/Discard sonrası: {renkler1}, {renkler2}")

# Küme İşlemleri
birlesim = renkler1 | renkler2
kesisim = renkler1 & renkler2
fark1 = renkler1 - renkler2
fark2 = renkler2 - renkler1
simetrik_fark = renkler1 ^ renkler2

print(f"Birleşim: {birlesim}")
print(f"Kesişim: {kesisim}")
print(f"Fark (Renkler1 - Renkler2): {fark1}")
print(f"Fark (Renkler2 - Renkler1): {fark2}")
print(f"Simetrik Fark: {simetrik_fark}")

print(f"'mavi' renkler1'de var mı? {'mavi' in renkler1}") # True

# Tekrarlanan elemanları listeden kaldırma
sayilar = [1, 2, 5, 2, 3, 5, 1, 4, 4]
benzersiz_sayilar = list(set(sayilar)) # Önce sete çevirip sonra listeye çevir
print(f"Benzersiz Sayılar: {benzersiz_sayilar}") # [1, 2, 3, 4, 5] (sıra değişebilir)
                      

Fonksiyonlar: Kodun Yapı Taşları

Fonksiyonlar, belirli bir görevi yerine getiren, tekrar tekrar çağrılabilen, isimlendirilmiş kod bloklarıdır. Python'da fonksiyonlar birinci sınıf nesnelerdir, yani değişkenlere atanabilir, başka fonksiyonlara argüman olarak geçilebilir ve fonksiyonlardan değer olarak döndürülebilirler.

Fonksiyon Tanımlama ve Çağırma (def, return)

Fonksiyonlar def anahtar kelimesi ile tanımlanır, ardından fonksiyon adı, parantez içinde parametre listesi ve iki nokta üst üste (:) gelir. Fonksiyon gövdesi girintili olmalıdır.

return ifadesi, fonksiyondan bir değer döndürmek ve fonksiyonun çalışmasını sonlandırmak için kullanılır. Eğer return kullanılmazsa veya return'den sonra bir değer belirtilmezse, fonksiyon varsayılan olarak None döndürür.


# Parametresiz, değer döndürmeyen fonksiyon
def selamla():
    """Bu fonksiyon basit bir selamlama mesajı yazdırır.""" # Docstring
    print("Merhaba!")

# Parametreli, değer döndüren fonksiyon
def topla(sayi1, sayi2):
    """Verilen iki sayıyı toplar ve sonucu döndürür."""
    sonuc = sayi1 + sayi2
    return sonuc

# Fonksiyonları çağırma
selamla() # Çıktı: Merhaba!

toplam_sonucu = topla(15, 7)
print(f"Toplama Sonucu: {toplam_sonucu}") # Çıktı: Toplama Sonucu: 22

def kontrol_et(deger):
    if deger > 10:
        return "Büyük"
    # Eğer return'e girmezse None döner
    print("Kontrol devam ediyor...") # Bu satır deger <= 10 ise çalışır

print(kontrol_et(20)) # Çıktı: Büyük
print(kontrol_et(5))  # Çıktı: Kontrol devam ediyor... \n None
                     

Fonksiyon başlığının hemen altındaki üç tırnaklı string (docstring), fonksiyonun ne yaptığını açıklamak için kullanılır ve help(fonksiyon_adi) veya araçlar tarafından kullanılabilir.

Parametre Türleri ve Argümanlar

Fonksiyonlara veri aktarmanın farklı yolları vardır:

  • Konumsal Argümanlar (Positional Arguments): Fonksiyon çağrılırken argümanlar, tanımda belirtilen parametre sırasına göre eşleştirilir.
  • Anahtar Kelimeli Argümanlar (Keyword Arguments): Fonksiyon çağrılırken parametre_adi=deger şeklinde argümanlar verilir. Sıra önemli değildir.
  • Varsayılan Parametre Değerleri: Fonksiyon tanımında parametreye parametre=varsayılan_deger şeklinde bir değer atanır. Fonksiyon çağrılırken o argüman verilmezse bu varsayılan değer kullanılır. Varsayılan değerli parametreler, konumsal parametrelerden sonra gelmelidir.
  • *args (Değişken Sayıda Konumsal Argüman): Fonksiyon tanımında parametre adının başına * konulur. Bu, fonksiyona geçirilen tüm "fazla" konumsal argümanları bir demet (tuple) içinde toplar.
  • **kwargs (Değişken Sayıda Anahtar Kelimeli Argüman): Fonksiyon tanımında parametre adının başına ** konulur. Bu, fonksiyona geçirilen tüm "fazla" anahtar kelimeli argümanları bir sözlük (dictionary) içinde toplar.

*args ve **kwargs genellikle birlikte kullanılır ve sırasıyla konumsal ve anahtar kelimeli parametrelerden sonra gelmelidir.


def bilgi_yazdir(ad, sehir, yas=30, ulke="Türkiye"): # yas ve ulke varsayılan değerli
    print(f"Ad: {ad}, Yaş: {yas}, Şehir: {sehir}, Ülke: {ulke}")

# Konumsal argümanlarla çağırma
bilgi_yazdir("Ahmet", "İstanbul")
# Çıktı: Ad: Ahmet, Yaş: 30, Şehir: İstanbul, Ülke: Türkiye

# Anahtar kelimeli argümanlarla çağırma (sıra farklı olabilir)
bilgi_yazdir(yas=25, ad="Ayşe", sehir="İzmir")
# Çıktı: Ad: Ayşe, Yaş: 25, Şehir: İzmir, Ülke: Türkiye

# Hem konumsal hem anahtar kelimeli (konumsallar önce gelmeli)
bilgi_yazdir("Mehmet", yas=40, sehir="Ankara")
# Çıktı: Ad: Mehmet, Yaş: 40, Şehir: Ankara, Ülke: Türkiye

def toplam_ve_ortalama(*sayilar): # *args
    if not sayilar: # Eğer hiç sayı gönderilmediyse
        return 0, 0.0
    toplam = sum(sayilar)
    ortalama = toplam / len(sayilar)
    return toplam, ortalama

t, o = toplam_ve_ortalama(10, 20, 30, 40)
print(f"Toplam: {t}, Ortalama: {o}") # Toplam: 100, Ortalama: 25.0

def ayar_goster(**ayarlar): # **kwargs
    print("\nAyar Detayları:")
    if not ayarlar:
        print("Ayar belirtilmemiş.")
        return
    for anahtar, deger in ayarlar.items():
        print(f"- {anahtar}: {deger}")

ayar_goster(tema="koyu", dil="tr", fontSize=14)
ayar_goster()

def genel_fonksiyon(zorunlu_param, *args, **kwargs):
    print(f"Zorunlu: {zorunlu_param}")
    print(f"Ek Konumsal (*args): {args}")
    print(f"Ek Anahtar Kelimeli (**kwargs): {kwargs}")

genel_fonksiyon(100, "ek1", "ek2", renk="mavi", boyut="L")
# Çıktı:
# Zorunlu: 100
# Ek Konumsal (*args): ('ek1', 'ek2')
# Ek Anahtar Kelimeli (**kwargs): {'renk': 'mavi', 'boyut': 'L'}
                      

Kapsam (Scope) ve LEGB Kuralı

Python'da bir değişkene erişilmeye çalışıldığında, yorumlayıcı değişkeni bulmak için belirli bir sıra izler. Bu sıraya LEGB kuralı denir:

  1. L (Local): Önce değişkenin içinde bulunulan mevcut fonksiyonda tanımlanıp tanımlanmadığına bakılır.
  2. E (Enclosing Function Locals): Eğer yerel kapsamda bulunamazsa, içinde bulunulan fonksiyonu kapsayan dış fonksiyonların kapsamlarına (eğer varsa, iç içe fonksiyonlar durumunda) bakılır.
  3. G (Global): Eğer kapsayan fonksiyonlarda da bulunamazsa, modül seviyesindeki (global) kapsama bakılır.
  4. B (Built-in): Eğer global kapsamda da bulunamazsa, Python'un yerleşik isimlerine (print, len, str gibi fonksiyonlar ve tipler) bakılır.

Eğer değişken hiçbir kapsamda bulunamazsa NameError hatası alınır.

Normalde fonksiyon içinden global bir değişkenin değeri okunabilir, ancak değiştirilmeye çalışılırsa, Python o isimde yeni bir yerel değişken oluşturur (eğer global değişkenle aynı isimde bir atama yapılırsa). Global bir değişkenin değerini fonksiyon içinden gerçekten değiştirmek için global anahtar kelimesi kullanılır. Benzer şekilde, kapsayan bir fonksiyondaki değişkeni değiştirmek için nonlocal anahtar kelimesi kullanılır.


x = "global x"

def dis_fonksiyon():
    y = "dış y"
    # x = "dış fonksiyon x" # Bu yeni bir yerel x oluşturur, global x'i değiştirmez

    def ic_fonksiyon():
        z = "iç z"
        # y = "iç fonksiyon y" # Bu yeni bir yerel y oluşturur, dış y'yi değiştirmez
        # nonlocal y # Bu satır eklenirse dıştaki y değiştirilir
        # y = "iç fonksiyon y (nonlocal)"

        # global x # Bu satır eklenirse global x değiştirilir
        # x = "iç fonksiyon x (global)"

        print(f"İç fonksiyon içi: x={x}, y={y}, z={z}") # x globalden, y dış fonksiyondan gelir

    ic_fonksiyon()
    print(f"Dış fonksiyon içi: x={x}, y={y}") # z'ye erişilemez

dis_fonksiyon()
print(f"Global alan: x={x}") # y ve z'ye erişilemez

# global ve nonlocal kullanılsaydı global x ve dış y'nin değeri değişirdi.
                      

Lambda Fonksiyonları

İsimsiz (anonim), tek bir ifade içeren ve bu ifadenin sonucunu döndüren küçük fonksiyonlar oluşturmak için kullanılır. lambda arguments: expression sözdizimine sahiptir.

Genellikle başka fonksiyonlara argüman olarak (örneğin sort, map, filter gibi) kısa bir işlem tanımlamak gerektiğinde kullanılırlar.


# İki sayıyı toplayan lambda
topla = lambda a, b: a + b
print(topla(5, 3)) # 8

# Tek argümanlı lambda
karesini_al = lambda x: x * x
print(karesini_al(6)) # 36

# map() ile kullanım: Listedeki her elemanın karesini alma
sayilar = [1, 2, 3, 4, 5]
kareler = list(map(lambda x: x * x, sayilar))
print(kareler) # [1, 4, 9, 16, 25]

# filter() ile kullanım: Listedeki çift sayıları filtreleme
cift_sayilar = list(filter(lambda x: x % 2 == 0, sayilar))
print(cift_sayilar) # [2, 4]

# sorted() ile kullanım: Listeyi elemanların ikinci harfine göre sıralama
isimler = ["Ali", "Zeynep", "Can", "Bora"]
sirali_isimler = sorted(isimler, key=lambda isim: isim[1]) # İkinci harfe göre sırala
print(sirali_isimler) # ['Can', 'Ali', 'Zeynep', 'Bora']
                     

Lambda fonksiyonları basit işlemler için kullanışlıdır, ancak karmaşık mantıklar için normal def ile tanımlanmış fonksiyonlar daha okunabilirdir.

Modüller ve Paketler: Kodu Organize Etme ve Yeniden Kullanma

Python'un gücünün önemli bir kısmı, kodun modüller ve paketler halinde organize edilerek yeniden kullanılabilmesinden gelir. Bu, büyük projelerin yönetimini kolaylaştırır ve başkaları tarafından yazılmış hazır çözümlerden (standart kütüphane ve üçüncü parti paketler) yararlanmayı sağlar.

Modül Nedir ve Nasıl Kullanılır?

Bir modül, Python tanımları ve ifadeleri içeren basit bir .py dosyasıdır. İçinde fonksiyonlar, sınıflar ve değişkenler barındırabilir. Başka bir Python script'inden bu modüldeki kodları kullanmak için import ifadesi kullanılır.

Farklı import yöntemleri:

  • import modul_adi: Tüm modülü içe aktarır. Modül içindeki nesnelere erişmek için modul_adi.nesne_adi kullanılır. Bu genellikle isim çakışmalarını önlediği için tercih edilir.
  • from modul_adi import nesne1, nesne2: Modülden sadece belirtilen nesneleri (fonksiyon, sınıf vb.) doğrudan mevcut isim alanına aktarır. Bu nesnelere doğrudan isimleriyle erişilebilir.
  • from modul_adi import *: Modüldeki tüm public isimleri (_ ile başlamayanları) mevcut isim alanına aktarır. İsim çakışmalarına yol açabileceği ve kodun okunabilirliğini azaltabileceği için genellikle önerilmez.
  • import modul_adi as takma_ad: Modülü farklı bir isimle (takma ad) içe aktarır. Özellikle uzun modül isimleri için kullanışlıdır.
  • from modul_adi import nesne as takma_ad: Modülden belirli bir nesneyi farklı bir isimle içe aktarır.

Örnek: hesaplamalar.py adında bir modülümüz olsun:


# hesaplamalar.py
PI = 3.14159

def topla(a, b):
    return a + b

def carp(a, b):
    return a * b

class HesapMakinesi:
    def bol(self, a, b):
        if b == 0:
            return "Sıfıra bölme hatası"
        return a / b
                     

Bu modülü başka bir dosyada kullanma (ana_script.py):


# ana_script.py

# Yöntem 1: Tüm modülü import etme
import hesaplamalar
print(hesaplamalar.PI)
print(hesaplamalar.topla(5, 3))
makine1 = hesaplamalar.HesapMakinesi()
print(makine1.bol(10, 2))

# Yöntem 2: Belirli nesneleri import etme
from hesaplamalar import PI, carp
print(PI) # Doğrudan erişim
print(carp(4, 6))

# Yöntem 3: Modüle takma ad verme
import hesaplamalar as hsp
print(hsp.PI)
makine2 = hsp.HesapMakinesi()
print(makine2.bol(9, 3))

# Yöntem 4: Nesneye takma ad verme
from hesaplamalar import HesapMakinesi as Calc
makine3 = Calc()
print(makine3.bol(12, 4))
                      

Python, modülleri bulmak için belirli bir arama yolu (sys.path) kullanır. Bu yol genellikle script'in bulunduğu dizini, PYTHONPATH çevre değişkeninde belirtilen dizinleri ve Python kurulum dizinindeki standart kütüphane yollarını içerir.

Python Standart Kütüphanesi

Python kurulumu ile birlikte gelen, çok çeşitli görevler için hazır modüller içeren zengin bir standart kütüphaneye sahiptir. Bu, harici paketlere ihtiyaç duymadan birçok temel işlevi yerine getirmenizi sağlar. Bazı önemli standart modüller:

  • math: Matematiksel fonksiyonlar (sqrt, sin, cos, log, pi, e vb.).
  • random: Rastgele sayı üretimi, liste karıştırma, rastgele seçim yapma (random, randint, choice, shuffle vb.).
  • datetime: Tarih ve saat işlemleri (datetime, date, time, timedelta sınıfları).
  • os: İşletim sistemi ile etkileşim (dosya/dizin işlemleri - listdir, mkdir, remove, çevre değişkenleri - environ, işlem yönetimi).
  • sys: Python yorumlayıcısı ile ilgili bilgilere ve fonksiyonlara erişim (komut satırı argümanları - argv, arama yolu - path, çıkış - exit).
  • json: JSON verilerini işleme (load, loads, dump, dumps).
  • csv: CSV dosyalarını okuma ve yazma.
  • re: Düzenli ifadeler (Regular Expressions) ile metin işleme.
  • collections: Ek veri yapıları sunar (defaultdict, Counter, deque, namedtuple).
  • urllib / requests (harici ama çok yaygın): HTTP istekleri yapma. (requests genellikle daha kullanıcı dostudur).
  • sqlite3: SQLite veritabanı ile çalışma.

Bu modülleri kullanmak için sadece import etmeniz yeterlidir.


import math
import random
from datetime import date

print(f"Pi sayısı: {math.pi}")
print(f"Karekök(16): {math.sqrt(16)}")

rastgele_sayi = random.randint(1, 100) # 1-100 arası rastgele tamsayı
print(f"Rastgele Sayı: {rastgele_sayi}")

bugun = date.today()
print(f"Bugünün tarihi: {bugun}")
print(f"Yıl: {bugun.year}")
                     

Paketler ve pip

Bir paket, modüllerin ve alt paketlerin hiyerarşik olarak organize edildiği bir dizin yapısıdır. Paketler, büyük kütüphaneleri veya uygulamaları mantıksal bileşenlere ayırmayı sağlar. Bir dizinin paket olarak tanınması için genellikle içinde (boş bile olsa) bir __init__.py dosyası bulunması gerekir (Python 3.3+ sürümlerinde bu dosya zorunlu olmasa da uyumluluk ve açıklık için hala yaygın olarak kullanılır).

Python'un standart kütüphanesi dışında kalan, topluluk tarafından geliştirilmiş milyonlarca paket Python Paket İndeksi'nde (PyPI - pypi.org) bulunur.

pip: Bu paketleri PyPI'dan indirmek, kurmak, güncellemek ve kaldırmak için kullanılan komut satırı aracıdır.

  • Paket Kurma: pip install paket_adi
  • Belirli Sürümü Kurma: pip install paket_adi==1.2.3
  • Paket Güncelleme: pip install --upgrade paket_adi
  • Paket Kaldırma: pip uninstall paket_adi
  • Kurulu Paketleri Listeleme: pip list
  • Paket Arama: pip search sorgu (Bu komut bazen kısıtlı çalışabilir, PyPI web sitesi daha güvenilirdir).
  • Bağımlılıkları Dosyadan Kurma: Proje bağımlılıkları genellikle requirements.txt adlı bir dosyada listelenir. Bu dosyadaki tüm paketleri kurmak için: pip install -r requirements.txt
  • Mevcut Ortamdaki Paketleri Dosyaya Yazma: pip freeze > requirements.txt

Sanal ortamlar kullanmak, farklı projelerin farklı paket sürümlerine olan bağımlılıklarını yönetmek için kritiktir.


# Komut Satırı Örnekleri (Terminal/CMD)

# requests paketini kurma
pip install requests

# numpy paketinin 1.20.0 sürümünü kurma
pip install numpy==1.20.0

# requests paketini güncelleme
pip install --upgrade requests

# kurulu paketleri listeleme
pip list

# mevcut ortamdaki paketleri requirements.txt'ye yazma
pip freeze > requirements.txt

# requirements.txt dosyasındaki paketleri kurma
pip install -r requirements.txt

# requests paketini kaldırma
pip uninstall requests
                     

Python'da Nesne Yönelimli Programlama (OOP)

Python, nesne yönelimli programlamayı tam olarak destekleyen bir dildir. OOP, kodu yeniden kullanılabilir nesneler etrafında modelleyerek daha organize, modüler ve yönetilebilir hale getirir.

Sınıflar (class) ve Nesneler

Daha önce bahsedildiği gibi, sınıf bir şablon, nesne ise o şablonun somut bir örneğidir.

  • Sınıf Tanımı: class SinifAdi: ile başlar. Genellikle sınıf isimleri BüyükHarfleBaşlar (CamelCase).
  • __init__(self, ...) Metodu (Constructor): Sınıftan bir nesne oluşturulduğunda (SinifAdi(...) çağrıldığında) otomatik olarak çalışan özel bir metottur. Genellikle nesnenin başlangıç niteliklerini (attributes) ayarlamak için kullanılır. İlk parametresi her zaman nesnenin kendisini temsil eden self'tir.
  • Nitelikler (Attributes): Nesnenin verileridir. Genellikle __init__ içinde self.nitelik_adi = deger şeklinde tanımlanır.
  • Metotlar (Methods): Sınıf içinde tanımlanan fonksiyonlardır. Nesnenin davranışlarını belirlerler. İlk parametreleri her zaman self olmalıdır (nesnenin kendi niteliklerine ve diğer metotlarına erişmek için).
  • Nesne Oluşturma (Instantiation): nesne_degiskeni = SinifAdi(argümanlar) şeklinde yapılır. Bu, __init__ metodunu çağırır.
  • Niteliklere ve Metotlara Erişim: Nokta (.) notasyonu ile yapılır: nesne_degiskeni.nitelik_adi, nesne_degiskeni.metot_adi(argümanlar).

class Araba:
    # Sınıf Niteliği (Tüm Araba nesneleri için ortak)
    tekerlek_sayisi = 4

    def __init__(self, marka, model, yil, renk="Bilinmiyor"):
        # Örnek Nitelikleri (Her nesne için ayrı)
        self.marka = marka
        self.model = model
        self.yil = yil
        self.renk = renk
        self.hiz = 0
        print(f"{self.yil} model {self.marka} {self.model} oluşturuldu.")

    # Örnek Metotları
    def hizlan(self, artis):
        self.hiz += artis
        print(f"Hız {self.hiz} km/s oldu.")

    def yavasla(self, azalis):
        self.hiz -= azalis
        if self.hiz < 0:
            self.hiz = 0
        print(f"Hız {self.hiz} km/s oldu.")

    def bilgi_goster(self):
        print(f"Marka: {self.marka}, Model: {self.model}, Yıl: {self.yil}, Renk: {self.renk}, Hız: {self.hiz}")
        print(f"Tekerlek Sayısı: {self.tekerlek_sayisi}") # Sınıf niteliğine erişim

# Nesneleri oluşturma
araba1 = Araba("Toyota", "Corolla", 2021, "Beyaz")
araba2 = Araba("Ford", "Focus", 2020)

# Metotları çağırma
araba1.hizlan(50)
araba2.hizlan(30)
araba1.yavasla(20)
araba1.bilgi_goster()
araba2.bilgi_goster()

# Niteliklere doğrudan erişim (genellikle önerilmez, kapsülleme tercih edilir)
# print(araba1.marka)
# araba1.hiz = 100 # Doğrudan değiştirme

# Sınıf niteliğine erişim
print(f"Araba Tekerlek Sayısı: {Araba.tekerlek_sayisi}")
                      

Kalıtım (Inheritance)

Bir sınıfın başka bir sınıfın özelliklerini ve metotlarını miras almasıdır.

  • Tanımlama: class AltSinif(UstSinif):
  • Alt sınıf, üst sınıfın tüm nitelik ve metotlarına (private olmayanlar - __ ile başlamayanlar) erişebilir.
  • Metot Geçersiz Kılma (Overriding): Alt sınıf, üst sınıftan miras aldığı bir metodu aynı isim ve parametrelerle yeniden tanımlayarak davranışını değiştirebilir.
  • super() Fonksiyonu: Alt sınıf içinden, üst sınıfın metotlarına (özellikle __init__ metoduna) erişmek için kullanılır.

class ElektrikliAraba(Araba): # Araba sınıfından kalıtım alır
    def __init__(self, marka, model, yil, batarya_kapasitesi, renk="Bilinmiyor"):
        # Üst sınıfın __init__ metodunu çağırarak temel nitelikleri ayarla
        super().__init__(marka, model, yil, renk)
        self.batarya_kapasitesi = batarya_kapasitesi # Yeni nitelik

    # Yeni metot
    def sarj_et(self):
        print(f"{self.marka} {self.model} şarj ediliyor ({self.batarya_kapasitesi} kWh)...")

    # Üst sınıftaki metodu override etme
    def bilgi_goster(self):
        super().bilgi_goster() # Önce üst sınıfın metodunu çağır
        print(f"Batarya Kapasitesi: {self.batarya_kapasitesi} kWh") # Ek bilgiyi yazdır

# Nesne oluşturma
elektrikli_arac = ElektrikliAraba("Tesla", "Model 3", 2022, 75, "Kırmızı")

elektrikli_arac.hizlan(80)
elektrikli_arac.sarj_et()
print("-" * 20)
elektrikli_arac.bilgi_goster() # Override edilmiş metot çalışır
                      

Kapsülleme ve Properties

Kapsülleme, nesnenin iç durumunu korumak ve dışarıdan doğrudan erişimi kısıtlamak anlamına gelir. Python'da bu genellikle isimlendirme kuralları ve property'ler ile sağlanır:

  • Tek Alt Çizgi (_): Bir nitelik veya metodun adının başına tek alt çizgi konulması (örn: self._gizli_veri), o üyenin sınıfın "iç kullanımı için" olduğunu ve dışarıdan doğrudan erişilmemesi gerektiğini belirten bir konvansiyondur (kuraldır). Python erişimi teknik olarak engellemez, ancak geliştiricilere bir uyarıdır.
  • Çift Alt Çizgi (__): Bir nitelik veya metodun adının başına çift alt çizgi konulması (örn: self.__cok_gizli), Python'un "isim mangling" (isim bozma) yapmasına neden olur. Bu, nitelik adını çalışma zamanında _SinifAdi__cok_gizli şeklinde değiştirerek alt sınıflarda yanlışlıkla override edilmesini veya dışarıdan doğrudan erişilmesini zorlaştırır. Tamamen gizlilik sağlamaz ama daha güçlü bir işarettir.
  • Properties (@property Decorator): Metotları, nitelik gibi (parantez kullanmadan) erişilebilir hale getirmenin Pythonic yoludur. Bu, getter, setter ve deleter metotları tanımlayarak niteliklere erişimi kontrol etmeyi sağlar (kapsüllemeyi destekler).
    • @property: Getter metodu için kullanılır.
    • @nitelik_adi.setter: Setter metodu için kullanılır.
    • @nitelik_adi.deleter: Deleter metodu için kullanılır.

class Calisan:
    def __init__(self, ad, maas):
        self.ad = ad # Public nitelik
        self._departman = "Genel" # Korumalı (konvansiyon)
        self.__maas = self._maas_kontrol(maas) # Özel (isim mangling)

    def _maas_kontrol(self, maas): # İç kullanım için metot (konvansiyon)
        if maas < 0:
            print("Maaş negatif olamaz! 0 olarak ayarlandı.")
            return 0
        return maas

    # Maas'a erişim için property (getter)
    @property
    def maas(self):
        print("Maaş değeri okunuyor (getter)...")
        return self.__maas

    # Maas'ı değiştirmek için property (setter)
    @maas.setter
    def maas(self, yeni_maas):
        print("Maaş değeri atanıyor (setter)...")
        self.__maas = self._maas_kontrol(yeni_maas)

    # Departmanı yöneten property
    @property
    def departman(self):
        return self._departman

    @departman.setter
    def departman(self, yeni_departman):
        if yeni_departman in ["IT", "İK", "Muhasebe", "Genel"]:
            self._departman = yeni_departman
        else:
            print(f"Geçersiz departman: {yeni_departman}")


emp = Calisan("Veli", 5000)
print(emp.ad)
# print(emp.__maas) # Hata! Doğrudan erişilemez (AttributeError)
# print(emp._Calisan__maas) # İsim mangling ile erişilebilir ama önerilmez

# Property kullanımı (nitelik gibi erişim)
print(f"Maaş: {emp.maas}") # Getter çalışır
emp.maas = 6000      # Setter çalışır
print(f"Yeni Maaş: {emp.maas}")
emp.maas = -100      # Setter çalışır, kontrol yapılır
print(f"Negatif atama sonrası Maaş: {emp.maas}")

print(f"Departman: {emp.departman}")
emp.departman = "IT"
print(f"Yeni Departman: {emp.departman}")
emp.departman = "Satış" # Geçersiz departman uyarısı verir
                      

Çok Biçimlilik ve Özel Metotlar

  • Çok Biçimlilik (Polymorphism): Python'da genellikle "Duck Typing" (Ördek Tiplemesi) ile kendini gösterir: "Eğer bir şey ördek gibi yürüyorsa ve ördek gibi vaklıyorsa, o muhtemelen bir ördektir." Yani bir nesnenin belirli bir metoda sahip olup olmadığına bakılır, tipine değil. Kalıtım yoluyla metot override etme de çok biçimliliğin bir örneğidir.
  • Özel Metotlar (Magic/Dunder Methods): Başında ve sonunda çift alt çizgi bulunan metotlardır (örn: __init__, __str__, __len__, __add__). Python'un yerleşik davranışlarını (nesne oluşturma, string temsili, uzunluk, operatörler vb.) özelleştirmek için kullanılırlar.
    • __str__(self): print(nesne) veya str(nesne) çağrıldığında nesnenin kullanıcı dostu string temsilini döndürür.
    • __repr__(self): repr(nesne) çağrıldığında veya interaktif kabukta nesne yazıldığında, nesnenin geliştirici dostu/teknik temsilini döndürür. __str__ yoksa bu kullanılır.
    • __len__(self): len(nesne) çağrıldığında nesnenin uzunluğunu döndürür.
    • __add__(self, other): + operatörünü nesneler için tanımlar.
    • __eq__(self, other): == operatörünü nesneler için tanımlar.

# Duck Typing Örneği
class Kedi:
    def konus(self): print("Miyav")
class Kopek:
    def konus(self): print("Hav hav")
class Insan:
    def konuss(self): print("Merhaba") # Metot adı farklı

def hayvan_konustur(hayvan):
    # Tip kontrolü yapmadan doğrudan metodu çağırmayı dene
    try:
        hayvan.konus()
    except AttributeError:
        print("Bu nesne 'konus' metoduna sahip değil.")

kedi = Kedi()
kopek = Kopek()
insan = Insan()

hayvan_konustur(kedi)  # Miyav
hayvan_konustur(kopek) # Hav hav
hayvan_konustur(insan) # Bu nesne 'konus' metoduna sahip değil.

# Özel Metotlar Örneği
class Vektor:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self): # Kullanıcı dostu temsil
        return f"Vektör({self.x}, {self.y})"

    def __repr__(self): # Teknik temsil
        return f"Vektor(x={self.x}, y={self.y})"

    def __add__(self, other): # + operatörünü tanımla
        if isinstance(other, Vektor):
            return Vektor(self.x + other.x, self.y + other.y)
        return NotImplemented # Diğer tiplerle toplamayı desteklemiyorsak

    def __len__(self): # len() fonksiyonunu tanımla
        # Başlangıç noktasına uzaklık (örnek amaçlı)
        import math
        return int(math.sqrt(self.x**2 + self.y**2))

v1 = Vektor(2, 3)
v2 = Vektor(4, 1)

print(v1) # __str__ çağrılır: Vektör(2, 3)
print(repr(v2)) # __repr__ çağrılır: Vektor(x=4, y=1)
print(v1 + v2) # __add__ çağrılır: Vektör(6, 4)
print(len(v1)) # __len__ çağrılır (yaklaşık uzaklık): 3
                       

Dosya İşlemleri: Veriyi Kalıcı Hale Getirme

Python, diskteki dosyaları okumak ve yazmak için basit ve güçlü araçlar sunar. Bu, programların verileri kalıcı olarak saklamasını ve başka programlarla veri alışverişi yapmasını sağlar.

Dosya Açma (open()) ve Modlar

Bir dosyayla işlem yapmak için önce open(dosya_yolu, mod='r', encoding=None) fonksiyonu ile açılması gerekir:

  • dosya_yolu: Açılacak dosyanın adı ve yolu (string).
  • mod (İsteğe bağlı): Dosyanın hangi amaçla açılacağını belirler. Yaygın modlar:
    • 'r': Okuma modu (varsayılan). Dosya yoksa FileNotFoundError verir.
    • 'w': Yazma modu. Dosya varsa içeriğini siler, yoksa yeni dosya oluşturur.
    • 'a': Ekleme modu (append). Dosya varsa sonuna ekler, yoksa yeni dosya oluşturur.
    • 'x': Özel oluşturma modu. Dosya zaten varsa FileExistsError verir, yoksa yazma modunda yeni dosya oluşturur.
    • 'b': İkili (binary) mod. Metin olmayan dosyalar (resim, ses vb.) için moda eklenir (örn: 'rb', 'wb').
    • '+': Güncelleme modu (hem okuma hem yazma). Moda eklenir (örn: 'r+', 'w+', 'a+').
  • encoding (İsteğe bağlı): Metin modunda kullanılacak karakter kodlamasını belirtir (örn: 'utf-8'). Belirtilmezse işletim sisteminin varsayılanı kullanılır, ancak özellikle farklı sistemler arasında uyumluluk için 'utf-8' belirtmek genellikle iyi bir fikirdir.

open() fonksiyonu bir dosya nesnesi (file object) döndürür. İşlem bittikten sonra dosyanın close() metodu ile kapatılması önemlidir (kaynakların serbest bırakılması için).

with İfadesi ile Güvenli Dosya İşlemleri

Dosyaları manuel olarak close() ile kapatmak unutulabilir veya hata durumlarında atlanabilir. with ifadesi, bir bağlam yöneticisi (context manager) kullanarak bu sorunu çözer. with bloğu sona erdiğinde (normal şekilde veya bir hata nedeniyle), dosya otomatik olarak kapatılır.


# with ifadesi ile dosya okuma (Önerilen Yöntem)
try:
    with open("veri.txt", "r", encoding="utf-8") as dosya:
        icerik = dosya.read() # Tüm içeriği oku
        print("Dosya içeriği (with):\n", icerik)
    # Bu noktada dosya otomatik olarak kapatılmıştır.
except FileNotFoundError:
    print("Hata: veri.txt dosyası bulunamadı!")
except Exception as e:
    print(f"Bir hata oluştu: {e}")

# with ifadesi ile dosyaya yazma
yazilacaklar = ["Satır 1\n", "Satır 2\n", "Türkçe karakterler: İıĞğŞşÖöÜü\n"]
try:
    with open("cikti.txt", "w", encoding="utf-8") as f:
        f.write("Bu ilk satır.\n") # Tek satır yazma
        f.writelines(yazilacaklar) # Liste içeriğini satır satır yazma
    print("cikti.txt dosyasına yazıldı.")
except Exception as e:
    print(f"Yazma hatası: {e}")

# Manuel open/close (Daha az güvenli)
# dosya_obj = None
# try:
#     dosya_obj = open("eski_yontem.txt", "r")
#     # ... okuma işlemleri ...
# except Exception as e:
#     print(f"Hata: {e}")
# finally:
#     if dosya_obj:
#         dosya_obj.close()
#         print("Dosya manuel olarak kapatıldı.")
                     

Dosya işlemleri için her zaman with ifadesini kullanmak tercih edilir.

Dosya Okuma ve Yazma Metotları

Açık bir dosya nesnesi üzerinden çeşitli okuma ve yazma metotları kullanılır:

  • Okuma Metotları ('r' veya 'r+' modunda):
    • read(size=-1): Dosyanın tamamını (veya belirtilen size kadar byte/karakter) okur ve string olarak döndürür. Büyük dosyalarda dikkatli kullanılmalıdır.
    • readline(size=-1): Dosyadan tek bir satırı (\n dahil) okur ve string olarak döndürür. Dosya sonuna gelindiğinde boş string ('') döndürür.
    • readlines(hint=-1): Dosyanın tüm satırlarını okur ve her satırın bir string olduğu bir liste olarak döndürür. Büyük dosyalarda belleği zorlayabilir.
    • Dosya nesnesi doğrudan for döngüsü içinde kullanılarak satır satır okunabilir (en verimli yöntemlerden biri):
      
      with open("veri.txt", "r") as f:
          for satir in f:
              print(satir.strip()) # strip() ile baştaki/sondaki boşlukları ve \n'i kaldır
                                          
  • Yazma Metotları ('w', 'a', 'w+', 'a+' modunda):
    • write(string): Verilen string'i dosyaya yazar. Yeni satır karakteri (\n) otomatik eklemez.
    • writelines(liste): String listesindeki her bir string'i dosyaya yazar (aralara veya sonlara \n eklemez, string'lerin kendisi içermelidir).

Hata Yönetimi (Exception Handling)

Program çalışırken dosya bulunamaması, geçersiz kullanıcı girdisi, ağ bağlantı sorunları gibi beklenmedik durumlar (hatalar veya istisnalar) oluşabilir. Hata yönetimi, bu tür durumları programın çökmesini önleyecek şekilde ele almayı sağlar.

try...except Bloğu

Hata oluşturma potansiyeli olan kodlar try bloğuna yazılır. Eğer bu blokta bir hata (exception) oluşursa, programın normal akışı durur ve Python uygun bir except bloğu arar.

  • except HataTipi:: Belirtilen HataTipi (örn: FileNotFoundError, ValueError, TypeError) veya onun alt türlerinden bir hata oluşursa bu blok çalışır.
  • except HataTipi as degisken:: Yakalanan hata nesnesini bir değişkene (örn: e) atayarak hata hakkında daha fazla bilgiye (e.args gibi) erişmeyi sağlar.
  • except (HataTipi1, HataTipi2):: Birden fazla hata tipini aynı blokta yakalamak için kullanılır.
  • except:: Hiçbir hata tipi belirtilmezse, tüm hata türlerini yakalar. Bu genellikle önerilmez, çünkü belirli hataları ele almak yerine tüm hataları maskeleyebilir. En sona genel Exception yazmak daha iyidir.

try:
    sayi_str = input("Bir sayı girin: ")
    sayi = int(sayi_str)
    sonuc = 100 / sayi
    print(f"100 / {sayi} = {sonuc}")

except ValueError:
    print("Hata: Lütfen geçerli bir tam sayı girin.")
except ZeroDivisionError:
    print("Hata: Sıfıra bölme yapılamaz.")
except Exception as e: # Diğer tüm beklenmedik hataları yakala
    print(f"Beklenmedik bir hata oluştu: {type(e).__name__} - {e}")

print("Program devam ediyor...")
                     

else ve finally Blokları

  • else Bloğu: try bloğunda hiçbir hata oluşmazsa, try bloğu bittikten hemen sonra çalıştırılır. Hata durumunda çalışmaz.
  • finally Bloğu: try bloğunda hata oluşsa da oluşmasa da, try, except ve else bloklarından sonra her zaman çalıştırılır. Genellikle kaynakları temizlemek (dosyaları kapatmak, ağ bağlantılarını sonlandırmak vb.) için kullanılır.

dosya_adi = "test_dosyasi.txt"
f = None # Dosya nesnesini dışarıda tanımla
try:
    f = open(dosya_adi, "w", encoding="utf-8")
    f.write("Bu bir testtir.\n")
    # Hata simülasyonu:
    # sonuc = 10 / 0
except IOError as e: # Dosya işlemleriyle ilgili hatalar
    print(f"Dosya hatası: {e}")
except Exception as e:
    print(f"Genel hata: {e}")
else:
    # Hata oluşmazsa burası çalışır
    print(f"'{dosya_adi}' dosyasına başarıyla yazıldı.")
finally:
    # Her durumda çalışır
    print("Finally bloğu çalışıyor...")
    if f: # Dosya başarıyla açıldıysa
        f.close()
        print(f"'{dosya_adi}' dosyası kapatıldı.")
                     

with ifadesi, dosya işlemleri için finally bloğunda close() çağırma ihtiyacını ortadan kaldırır.

Hata Fırlatma (raise)

Kendi özel hata durumlarınızı belirtmek veya belirli koşullar sağlandığında programın akışını bilinçli olarak durdurmak için raise ifadesi kullanılır. Genellikle yerleşik hata sınıflarından (ValueError, TypeError, Exception vb.) bir örnek fırlatılır veya kendi özel hata sınıflarınızı tanımlayabilirsiniz.


def yas_kontrol(yas):
    if not isinstance(yas, int):
        raise TypeError("Yaş tam sayı olmalıdır.")
    if yas < 0:
        raise ValueError("Yaş negatif olamaz.")
    elif yas < 18:
        print("Kullanıcı reşit değil.")
    else:
        print("Kullanıcı reşit.")

try:
    yas_kontrol(25)
    yas_kontrol(-5) # ValueError fırlatacak
    yas_kontrol("abc") # Bu satıra ulaşılamayacak
except ValueError as ve:
    print(f"Değer Hatası Yakalandı: {ve}")
except TypeError as te:
    print(f"Tip Hatası Yakalandı: {te}")

# Kendi özel hata sınıfı
class BenimHatam(Exception):
    pass

def ozel_islem(deger):
    if deger == "yasak":
        raise BenimHatam("Bu değere izin verilmiyor!")
    print("İşlem başarılı.")

try:
    ozel_islem("izinli")
    ozel_islem("yasak")
except BenimHatam as bh:
    print(f"Özel Hata Yakalandı: {bh}")
                     

İleri Konular ve Python Ekosistemi

Python'un temellerini ve ana veri yapılarını öğrendikten sonra, dili daha etkili kullanmanızı sağlayacak ileri seviye özelliklere ve geniş ekosistemine göz atabilirsiniz.

Comprehensions (List, Dict, Set) ve Generator Expressions

Mevcut bir koleksiyon veya yinelenebilir (iterable) üzerinden yeni listeler, sözlükler veya kümeler oluşturmak için kısa ve okunabilir bir yol sunarlar.

  • List Comprehension: [ifade for eleman in iterable if koşul]
    
    # 0-9 arası sayıların kareleri
    kareler = [x**2 for x in range(10)] # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    # 0-9 arası çift sayıların kareleri
    cift_kareler = [x**2 for x in range(10) if x % 2 == 0] # [0, 4, 16, 36, 64]
    print(kareler)
    print(cift_kareler)
                                
  • Dictionary Comprehension: {anahtar_ifadesi: deger_ifadesi for eleman in iterable if koşul}
    
    # Sayıların karelerini içeren sözlük
    sayi_kare_sozluk = {x: x**2 for x in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
    print(sayi_kare_sozluk)
                                
  • Set Comprehension: {ifade for eleman in iterable if koşul}
    
    # Listedeki tek sayıların kümesi
    sayilar = [1, 2, 2, 3, 4, 5, 5, 1]
    tek_sayi_kumesi = {x for x in sayilar if x % 2 != 0} # {1, 3, 5}
    print(tek_sayi_kumesi)
                                
  • Generator Expressions: List comprehension'a benzer ancak köşeli parantez yerine normal parantez kullanılır: (ifade for eleman in iterable if koşul). Tüm listeyi bellekte oluşturmak yerine, ihtiyaç duyuldukça değer üreten bir generator nesnesi döndürür. Büyük veri setleri için bellek açısından daha verimlidir.
    
    kareler_generator = (x**2 for x in range(1000000))
    print(kareler_generator) # <generator object <genexpr> at ...>
    # print(list(kareler_generator)) # Tümünü listeye çevirir (bellek kullanır)
    print(sum(kareler_generator)) # Generator üzerinde işlem yapma (tek tek üretir)
                                 

Generators (yield)

Generator'lar, değerleri tek tek üreten özel fonksiyonlardır. Normal fonksiyonlar gibi return ile tek bir değer döndürmek yerine, yield anahtar kelimesini kullanarak bir dizi değer üretebilirler. Her yield çağrıldığında fonksiyonun durumu korunur ve bir sonraki çağrıda kaldığı yerden devam eder.

Büyük veri dizileri veya sonsuz diziler oluşturmak için bellek açısından çok verimlidirler, çünkü tüm değerleri aynı anda bellekte tutmazlar.


def cift_sayi_uret(limit):
    sayi = 0
    while sayi < limit:
        yield sayi # Değeri üret ve fonksiyonun durumunu koru
        sayi += 2

# Generator nesnesini oluştur
ciftler = cift_sayi_uret(10)
print(type(ciftler)) # <class 'generator'>

# Değerleri tek tek al
print(next(ciftler)) # 0
print(next(ciftler)) # 2
print(next(ciftler)) # 4

# Veya döngü ile
print("Kalan çift sayılar:")
for c in ciftler:
    print(c) # 6, 8
                     

Decorators (@)

Decorator'lar, mevcut bir fonksiyonun veya metodun davranışını, kodunu doğrudan değiştirmeden sarmalayarak (wrap) veya değiştirerek genişletmek için kullanılan bir Python özelliğidir. Genellikle fonksiyonları argüman olarak alan ve yeni bir fonksiyon döndüren üst düzey fonksiyonlar olarak implemente edilirler. @decorator_adi sözdizimi ile uygulanırlar.

Yaygın kullanım alanları: Logging (kayıt tutma), erişim kontrolü, zamanlama ölçümü, önbelleğe alma.


import time

# Basit bir decorator fonksiyonu
def zaman_olc(fonksiyon):
    def wrapper(*args, **kwargs): # Orijinal fonksiyonun argümanlarını al
        baslangic = time.time()
        sonuc = fonksiyon(*args, **kwargs) # Orijinal fonksiyonu çalıştır
        bitis = time.time()
        print(f"'{fonksiyon.__name__}' fonksiyonu {bitis - baslangic:.4f} saniyede çalıştı.")
        return sonuc
    return wrapper

# Decorator'ı uygulama
@zaman_olc
def uzun_islem(n):
    toplam = 0
    for i in range(n):
        toplam += i
    return toplam

@zaman_olc
def baska_islem(a, b):
    time.sleep(0.5) # 0.5 saniye bekle
    return a + b

# Fonksiyonları çağırma (decorator otomatik olarak çalışır)
sonuc1 = uzun_islem(1000000)
# Çıktı: 'uzun_islem' fonksiyonu X.XXXX saniyede çalıştı.
sonuc2 = baska_islem(10, 20)
# Çıktı: 'baska_islem' fonksiyonu Y.YYYY saniyede çalıştı.

print(f"Sonuç 1: {sonuc1}")
print(f"Sonuç 2: {sonuc2}")
                     

Popüler Alanlar ve Kütüphaneler (Kısa Bahis)

  • Web Geliştirme:
    • Django: Kapsamlı, "batteries-included" bir web framework'ü. Hızlı geliştirme, ORM, admin paneli gibi birçok özellik sunar. Büyük ve karmaşık projeler için idealdir.
    • Flask: Mikro bir web framework'üdür. Daha basit ve esnektir, sadece temel yönlendirme ve istek/yanıt yönetimi sağlar. Eklentilerle genişletilebilir. Daha küçük projeler veya API'ler için popülerdir.
    • FastAPI: Modern, hızlı, asenkron web framework'ü. API geliştirmeye odaklıdır, otomatik dökümantasyon (Swagger UI) ve veri doğrulama gibi özellikler sunar.
  • Veri Bilimi ve Analiz:
    • NumPy (Numerical Python): Çok boyutlu diziler (arrays) ve matrisler üzerinde yüksek performanslı matematiksel işlemler için temel kütüphane.
    • Pandas: Veri manipülasyonu ve analizi için güçlü ve esnek veri yapıları (DataFrame, Series) sunar. Veri temizleme, işleme, birleştirme, gruplama gibi işlemler için vazgeçilmezdir.
    • Matplotlib: Statik, dinamik ve interaktif veri görselleştirmeleri (grafikler, çizimler) oluşturmak için temel kütüphane.
    • Seaborn: Matplotlib üzerine kurulu, daha çekici ve bilgilendirici istatistiksel grafikler oluşturmayı kolaylaştıran bir kütüphane.
  • Makine Öğrenmesi ve Yapay Zeka:
    • Scikit-learn: Sınıflandırma, regresyon, kümeleme, boyut azaltma gibi birçok makine öğrenmesi algoritmasını, model değerlendirme ve veri ön işleme araçlarını içeren popüler bir kütüphane.
    • TensorFlow: Google tarafından geliştirilen, özellikle derin öğrenme (deep learning) için kullanılan açık kaynaklı güçlü bir kütüphane.
    • PyTorch: Facebook (Meta) tarafından geliştirilen, TensorFlow'a popüler bir alternatif olan, esnek ve araştırmacılar arasında yaygın olarak kullanılan bir derin öğrenme kütüphanesi.
    • Keras: TensorFlow veya diğer backend'ler üzerinde çalışan, derin öğrenme modellerini daha kolay oluşturmayı sağlayan üst düzey bir API.
  • Otomasyon ve Scripting: os, sys, shutil, subprocess gibi standart kütüphaneler veya requests (web), BeautifulSoup/Scrapy (web scraping), Selenium (web otomasyonu) gibi harici kütüphanelerle tekrarlayan görevleri otomatikleştirmek için sıkça kullanılır.

Python En İyi Uygulamaları (PEP 8 ve Ötesi)

  • PEP 8'e Uyun: Python'un resmi stil rehberi olan PEP 8 (PEP 8), kodun okunabilir ve tutarlı olması için girintileme, isimlendirme kuralları, satır uzunluğu, boşluk kullanımı gibi konularda öneriler sunar. Linters (Flake8, Pylint) ve formatters (Black, Yapf) bu kurallara uymayı kolaylaştırır.
  • Anlamlı İsimler Kullanın: Değişkenlere, fonksiyonlara ve sınıflara ne işe yaradıklarını açıkça belirten, okunabilir isimler verin (kullanici_listesi yerine kullanicilar, hesapla yerine vergiyi_hesapla gibi).
  • Okunabilir Kod Yazın: Basitliği hedefleyin. Karmaşık ifadeleri parçalara ayırın. Mantığı açıkça ifade edin. Pythonic (Python'a özgü doğal ve zarif) çözümleri tercih edin (örn: list comprehensions, with ifadesi).
  • DRY (Don't Repeat Yourself): Kod tekrarından kaçının. Tekrar eden kod bloklarını fonksiyonlara veya sınıflara taşıyın.
  • Yorumlar ve Docstrings: Kodun "neden"ini açıklamak için yorumlar, fonksiyonların/sınıfların/modüllerin "ne" yaptığını açıklamak için docstring'ler kullanın. Kodun kendisi "nasıl" yapıldığını açıklamalıdır.
  • Sanal Ortamlar Kullanın: Her proje için ayrı bir sanal ortam kullanarak bağımlılıkları izole edin.
  • Test Yazın: Kodunuzun doğru çalıştığından emin olmak ve gelecekteki değişikliklerde regresyonları (eski hataların tekrar oluşması) önlemek için birim testleri (unittest, pytest) yazın.
  • Hata Yönetimini İhmal Etmeyin: Olası hataları öngörün ve try...except blokları ile uygun şekilde yönetin.
  • Standart Kütüphaneyi İyi Öğrenin: Tekerleği yeniden icat etmeden önce, ihtiyacınız olan işlevselliğin standart kütüphanede olup olmadığını kontrol edin.

Sonuç: Python ile Sonsuz Olasılıklar

Python, öğrenmesi nispeten kolay, ancak son derece güçlü ve çok yönlü bir programlama dilidir. Temiz sözdizimi, geniş standart kütüphanesi ve devasa topluluk desteği sayesinde, web geliştirmeden yapay zekaya, veri analizinden otomasyona kadar akla gelebilecek hemen her alanda kullanılmaktadır.

Bu rehber, Python'un temel yapı taşlarından başlayarak önemli kavramları ve modern teknikleri kapsamlı bir şekilde ele almayı amaçladı. Ancak Python dünyası çok geniştir ve öğrenme süreci sürekli devam eder. Belirli bir alanda (web, veri bilimi vb.) uzmanlaşmak için ilgili framework ve kütüphaneleri derinlemesine öğrenmek, pratik projeler geliştirmek ve toplulukla etkileşimde bulunmak önemlidir.

Python'un okunabilirliği ve basitliği, onu programlamaya yeni başlayanlar için harika bir başlangıç noktası yaparken, sunduğu derinlik ve esneklik deneyimli geliştiriciler için de cazip kılmaktadır. Bu güçlü dili öğrenerek, teknoloji dünyasında kendinize sayısız kapı açabilir ve yaratıcı fikirlerinizi hayata geçirebilirsiniz.