
Python’da fonksiyonlar, kodu okunabilir, tekrar kullanılabilir ve test edilebilir hale getiren temel yapı taşlarından biridir. Günlük pratikte en çok kafa karışıklığı yaratan konular genellikle şunlardır: parametrelerin nasıl geçirildiği (pozisyonel mi, isimle mi?), varsayılan argümanların ne zaman “oluşturulduğu”, *args/**kwargs ile esnek imza tasarımı, küçük yardımcı fonksiyonlar için lambda kullanımı ve dekoratörlerle (dekoratör/decorator) fonksiyon sarmalama.
Bu yazı, resmi Python dokümantasyonundaki kuralları temel alır (özellikle fonksiyon tanımlama ve parametre davranışı) ve pratik örneklerle pekiştirir. Ana referans: Python Tutorial’daki “Defining Functions / More on Defining Functions” bölümleri (S1) ve dekoratörlerde metadata korumak için functools.wraps dokümantasyonu (S4). Stil tarafında PEP rehberleriyle uyumlu öneriler bulacaksınız (S2).
En temel haliyle Python fonksiyonu def ile tanımlanır ve parametreler parantez içinde belirtilir. Python, argümanları iki ana yoldan alır: pozisyonel (sıraya göre) ve keyword (isim vererek). Resmi dokümantasyon, fonksiyonların bu çağrı modelini ve farklı parametre tiplerini sistematik olarak açıklar (S1).
Aşağıdaki örnek, aynı fonksiyonun hem pozisyonel hem de keyword argümanlarla çağrılabileceğini gösterir:
Kod:
def greet(name, greeting):
return f"{greeting}, {name}!"
greet("Ada", "Merhaba")
greet(name="Ada", greeting="Merhaba")
greet("Ada", greeting="Merhaba")
Okunabilirlik açısından, özellikle birden çok parametre olduğunda keyword argümanlar çağrının “kendini anlatmasını” sağlar.
Python, fonksiyon imzasında parametrelerin belli bir düzenini destekler. Dokümantasyonda bu parçaların birlikte nasıl çalıştığı anlatılır (S1). Pratik bir model olarak şu sırayı düşünün:
Her projede positional-only kullanmanız gerekmez; ama kütüphane API’sı tasarlarken (özellikle geriye dönük uyumluluk için) işe yarayabilir.
/ işareti, kendisinden önce gelen parametrelerin yalnızca pozisyonel verilebileceğini ifade eder (S1):
Kod:
def f(a, b, /, c, *, d):
return a, b, c, d
f(1, 2, 3, d=4)
# f(a=1, b=2, c=3, d=4) # a ve b positional-only olduğu için geçersiz
# f(1, 2, 3, 4) # d keyword-only olduğu için geçersiz
Fonksiyonun bazı parametrelerinin mutlaka isimle verilmesini istiyorsanız, imzada * kullanarak onları keyword-only yapabilirsiniz (S1).
Kod:
def connect(host, port, *, timeout=10):
return f"Connecting to {host}:{port} with timeout={timeout}"
connect("example.com", 443, timeout=5)
# connect("example.com", 443, 5) # timeout isim verilmeden geçilemez
Bu yaklaşım, özellikle “üçüncü parametre neydi?” türü karışıklıkları azaltır.
Varsayılan değerler, fonksiyon çağrısında parametre verilmezse kullanılacak değerleri tanımlar. Kritik detay: varsayılan argüman ifadeleri, fonksiyon tanımlandığı anda değerlendirilir; her çağrıda tekrar “sıfırdan” oluşturulmaz (S1). Bu kural, özellikle mutable (değiştirilebilir) tiplerde beklenmedik sonuçlara yol açabilir.
Mutable varsayılanların paylaşılan duruma neden olabileceği sık görülen bir durumdur; yaygın çözüm None kullanıp içeride yeni nesne üretmektir (S1, S3).
Kod:
# Kaçınılması önerilen kullanım
def add_item(item, items=[]):
items.append(item)
return items
# Önerilen kalıp
def add_item_safe(item, items=None):
if items is None:
items = []
items.append(item)
return items
Ne zaman sorun olur? Fonksiyon, çağrılar arasında default listedeki değişiklikleri “hatırladığı” için. Bu davranış her zaman problem olmayabilir; ancak çoğu iş kodunda istenmeyen sürprizlere yol açtığından “None + içeride oluştur” kalıbı daha öngörülebilirdir.
Varsayılanlar, fonksiyonun “kolay kullanım” sürümünü sağlar. İyi bir default seti belirlerken:
*args bilinmeyen sayıda pozisyonel argümanı, **kwargs ise bilinmeyen sayıda keyword argümanını yakalamanızı sağlar (S1). Fonksiyonun içinde args bir tuple, kwargs ise bir dict gibi davranır (S1). Bu özellik adaptör fonksiyonlar ve sarmalayıcılar (wrapper) için kullanışlıdır; ancak her şeyi **kwargs içine toplamak okunabilirliği düşürebilir. Mümkünse önce açık isimli parametreleri tercih etmek iyi bir pratiktir (S3).
Kod:
def total(*numbers):
s = 0
for n in numbers:
s += n
return s
total(1, 2, 3)
total(10)
Kod:
def build_url(base, **params):
# Basit bir örnek: gerçek hayatta query string için uygun encode işlemi gerekir.
if not params:
return base
query = "&".join(f"{k}={v}" for k, v in params.items())
return f"{base}?{query}"
build_url("https://example.com", page=2, sort="name")
Not: Yukarıdaki örnek, **kwargs fikrini anlatmak içindir. Üretim kodunda query string oluşturma/encode detayı için standart kütüphanedeki ilgili yardımcıları kullanmak daha doğru bir yaklaşımdır.
Python’daki lambda ifadeleri, tek bir ifadeden oluşan küçük anonim fonksiyonlar tanımlamak içindir. Birden fazla ifade, atama ya da kapsamlı kontrol akışı gerektiğinde normal def daha uygundur (S1).
Sıralama/anahtar seçimi gibi yerlerde kısa bir fonksiyon vermek yaygındır:
Kod:
users = [
{"name": "Ada", "age": 36},
{"name": "Linus", "age": 54},
{"name": "Grace", "age": 40},
]
sorted_users = sorted(users, key=lambda u: u["age"])
Dekoratörler bir fonksiyonu alıp başka bir fonksiyonla sarmalayarak davranış eklemenizi sağlar. Örneğin loglama veya ölçüm gibi çapraz kesen konularda kullanılabilir. Dekoratör yazarken pratik bir “en iyi uygulama”: sarmalanan fonksiyonun __name__, __doc__ gibi metadata’sını korumak için functools.wraps kullanmaktır (S4).
Kod:
from functools import wraps
def count_calls(func):
@wraps(func)
def wrapper(*args, **kwargs):
wrapper.calls += 1
return func(*args, **kwargs)
wrapper.calls = 0
return wrapper
@count_calls
def hello(name):
"""İsimle selam verir."""
return f"Merhaba, {name}"
Bu örnekte iki kritik nokta var:
Docstring, fonksiyonun ne yaptığını ve nasıl kullanılacağını anlatan ilk dokümantasyon katmanıdır. PEP rehberleri, kod stilinde tutarlılık konusunda yol gösterir (S2). Ekip içinde tek bir docstring formatında anlaşmak (ör. kısa açıklama + parametreler + dönüş) bakım maliyetini azaltır.
Kod:
def area(width, height):
"""Dikdörtgen alanını hesaplar.
Args:
width: Genişlik (sayı).
height: Yükseklik (sayı).
Returns:
Alan (width * height).
"""
return width * height
Not: Amaç tek bir “zorunlu format” dayatmak değil; tutarlılık ve açıklıktır.
Python fonksiyonlarında “küçük” görünen detaylar (varsayılanların değerlendirilme zamanı, keyword-only/positional-only kuralları, dekoratörlerde metadata) proje büyüdükçe bakım maliyetini doğrudan etkiler. Bu rehberdeki örnekleri kendi kod tabanınıza uyarlarken önce okunabilirlik ve öngörülebilirlik hedefleyin; emin olmadığınız noktalarda küçük testlerle davranışı doğrulamak iyi bir pratiktir.
Yorumlar