← Kursa Dön
📄 Text · 18 min

Parametreler: *args, **kwargs, Default

Fonksiyon tanımlamayı öğrendin. Ama gerçek güç, fonksiyonlara nasıl veri aktardığında ortaya çıkar. Python'un parametre sistemi inanılmaz esnek — aynı fonksiyonu 3 farklı şekilde çağırabilir, isteğe bağlı parametreler ekleyebilir, hatta kaç parametre geleceğini bilmeden bile fonksiyon yazabilirsin.

Bu derste Python'un tüm parametre türlerini, sıralama kurallarını ve sık yapılan hataları detaylıca göreceğiz.


Terimler: Parametre vs Argüman

Önce terminolojiyi netleştirelim çünkü herkes bunları karıştırır:

  • Parametre (parameter): Fonksiyon tanımında yazılan değişken adları

  • Argüman (argument): Fonksiyon çağrılırken verilen değerler

def selamla(isim):     # "isim" bir PARAMETRE
    return f"Merhaba {isim}!"

selamla("Ayşe")        # "Ayşe" bir ARGÜMAN

Günlük konuşmada çoğu kişi ikisini birbirinin yerine kullanır ve dünya yıkılmaz. Ama teknik tartışmalarda farkı bilmek işe yarar.


Positional vs Keyword Arguments

Python'da fonksiyon çağırırken argümanları iki şekilde geçebilirsin:

Positional (Konumsal) Arguments

Argümanlar sırayla eşleşir. İlk argüman ilk parametreye, ikinci argüman ikinci parametreye gider:

def bilgi_goster(ad, yas, sehir):
    print(f"{ad}, {yas} yaşında, {sehir}'da yaşıyor.")

# Positional: sıra önemli!
bilgi_goster("Ahmet", 25, "İstanbul")
# Ahmet, 25 yaşında, İstanbul'da yaşıyor.

# Sırayı karıştırırsan anlam da karışır
bilgi_goster(25, "İstanbul", "Ahmet")
# 25, İstanbul yaşında, Ahmet'da yaşıyor. ← Anlamsız!

Keyword (İsimli) Arguments

Argümanları parametre adıyla geçersin. Sıra önemli değil:

def bilgi_goster(ad, yas, sehir):
    print(f"{ad}, {yas} yaşında, {sehir}'da yaşıyor.")

# Keyword: sıra önemli değil!
bilgi_goster(yas=25, sehir="İstanbul", ad="Ahmet")
# Ahmet, 25 yaşında, İstanbul'da yaşıyor. ✅

# İkisini karıştırabilirsin (ama positional önce gelmeli)
bilgi_goster("Ahmet", sehir="İstanbul", yas=25)
# Ahmet, 25 yaşında, İstanbul'da yaşıyor. ✅

Kural: Positional argümanlar her zaman keyword argümanlardan önce gelir.

# ❌ Hata: positional, keyword'den sonra gelemez
bilgi_goster(ad="Ahmet", 25, "İstanbul")
# SyntaxError: positional argument follows keyword argument

Ne Zaman Hangisini Kullanmalı?

  • Positional: Az sayıda, sırası belli argümanlar için. topla(3, 5) gayet anlaşılır.

  • Keyword: Çok parametreli fonksiyonlarda, boolean parametrelerde veya varsayılan değerlerle çalışırken. dosya_ac(yol="data.txt", encoding="utf-8", readonly=True) çok daha okunabilir.


Default (Varsayılan) Parameter Values

Parametrelere varsayılan değer atayabilirsin. Böylece çağıran kişi o parametreyi vermezse varsayılan kullanılır:

def kahve_siparis(boy="orta", sut=True, seker=1):
    """Kahve siparişi oluşturur."""
    sut_str = "sütlü" if sut else "sütsüz"
    print(f"{boy.capitalize()} boy, {sut_str}, {seker} şekerli kahve")

# Tüm varsayılanları kullan
kahve_siparis()
# Orta boy, sütlü, 1 şekerli kahve

# Sadece boy değiştir
kahve_siparis("büyük")
# Büyük boy, sütlü, 1 şekerli kahve

# Sadece şekeri değiştir
kahve_siparis(seker=0)
# Orta boy, sütlü, 0 şekerli kahve

# Hepsini değiştir
kahve_siparis("küçük", False, 3)
# Küçük boy, sütsüz, 3 şekerli kahve

Bu, fonksiyonları çok esnek yapar. Kullanıcı sadece değiştirmek istediği parametreleri verir, gerisini varsayılanlara bırakır.

Kural: Varsayılan değeri olan parametreler, olmayan parametrelerden sonra gelmeli:

# ✅ Doğru: default'suz önce, default'lu sonra
def kayit_olustur(ad, soyad, aktif=True, rol="kullanici"):
    pass

# ❌ Hata: default'suz parametre default'lunun arkasında
def kayit_olustur(ad, aktif=True, soyad):
    pass
# SyntaxError: non-default argument follows default argument

Pratik Örnek: Esnek Loglama

import datetime

def log(mesaj, seviye="INFO", zaman_damgasi=True):
    """Esnek loglama fonksiyonu."""
    cikti = ""
    if zaman_damgasi:
        simdi = datetime.datetime.now().strftime("%H:%M:%S")
        cikti += f"[{simdi}] "
    cikti += f"[{seviye}] {mesaj}"
    print(cikti)

log("Uygulama başlatıldı")
# [14:30:22] [INFO] Uygulama başlatıldı

log("Disk dolu!", seviye="WARN")
# [14:30:22] [WARN] Disk dolu!

log("Debug bilgisi", seviye="DEBUG", zaman_damgasi=False)
# [DEBUG] Debug bilgisi

*args: Değişken Sayıda Positional Argüman

Bazen fonksiyona kaç argüman geleceğini bilemezsin. *args ile sınırsız sayıda positional argüman kabul edebilirsin:

def topla(*args):
    """İstediğin kadar sayıyı toplar."""
    print(f"Gelen argümanlar: {args}")
    print(f"Tipi: {type(args)}")  # <class 'tuple'>
    return sum(args)

print(topla(1, 2))           # 3
print(topla(1, 2, 3, 4, 5))  # 15
print(topla(10))              # 10
print(topla())                # 0

*args bir tuple olarak gelir. İsim "args" olmak zorunda değil — *sayilar, *elemanlar da yazabilirsin. Önemli olan * yıldızı.

def en_buyuk_bul(*sayilar):
    """Verilen sayılardan en büyüğünü bulur."""
    if not sayilar:
        return None
    
    en_buyuk = sayilar[0]
    for sayi in sayilar[1:]:
        if sayi > en_buyuk:
            en_buyuk = sayi
    return en_buyuk

print(en_buyuk_bul(3, 7, 2, 9, 1))  # 9
print(en_buyuk_bul(42))               # 42
print(en_buyuk_bul())                  # None

Normal Parametrelerle Birlikte Kullanım

*args normal parametrelerle beraber kullanılabilir:

def raporla(baslik, *satirlar):
    """Başlık ve altına satırlar yazar."""
    print(f"=== {baslik} ===")
    for i, satir in enumerate(satirlar, 1):
        print(f"  {i}. {satir}")

raporla("Yapılacaklar", "Market alışverişi", "Fatura ödeme", "Spor")

Çıktı:

=== Yapılacaklar ===
  1. Market alışverişi
  2. Fatura ödeme
  3. Spor

İlk argüman baslik'a gider, geri kalan her şey *satirlar tuple'ına toplanır.


**kwargs: Değişken Sayıda Keyword Argüman

**kwargs ile sınırsız sayıda isimli argüman kabul edebilirsin. Bu argümanlar bir dictionary olarak gelir:

def profil_olustur(**kwargs):
    """Dinamik profil oluşturur."""
    print(f"Gelen veri: {kwargs}")
    print(f"Tipi: {type(kwargs)}")  # <class 'dict'>
    
    for anahtar, deger in kwargs.items():
        print(f"  {anahtar}: {deger}")

profil_olustur(ad="Ayşe", yas=28, sehir="Ankara")

Çıktı:

Gelen veri: {'ad': 'Ayşe', 'yas': 28, 'sehir': 'Ankara'}
Tipi: <class 'dict'>
  ad: Ayşe
  yas: 28
  sehir: Ankara

**kwargs ismi de zorunlu değil — **bilgiler, **options da olabilir. Önemli olan ** çift yıldız.

Pratik: Esnek HTML Tag Oluşturucu

def html_tag(tag, icerik, **attributes):
    """HTML tag'i oluşturur."""
    attr_str = ""
    for anahtar, deger in attributes.items():
        # class_ → class (Python'da class reserved word)
        attr_adi = anahtar.rstrip("_")
        attr_str += f' {attr_adi}="{deger}"'
    
    return f"<{tag}{attr_str}>{icerik}</{tag}>"

print(html_tag("p", "Merhaba Dünya"))
# <p>Merhaba Dünya</p>

print(html_tag("div", "İçerik", class_="container", id="main"))
# <div class="container" id="main">İçerik</div>

print(html_tag("a", "Tıkla", href="https://python.org", target="_blank"))
# <a href="https://python.org" target="_blank">Tıkla</a>

*args ve **kwargs Birlikte

İkisi aynı fonksiyonda kullanılabilir:

def super_fonksiyon(*args, **kwargs):
    """Her şeyi kabul eden fonksiyon."""
    print(f"Positional: {args}")
    print(f"Keyword: {kwargs}")

super_fonksiyon(1, 2, 3, ad="Ali", yas=30)
# Positional: (1, 2, 3)
# Keyword: {'ad': 'Ali', 'yas': 30}

Bu pattern özellikle wrapper fonksiyonlar ve decorator'lar yazarken çok kullanılır:

def loglayici(fonksiyon):
    """Fonksiyon çağrılarını loglar."""
    def wrapper(*args, **kwargs):
        print(f"Çağrılıyor: {fonksiyon.__name__}")
        print(f"  args: {args}")
        print(f"  kwargs: {kwargs}")
        sonuc = fonksiyon(*args, **kwargs)
        print(f"  Sonuç: {sonuc}")
        return sonuc
    return wrapper

*args ve **kwargs wrapper'ın herhangi bir fonksiyonu sarmasını sağlar — kaç parametre alırsa alsın.


Keyword-Only Arguments (Sadece İsimle Geçilebilen)

Python 3'te, * işaretinden sonra gelen parametreler sadece keyword olarak geçilebilir:

def dosya_yaz(icerik, *, dosya_adi, encoding="utf-8"):
    """Dosyaya yazar. dosya_adi sadece keyword olarak geçilebilir."""
    print(f"'{dosya_adi}' dosyasına yazılıyor ({encoding})...")
    print(f"İçerik: {icerik}")

# ✅ Doğru: keyword olarak geç
dosya_yaz("Merhaba", dosya_adi="test.txt")
dosya_yaz("Veri", dosya_adi="data.csv", encoding="latin-1")

# ❌ Hata: positional olarak geçemezsin
# dosya_yaz("Merhaba", "test.txt")
# TypeError: dosya_yaz() takes 1 positional argument but 2 were given

* tek başına "burada positional argümanlar bitiyor" demektir. Ondan sonraki her şey keyword-only.

Neden Keyword-Only?

Boolean parametreler için özellikle kullanışlı:

# ❌ Bu çağrı ne anlama geliyor? Kim bilebilir?
dosya_oku("data.txt", True, False, True)

# ✅ Çok daha anlaşılır!
dosya_oku("data.txt", binary=True, cache=False, verbose=True)

Keyword-only zorunluluğu koyarak API'yi yanlış kullanımdan korursun.

def connect(host, port, *, timeout=30, retry=3, ssl=False):
    """Sunucuya bağlanır. Opsiyonel parametreler keyword-only."""
    print(f"Bağlanılıyor: {host}:{port}")
    print(f"  timeout={timeout}, retry={retry}, ssl={ssl}")

# Temiz, anlaşılır çağrı
connect("api.example.com", 443, ssl=True, timeout=10)

*args ile Birlikte

*args kullandığında, ondan sonraki parametreler zaten keyword-only olur:

def log(*mesajlar, seviye="INFO", dosya=None):
    for mesaj in mesajlar:
        satir = f"[{seviye}] {mesaj}"
        print(satir)

log("Başladı", "İşleniyor", "Bitti", seviye="DEBUG")

Positional-Only Arguments (Python 3.8+)

Python 3.8 ile / operatörü geldi. /'den önceki parametreler sadece positional olarak geçilebilir:

def us_al(taban, us, /):
    """Taban ve üs sadece positional olarak geçilebilir."""
    return taban ** us

# ✅ Doğru: positional
print(us_al(2, 10))  # 1024

# ❌ Hata: keyword olarak geçilemez
# print(us_al(taban=2, us=10))
# TypeError: us_al() got some positional-only arguments passed as keyword arguments

Neden Positional-Only?

  1. Parametre ismi değişebilir: API'yi bozmadan parametre adını değiştirebilirsin

  2. Doğal kullanım: len(obj) doğal, len(obj=mylist) garip

  3. Performans: CPython'da positional-only parametreler biraz daha hızlı

Python'un kendi fonksiyonlarının çoğu positional-only:

# Bu çalışmaz:
# len(obj=[1,2,3])  # TypeError
# print(sep=" ")     # Bu çalışır çünkü sep keyword-only

# len, range, abs gibi fonksiyonlar positional-only

/ ve * Birlikte

Hem positional-only hem keyword-only parametreleri aynı fonksiyonda kullanabilirsin:

def karma(pos_only, /, normal, *, kw_only):
    """Üç tür parametre bir arada."""
    print(f"pos_only={pos_only}, normal={normal}, kw_only={kw_only}")

# ✅ Doğru kullanımlar
karma(1, 2, kw_only=3)         # pos_only=1, normal=2, kw_only=3
karma(1, normal=2, kw_only=3)  # pos_only=1, normal=2, kw_only=3

# ❌ Hatalar
# karma(pos_only=1, normal=2, kw_only=3)  # pos_only keyword olamaz!
# karma(1, 2, 3)                           # kw_only keyword olmalı!
# Daha gerçekçi bir örnek
def arama(query, /, *, limit=10, offset=0, siralama="relevance"):
    """Arama yapar. Query positional-only, geri kalan keyword-only."""
    print(f"Aranan: '{query}' (limit={limit}, offset={offset})")

arama("python dersleri", limit=5)
arama("machine learning", limit=20, siralama="date")

Parametre Sırası Kuralı

Tüm parametre türlerini bir arada kullandığında belirli bir sıra izlemelisin:

def fonksiyon(pos_only, /, normal, *args, kw_only, **kwargs):

Sıra:

  1. Positional-only parametreler (/ öncesi)

  2. Normal parametreler (positional veya keyword)

  3. `*args` (variable positional)

  4. Keyword-only parametreler (* veya *args sonrası)

  5. `kwargs`** (variable keyword)

def tam_ornek(a, b, /, c, d=10, *args, e, f=20, **kwargs):
    """Tüm parametre türleri bir arada."""
    print(f"Positional-only: a={a}, b={b}")
    print(f"Normal: c={c}, d={d}")
    print(f"*args: {args}")
    print(f"Keyword-only: e={e}, f={f}")
    print(f"**kwargs: {kwargs}")

tam_ornek(1, 2, 3, 4, 5, 6, e=100, g=200, h=300)

Çıktı:

Positional-only: a=1, b=2
Normal: c=3, d=4
*args: (5, 6)
Keyword-only: e=100, f=20
**kwargs: {'g': 200, 'h': 300}

💡 İpucu: Bu kadar karmaşık parametre kombinasyonunu pratikte nadiren kullanırsın. Ama Python'un parametre sistemini tam olarak anlamak için bilmen gerekiyor. Gerçek projelerde genellikle 2-3 parametre türünü bir arada kullanırsın, hepsini birden değil.


⚠️ Mutable Default Argument TUZAĞI

Bu, Python'un en ünlü tuzaklarından biri. Acemi-kıdemli fark etmez, herkes en az bir kere düşer.

Tuzak: def f(lst=[])

def listeye_ekle(eleman, liste=[]):
    """BU FONKSİYON BOZUK! Kullanma!"""
    liste.append(eleman)
    return liste

# İlk çağrı — sorun yok gibi
print(listeye_ekle("elma"))    # ['elma'] ✅

# İkinci çağrı — HOP!
print(listeye_ekle("armut"))   # ['elma', 'armut'] 😱

# Üçüncü çağrı — gittikçe büyüyor!
print(listeye_ekle("muz"))     # ['elma', 'armut', 'muz'] 😱😱

Ne oldu? Her çağrıda yeni boş bir liste bekliyordun ama önceki elemanlar hâlâ orada!

Neden?

Python'da default değerler fonksiyon tanımlandığında bir kere oluşturulur, her çağrıda yeniden oluşturulmaz. Yani [] bir kere yaratılır ve tüm çağrılar aynı listeyi paylaşır.

Bunu kanıtlayalım:

def tuzak(lst=[]):
    print(f"  Liste id: {id(lst)}")
    lst.append(1)
    return lst

print("Çağrı 1:")
tuzak()  # Liste id: 140234567890 ← bu id'yi not et
print("Çağrı 2:")
tuzak()  # Liste id: 140234567890 ← AYNI id! Aynı nesne!
print("Çağrı 3:")
tuzak()  # Liste id: 140234567890 ← hâlâ aynı!

Çözüm: None Pattern

def listeye_ekle(eleman, liste=None):
    """Doğru yol: None kullan, fonksiyon içinde oluştur."""
    if liste is None:
        liste = []  # Her çağrıda yeni liste oluşturulur
    liste.append(eleman)
    return liste

print(listeye_ekle("elma"))    # ['elma'] ✅
print(listeye_ekle("armut"))   # ['armut'] ✅ Her seferinde yeni!
print(listeye_ekle("muz"))     # ['muz'] ✅

# Mevcut listeye de ekleyebilirsin
meyveler = ["çilek"]
print(listeye_ekle("karpuz", meyveler))  # ['çilek', 'karpuz']

⚠️ Dikkat: Bu tuzak sadece mutable (değiştirilebilir) default değerler için geçerli: list, dict, set. Immutable değerler (int, str, tuple, None, True) için sorun yok.

# ✅ Bunlar güvenli
def f(x=0):      pass  # int immutable
def f(s="abc"):  pass  # str immutable
def f(t=(1,2)):  pass  # tuple immutable

# ❌ Bunlar tuzak!
def f(lst=[]):    pass  # list mutable → TUZAK
def f(d={}):      pass  # dict mutable → TUZAK
def f(s=set()):   pass  # set mutable → TUZAK

Bu tuzağı bildiğine göre her zaman None pattern kullan:

def fonksiyon(veri=None, config=None):
    if veri is None:
        veri = []
    if config is None:
        config = {}
    # ...

Unpacking ile Fonksiyon Çağırma

Bir listeyi veya dictionary'yi "açarak" fonksiyona argüman olarak geçebilirsin.

* ile Liste/Tuple Unpacking

def toplam(a, b, c):
    return a + b + c

sayilar = [10, 20, 30]

# ❌ Bu çalışmaz
# toplam(sayilar)  # TypeError: 1 argüman verildi, 3 bekleniyor

# ✅ * ile aç
print(toplam(*sayilar))  # 60
# Aslında toplam(10, 20, 30) çağrılıyor

Bu, range() gibi fonksiyonlarla da çalışır:

aralik = (2, 10, 3)
print(list(range(*aralik)))  # [2, 5, 8]
# range(2, 10, 3) ile aynı

** ile Dictionary Unpacking

def kullanici_olustur(ad, soyad, yas, sehir):
    return f"{ad} {soyad}, {yas} yaşında, {sehir}"

bilgiler = {
    "ad": "Zeynep",
    "soyad": "Kaya",
    "yas": 27,
    "sehir": "İzmir"
}

# ** ile dictionary'yi aç
print(kullanici_olustur(**bilgiler))
# Zeynep Kaya, 27 yaşında, İzmir
# Aslında kullanici_olustur(ad="Zeynep", soyad="Kaya", yas=27, sehir="İzmir") çağrılıyor

İkisini Birlikte Kullanma

def rapor(baslik, *satirlar, **meta):
    print(f"\n{'='*40}")
    print(f"  {baslik}")
    print(f"{'='*40}")
    for satir in satirlar:
        print(f"  • {satir}")
    if meta:
        print(f"{'─'*40}")
        for k, v in meta.items():
            print(f"  {k}: {v}")

# Unpacking ile çağır
konular = ["Giriş", "Analiz", "Sonuç"]
detaylar = {"yazar": "Ayşe", "tarih": "2024-01-15"}

rapor("Proje Raporu", *konular, **detaylar)

Çıktı:

========================================
  Proje Raporu
========================================
  • Giriş
  • Analiz
  • Sonuç
────────────────────────────────────────
  yazar: Ayşe
  tarih: 2024-01-15

Unpacking ile Dictionary Birleştirme

Python 3.5+ ile ** kullanarak dictionary'leri birleştirebilirsin:

varsayilan_ayarlar = {"tema": "açık", "dil": "tr", "font_boyut": 14}
kullanici_ayarlari = {"tema": "koyu", "font_boyut": 16}

# Kullanıcı ayarları varsayılanları override eder
ayarlar = {**varsayilan_ayarlar, **kullanici_ayarlari}
print(ayarlar)
# {'tema': 'koyu', 'dil': 'tr', 'font_boyut': 16}

Son gelen aynı anahtarı ezer. Bu pattern konfigürasyon yönetiminde çok kullanılır.


Pratik: Esnek print_info() Fonksiyonu

Öğrendiklerimizi birleştirip gerçek hayatta kullanılabilecek esnek bir fonksiyon yazalım:

def print_info(baslik="Bilgi", /, *args, ayirici="─", genislik=40, **kwargs):
    """Esnek bilgi kartı yazdırır.
    
    Args:
        baslik: Kart başlığı (positional-only)
        *args: Ek satırlar
        ayirici: Ayırıcı karakter
        genislik: Kart genişliği
        **kwargs: Anahtar-değer çiftleri
    """
    print(ayirici * genislik)
    print(f"  📋 {baslik}")
    print(ayirici * genislik)
    
    # Sırasız satırlar (*args)
    for arg in args:
        print(f"  {arg}")
    
    # Anahtar-değer çiftleri (**kwargs)
    if kwargs:
        max_key_len = max(len(str(k)) for k in kwargs)
        for key, value in kwargs.items():
            # snake_case'i güzel formata çevir
            guzel_key = key.replace("_", " ").title()
            print(f"  {guzel_key:<{max_key_len + 5}}: {value}")
    
    print(ayirici * genislik)

# Basit kullanım
print_info("Kullanıcı", ad="Mehmet", soyad="Demir", yas=32)

# Daha detaylı
print_info(
    "Proje Durumu",
    "⚡ Acil güncelleme gerekli",
    "🔄 Sprint 3 devam ediyor",
    proje_adi="E-Ticaret",
    durum="Geliştirmede",
    tamamlanma="65%",
    son_guncelleme="2024-01-15",
    ayirici="═",
    genislik=50
)

Çıktı:

────────────────────────────────────────
  📋 Kullanıcı
────────────────────────────────────────
  Ad       : Mehmet
  Soyad    : Demir
  Yas      : 32
────────────────────────────────────────

══════════════════════════════════════════════════
  📋 Proje Durumu
══════════════════════════════════════════════════
  ⚡ Acil güncelleme gerekli
  🔄 Sprint 3 devam ediyor
  Proje Adi        : E-Ticaret
  Durum            : Geliştirmede
  Tamamlanma       : 65%
  Son Guncelleme   : 2024-01-15
══════════════════════════════════════════════════

Bu tek fonksiyonda gördüğün parametreler:

  • baslik → positional-only (/ sonrası)

  • *args → değişken sayıda ek satır

  • ayirici, genislik → keyword-only (varsayılanlı)

  • **kwargs → istediğin kadar anahtar-değer


Type Hints ile Parametreleri Belgeleme

Python 3.5+ ile parametrelerin tipini belirtebilirsin. Bu çalışma zamanında kontrol yapmaz ama dokümantasyon ve IDE desteği için harika:

def hesapla_bmi(kilo: float, boy: float) -> float:
    """BMI hesaplar."""
    return kilo / (boy ** 2)

def selamla(isim: str, resmi: bool = False) -> str:
    """İsme göre selamlama döndürür."""
    if resmi:
        return f"Sayın {isim}, hoş geldiniz."
    return f"Selam {isim}!"

# Type hint'ler çalışmayı DEĞİŞTİRMEZ
# Yanlış tip versen bile Python hata vermez (runtime'da)
print(hesapla_bmi("yetmiş", "bir_yetmiş"))  # Çalışır ama hata verir

Daha karmaşık type hint'ler:

from typing import Optional

def ara(
    query: str,
    limit: int = 10,
    filtreler: Optional[dict] = None
) -> list[dict]:
    """Arama yapar ve sonuçları döndürür."""
    if filtreler is None:
        filtreler = {}
    # ... arama mantığı
    return []

💡 İpucu: Type hint'ler zorunlu değil ama büyük projelerde hayat kurtarır. IDE'ler (VS Code, PyCharm) type hint'leri kullanarak otomatik tamamlama ve hata tespiti yapar. mypy gibi araçlar type hint'leri statik olarak kontrol edebilir.


Sık Yapılan Hatalar

1. Argüman Sayısı Uyuşmazlığı

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

# topla(1)        # TypeError: missing 1 required positional argument: 'b'
# topla(1, 2, 3)  # TypeError: takes 2 positional arguments but 3 were given

2. Keyword Argümanı İki Kere Verme

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

# f(1, a=2)  # TypeError: f() got multiple values for argument 'a'
# İlk argüman a=1 oldu, sonra a=2 diyorsun → çakışma!

3. Bilinmeyen Keyword Argümanı

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

# f(a=1, b=2, c=3)  # TypeError: f() got an unexpected keyword argument 'c'
# **kwargs yoksa fazladan keyword argüman veremezsin

4. Mutable Default (Tekrar!)

Bu kadar önemli ki bir kere daha vurgulayalım:

# ❌ ASLA böyle yapma
def kotu(sozluk={}):
    sozluk["sayac"] = sozluk.get("sayac", 0) + 1
    return sozluk

print(kotu())  # {'sayac': 1}
print(kotu())  # {'sayac': 2} ← aynı dict!

# ✅ HER ZAMAN böyle yap
def iyi(sozluk=None):
    if sozluk is None:
        sozluk = {}
    sozluk["sayac"] = sozluk.get("sayac", 0) + 1
    return sozluk

print(iyi())  # {'sayac': 1}
print(iyi())  # {'sayac': 1} ✅ her seferinde yeni dict

Özet

  • Positional argümanlar sırayla, keyword argümanlar isimle eşleşir. İkisi karışık kullanılabilir ama positional her zaman önce gelir.

  • Default değerler parametrelere varsayılan atamayı sağlar. Default'lu parametreler default'suzlardan sonra gelmelidir.

  • `*args` sınırsız positional argümanı tuple olarak toplar; `kwargs`** sınırsız keyword argümanı dictionary olarak toplar.

  • Keyword-only (* sonrası) ve positional-only (/ öncesi) parametreler API tasarımını daha güvenli yapar.

  • Mutable default argument tuzağı Python'un en tehlikeli gotcha'larından biridir. def f(lst=[]) yerine her zaman def f(lst=None) kullan.

  • Unpacking (*liste, **dict) ile koleksiyonları açarak fonksiyonlara argüman olarak geçebilirsin. Bu özellikle konfigürasyon ve wrapper fonksiyonlarda çok işe yarar.