Temeller

Python'da fonksiyonlar: def, return, argümanlar, *args ve **kwargs, lambda ve tür ipuçları

Güncellendi: · Skillaria.Top

Python'da fonksiyon, bir isme sahip ve parantezle çağrılan kod bloğudur; def ile tanımlanır ve sonucunu return ile döndürür, return yoksa None döner. Argümanlar sırayla ya da isimle verilebilir; *args fazla konumsal değerleri bir demete, **kwargs adlandırılmış değerleri bir sözlüğe toplar, lambda ise tek ifadelik kısa bir isimsiz fonksiyon oluşturur.

Python'da fonksiyon, bir isimle tanımlanan ve istediğiniz kadar farklı girdiyle çağırabileceğiniz bir kod parçasıdır. def anahtar sözcüğüyle tanımlanır, değerleri parametreler üzerinden alır ve sonucu return ile verir. Python'da fonksiyonlar, uzun bir betiği anlaşılır ve test edilebilir adımlara bölmenin ana yoludur.

Fonksiyon nasıl tanımlanır: def

Tanım def ile başlar; ardından ad, parantez içinde parametreler ve iki nokta gelir. Gövde dört boşluk girintiyle yazılır: girinti Python'da bir süs değil, sözdiziminin parçasıdır.

def greet(name):
    """Kullanıcı için bir karşılama metni döndürür."""
    return f"Merhaba, {name}!"

print(greet("Ayşe"))   # Merhaba, Ayşe!

Başlığın hemen altındaki üç tırnaklı metin docstring'dir, yani fonksiyonun belgesidir. help(greet) bunu gösterir, editörler de ipucu olarak kullanır. Fonksiyon adları PEP 8 gereği snake_case biçiminde yazılır: add_expense, get_rate.

def greet(name): satırına fonksiyonun imzası (signature) denir: ad ve parametre listesi, varsa tür ipuçlarıyla birlikte. İmzaya bakan okur, gövdeyi okumadan fonksiyonun nasıl çağrılacağını anlar.

Python'da return ne yapar

return fonksiyonu bitirir ve değeri çağırana verir. return çalıştıktan sonraki kodlar işletilmez. return yoksa ya da değersiz yazılmışsa fonksiyon None döndürür.

def area(w, h):
    w * h          # sonuç hesaplandı ama atıldı

print(area(3, 4))  # None

def area(w, h):
    return w * h

print(area(3, 4))  # 12

Unutulmuş return, yeni başlayanların en sık hatalarından biridir: fonksiyon "çalışır" gibi görünür ama None döndürür ve hata bir satır sonra ortaya çıkar. Birden fazla değer de döndürebilirsiniz; aslında bu tek bir demettir ve kolayca açılabilir:

def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([4, 1, 9])
print(low, high)   # 1 9

Erken return, gereksiz iç içe bloklardan kurtulmanıza yardım eder: önce özel durumları ayıklayın, sonra asıl mantığı yazın.

def discount(price, is_member):
    if price <= 0:
        return 0
    if not is_member:
        return price
    return price * 0.9

Python fonksiyon argümanları: konumsal ve adlandırılmış

Parametreler fonksiyonun tanımında yazılan isimlerdir; argümanlar ise çağrı sırasında verilen değerlerdir. Argümanları sırayla (konumsal) ya da isimle (adlandırılmış) verebilirsiniz. Adlandırılmış argümanlar çağrıyı kendini açıklayan hâle getirir ve sıranın önemini ortadan kaldırır.

def add_expense(title, amount, category="diğer"):
    return {"title": title, "amount": amount, "category": category}

add_expense("Kahve", 250)                          # konumsal
add_expense(amount=250, title="Kahve")             # isimle
add_expense("Taksi", 600, category="ulaşım")       # karışık

Varsayılan değeri olan parametre isteğe bağlıdır. Kurallar şöyledir: varsayılan değerli parametreler, zorunlu olanlardan sonra gelir; çağrıda konumsal argümanlar adlandırılmış olanlardan önce yazılır. Aksi halde Python SyntaxError verir.

Tuzak: değiştirilebilir varsayılan değer

Varsayılan değer her çağrıda değil, def satırı çalışırken bir kez hesaplanır. Liste ya da sözlükse, tüm çağrılar onu paylaşır.

def add_item(item, acc=[]):
    acc.append(item)
    return acc

print(add_item(1))  # [1]
print(add_item(2))  # [1, 2] — liste bir önceki çağrıyı hatırlıyor

Doğru yöntem, varsayılanı None yapıp içeride yeni bir nesne oluşturmaktır:

def add_item(item, acc=None):
    if acc is None:
        acc = []
    acc.append(item)
    return acc

*args ve **kwargs: değişken sayıda argüman

Parametre adının önündeki yıldız, fazla konumsal argümanları bir demete (tuple), çift yıldız ise fazla adlandırılmış argümanları bir sözlüğe (dict) toplar. args ve kwargs adları yalnızca bir gelenektir; önemli olan yıldızlardır.

def total(*args):
    return sum(args)

print(total(100, 250, 40))   # 390

def make_user(name, **kwargs):
    user = {"name": name}
    user.update(kwargs)
    return user

print(make_user("Ayşe", city="Ankara", age=30))
# {'name': 'Ayşe', 'city': 'Ankara', 'age': 30}

Aynı yıldızlar ters yönde de çalışır: çağrı sırasında bir diziyi ya da sözlüğü argümanlara açarlar:

values = [3, 4]
print(pow(*values))          # 81

opts = {"sep": " | ", "end": "\n"}
print("a", "b", **opts)      # a | b

Yalnızca konumsal ve yalnızca adlandırılmış parametreler

Parametre listesindeki eğik çizgi / ve yıldız * birer engel gibi çalışır: /'den öncekiler yalnızca sırayla (sözdizimi Python 3.8'de geldi, PEP 570), *'dan sonrakiler yalnızca isimle verilebilir.

def convert(amount, /, *, rate, currency="USD"):
    return round(amount / rate, 2), currency

convert(100, rate=90.5)          # (1.1, 'USD')
convert(amount=100, rate=90.5)   # TypeError: amount yalnızca konumsal verilebilir
convert(100, 90.5)               # TypeError: rate yalnızca isimle verilebilir

Yalnızca adlandırılmış parametreler, convert(100, 90.5, "EUR") gibi neyin ne olduğunun belli olmadığı çağrılardaki karışıklığı önler.

Kapsam: LEGB kuralı

Python bir ismi şu sırayla arar: Local (fonksiyonun içi) → Enclosing (dıştaki fonksiyon) → Global (modül düzeyi) → Built-in (len gibi yerleşik adlar). Fonksiyon içinden global bir değişkeni okuyabilirsiniz; ama ona yeni değer atamak için global bildirimi gerekir.

limit = 1000

def check(amount):
    return amount > limit    # global değişkeni okumak serbest

def raise_limit():
    global limit
    limit = 2000             # global olmadan yerel bir değişken oluşurdu

Pratikte global kullanmamak daha iyidir: dış durumu değiştiren bir fonksiyon zor test edilir. Değeri argüman olarak alıp sonucu döndürmek daha sağlam bir yaklaşımdır.

İç içe fonksiyonlar ve kapanımlar (closure)

Bir fonksiyonun içinde başka bir fonksiyon tanımlanabilir. İç fonksiyon, dış fonksiyon bitse bile onun değişkenlerini görmeye devam eder: buna kapanım (closure) denir. Bu tür bir değişkeni değiştirmek için nonlocal kullanılır.

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

c = make_counter()
print(c(), c(), c())   # 1 2 3

Kapanımlar dekoratörlerin ve fonksiyon fabrikalarının temelidir.

Fonksiyon da bir nesnedir

Python'da fonksiyon sıradan bir nesnedir: bir değişkene atanabilir, başka bir fonksiyona verilebilir, bir fonksiyondan döndürülebilir, bir sözlükte saklanabilir. Özellikleri vardır: __name__, __doc__, __defaults__, __annotations__.

def double(x):
    return x * 2

f = double
print(f(5), f.__name__)                # 10 double
print(list(map(double, [1, 2, 3])))   # [2, 4, 6]

Lambda: isimsiz fonksiyonlar

Lambda, adı olmayan ve gövdesi tek bir ifadeden oluşan kısa bir fonksiyondur. İfadenin sonucu otomatik döndürülür; return yazılmaz.

square = lambda x: x ** 2
print(square(4))   # 16

expenses = [("Kahve", 250), ("Taksi", 600), ("Öğle yemeği", 450)]
expenses.sort(key=lambda e: e[1], reverse=True)
print(expenses)  # [('Taksi', 600), ('Öğle yemeği', 450), ('Kahve', 250)]

print(list(filter(lambda n: n % 2 == 0, range(10))))  # [0, 2, 4, 6, 8]

Lambda, sorted, max, map ve filter gibi fonksiyonlara verilen kısa argümanlar için uygundur. Yukarıdaki square örneğindeki gibi bir lambda'yı bir değişkene atamak PEP 8'e göre önerilmez: bir fonksiyonun adı olacaksa def kullanın; hata izinde anlamlı bir __name__ ve docstring için yer olur.

deflambda
Advaryok (<lambda>)
Gövdeistediğiniz kadar ifadetek ifade
returnaçıkörtük
Docstring, tür ipuçlarıvaryok
Uygun olduğu yerher yerkısa key ya da geri çağırma (callback)

Tür ipuçları: denetim değil, öneri

Tür ipuçları parametrelerin ve sonucun beklenen türlerini tanımlar. Python bunları çalışma zamanında denetlemez: x: int olarak tanımlanmış bir fonksiyon bir metni de kabul eder. Denetimi mypy, pyright ve ty gibi dış araçlar ile editörünüz yapar; hatayı çalıştırmadan gösterirler.

def add_expense(title: str, amount: float, category: str = "diğer") -> dict[str, object]:
    return {"title": title, "amount": amount, "category": category}

def area(w: float, h: float) -> float:
    w * h   # mypy: Missing return statement

Denetimi kurulum gerektirmeden uvx mypy . ile çalıştırabilirsiniz. Yukarıdaki örnek tür ipuçlarının faydasını gösteriyor: -> float ile yazılmış fonksiyonda unutulan return'ü analiz aracı hemen yakalar.

Python 3.12'den itibaren (PEP 695) genel fonksiyonlar TypeVar olmadan yazılır: tür parametresi köşeli parantez içinde verilir, tür takma adı ise type anahtar sözcüğüyle tanımlanır.

def first[T](xs: list[T]) -> T:
    return xs[0]

type Money = int | float

Python 3.14'te (PEP 649) tür ipuçları tembel hâle geldi: ilk erişildiğinde hesaplanırlar. Bu sayede aşağıda tanımlanan bir sınıfa tırnaksız başvurabilirsiniz ve from __future__ import annotations satırına artık gerek yoktur.

"Verilmedi" ile "None verildi" ayrımı: Python 3.15'te sentinel

Varsayılan None değeri, argümanın hiç verilmediğiyle bilerek None verildiğini ayırt etmeye yetmez. Yıllardır bu sorun el yapımı bir işaretçiyle çözülüyordu:

_MISSING = object()

def get_rate(currency, default=_MISSING):
    rates = {"USD": 90.5}
    if currency in rates:
        return rates[currency]
    if default is _MISSING:
        raise KeyError(currency)
    return default

Python 3.15 ile PEP 661 gereği yerleşik sentinel fonksiyonu geldi: MISSING = sentinel("MISSING") okunaklı bir gösterimi olan benzersiz bir işaretçi oluşturur; kopyalandığında kendisi kalır, pickle ile serileştirilir ve float | MISSING gibi bir tür ipucunda kullanılabilir. Eski sürümlerde object() ile yapılan yöntem çalışmaya devam eder.

Fonksiyonlarda tipik hatalar

  • return unutmak — fonksiyon None döndürür.
  • Parantezi unutmak — print(greet) sonucu değil, fonksiyon nesnesini yazdırır.
  • Değiştirilebilir varsayılan değer — tüm çağrılar için ortak liste.
  • Yanlış argüman sayısı — TypeError: greet() missing 1 required positional argument: 'name'.
  • Yerleşik bir adla çakışma — list ya da sum adında bir fonksiyon, yerleşik fonksiyonu gölgeler.

Özet

Python'da fonksiyonlar def ile tanımlanır, sonucu return ile döndürür ve argümanları sırayla ya da isimle alır. *args ve **kwargs esneklik sağlar, / ve * çağrıyı katılaştırır, lambda tek kullanımlık fonksiyonlar için kısalık sağlar, tür ipuçları ise mypy ile birlikte hataları çalıştırmadan yakalar. Kursta bu konu uygulamalı işlenir: harcama takipçisi, tür ipuçlu fonksiyonlara bölünür ve mypy ile denetlenir.

Sık sorulan sorular

Parametre ile argüman arasındaki fark nedir?

Parametre fonksiyonun tanımındaki isimdir; argüman çağrıda verilen somut değerdir. def greet(name) içindeki name parametredir, greet("Ayşe") çağrısındaki "Ayşe" ise argümandır.

Bir fonksiyonu tanımlanmadan önce çağırabilir miyim?

Modül düzeyinde hayır: def satırı önce çalışmalıdır, yoksa NameError alırsınız. Başka bir fonksiyonun içinde ise, çağrı tanım çalıştıktan sonra olduğu sürece, aşağıda tanımlanan bir fonksiyonu çağırabilirsiniz.

Bir fonksiyon kaç değer döndürebilir?

Teknik olarak bir değer; ama bu değer bir demet olabilir: return a, b iki elemanlı bir demet döndürür ve x, y = f() ile açılır.

Tür ipuçlarını yazmak zorunlu mu?

Hayır, Python onlarsız da çalışır. Ancak başkalarının okuyacağı kodda tür ipuçları bakımı kolaylaştırır ve mypy ya da pyright hataları çalıştırmadan bulur.

lambda içinde if kullanabilir miyim?

Yalnızca koşullu ifade kullanabilirsiniz: lambda x: "çift" if x % 2 == 0 else "tek". Lambda içinde komutlar, döngüler ve atamalar kullanılamaz.

Bu konuyu videoyla öğrenin

Bu konu “Python 3.15: от новичка до профи” kursunda uygulamalı olarak ayrıntılı işleniyor.

Kursa git