Python En İyi Pratikler, İpuçları ve Tasarım Desenleri — Kapsamlı Rehber

4 Mart 2026 · netologist · 22 dakika, 4573 kelime ·

Deyimsel (Pythonic), sürdürülebilir, performanslı ve güvenli Python kodu yazmak için principal-engineer seviyesinde bir referans.


İçindekiler

  1. Felsefe & Python’un Zen’i
  2. Kod Stili & PEP 8
  3. İsimlendirme Kuralları
  4. Type Hints & Statik Tipleme
  5. Veri Yapıları — Deyimsel Kullanım
  6. Fonksiyonlar: Tasarım & En İyi Pratikler
  7. Comprehension’lar & Generator İfadeleri
  8. Iterator’lar & Generator’lar
  9. Context Manager’lar
  10. Decorator’lar
  11. Python’da OOP: Sınıfları Doğru Yazmak
  12. Dataclass, NamedTuple & Attrs
  13. Hata Yönetimi & Exception’lar
  14. Modüller, Paketler & Proje Yapısı
  15. Eşzamanlılık: Threading, Multiprocessing, Asyncio
  16. Performans Optimizasyonu
  17. Test Yazma En İyi Pratikleri
  18. Loglama
  19. Güvenlik En İyi Pratikleri
  20. Python’da Tasarım Desenleri
  21. Yaygın Tuzaklar & Anti-Pattern’ler
  22. Araç Ekosistemi
  23. Son Kontrol Listesi

1. Felsefe & Python’un Zen’i

import this komutunu çalıştırın ve içselleştirin. Bazı ilkeler daha derin bir yorumu hak eder:

# Kötü: iç içe geçmiş koşullar
def process(order):
    if order is not None:
        if order.is_valid():
            if order.total > 0:
                return charge(order)
            else:
                return None
        else:
            return None
    else:
        return None

# İyi: guard clause'lar (düz, açık, okunabilir)
def process(order):
    if order is None:
        return None
    if not order.is_valid():
        return None
    if order.total <= 0:
        return None
    return charge(order)

2. Kod Stili & PEP 8

# Import sırası örneği
import os
import sys
from collections import defaultdict

import numpy as np
import requests

from myapp.core import settings
from myapp.utils import helpers
name = "Ada"
# İyi
greeting = f"Hello, {name}!"
# Kaçının
greeting = "Hello, {}!".format(name)
greeting = "Hello, %s!" % name

3. İsimlendirme Kuralları

ÖğeKuralÖrnek
Modülsnake_case, kısadata_loader.py
Paketsnake_case, tercihen alt çizgisizmypackage
SınıfPascalCaseHttpClient
Exception sınıfıPascalCase + Error ekiValidationError
Fonksiyon/metotsnake_case, fiil öbeğicalculate_total()
Değişkensnake_case, isimuser_count
SabitUPPER_SNAKE_CASEMAX_RETRIES = 3
“Private” öznitelikbaşta tek alt çizgiself._cache
Name-mangled öznitelikbaşta çift alt çizgiself.__internal
Kullanılmayan/atılabilir değişkentek alt çizgifor _ in range(5):

Bariz ve dar kapsamlar (döngüde i, j; matematikte x, y) dışında tek harfli isimlerden kaçının. Belirsiz kısaltmalardan kaçının — cfg uygun, cfgr değil.


4. Type Hints & Statik Tipleme

Type hint’ler artık profesyonel kod tabanlarında opsiyonel değildir. Dokümantasyon görevi görürler, IDE otomatik tamamlamayı etkinleştirirler ve mypy/pyright ile hataları çalışma zamanından önce yakalarlar.

from __future__ import annotations
from typing import Optional, Union, Callable, Iterable

def find_user(user_id: int, *, cache: dict[int, "User"] | None = None) -> "User" | None:
    ...

def apply(fn: Callable[[int, int], int], values: Iterable[tuple[int, int]]) -> list[int]:
    return [fn(a, b) for a, b in values]

Ana pratikler:

from typing import Protocol

class SupportsClose(Protocol):
    def close(self) -> None: ...

def cleanup(resource: SupportsClose) -> None:
    resource.close()
from typing import TypedDict

class UserPayload(TypedDict):
    id: int
    name: str
    email: str
from typing import Literal

def set_mode(mode: Literal["r", "w", "a"]) -> None: ...
from typing import TypeVar, Generic

T = TypeVar("T")

class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        return self._items.pop()

5. Veri Yapıları — Deyimsel Kullanım

Doğru konteyneri seçmek

İhtiyaçKullan
Sıralı, değiştirilebilir dizilist
Sıralı, değiştirilemez dizituple
Benzersiz sırasız öğeler, hızlı üyelik testiset
Anahtar-değer eşlemesidict
O(1) uçlu FIFO/LIFO kuyrukcollections.deque
Tekrar sayımıcollections.Counter
Eksik anahtarlarda varsayılan değerlercollections.defaultdict
Sona-taşıma özellikli sıralı eşlemecollections.OrderedDict (3.7+ itibarıyla dict’ler zaten sıralı olduğu için çoğunlukla gereksiz, ama move_to_end için hâlâ kullanışlı)
Hafif, değiştirilemez kayıtNamedTuple veya dataclass(frozen=True)
Birden fazla dict benzeri nesneyi tembelce (lazy) birleştirmecollections.ChainMap
from collections import Counter, defaultdict, deque

# Sayma
word_counts = Counter("the quick brown fox jumps over the lazy dog".split())
print(word_counts.most_common(2))

# Gruplama
groups: defaultdict[str, list[int]] = defaultdict(list)
for n in range(10):
    groups["even" if n % 2 == 0 else "odd"].append(n)

# Kayan pencere / kuyruk
window = deque(maxlen=3)
for x in range(10):
    window.append(x)

Üyelik testleri için set

# Kötü: O(n) üyelik kontrolü
valid_ids = [1, 2, 3, 4, 5]
if user_id in valid_ids:
    ...

# İyi: ortalama O(1)
valid_ids = {1, 2, 3, 4, 5}
if user_id in valid_ids:
    ...

Unpacking (paketten çıkarma)

first, *rest = [1, 2, 3, 4]
*init, last = [1, 2, 3, 4]
a, (b, c) = 1, (2, 3)

# Geçici değişken olmadan takas
a, b = b, a

dict.get, setdefault ve walrus operatörü

value = data.get("key", "default")
data.setdefault("key", []).append(item)

# Walrus operatörü (3.8+) çift hesaplamayı önler
if (n := len(data)) > 10:
    print(f"Too many items: {n}")

6. Fonksiyonlar: Tasarım & En İyi Pratikler

# Kötü — liste çağrılar arasında paylaşılır!
def append_item(item, items=[]):
    items.append(item)
    return items

# İyi
def append_item(item, items: list | None = None):
    if items is None:
        items = []
    items.append(item)
    return items
def create_user(*, name: str, email: str, is_admin: bool = False) -> "User":
    ...

create_user(name="Ada", email="[email protected]", is_admin=True)
def add(a: int, b: int, /) -> int:
    return a + b
def calculate_discount(price: float, percentage: float) -> float:
    """Calculate the discounted price.

    Args:
        price: The original price.
        percentage: Discount percentage (0-100).

    Returns:
        The price after discount is applied.

    Raises:
        ValueError: If percentage is not between 0 and 100.
    """
    if not 0 <= percentage <= 100:
        raise ValueError("percentage must be between 0 and 100")
    return price * (1 - percentage / 100)

7. Comprehension’lar & Generator İfadeleri

Comprehension’lar, .append() ile elle yazılan döngülerden daha Pythonic ve genellikle daha hızlıdır.

# List comprehension
squares = [x**2 for x in range(10)]

# Set comprehension
unique_lengths = {len(word) for word in ["a", "bb", "ccc", "dd"]}

# Dict comprehension
name_to_len = {name: len(name) for name in ["Ada", "Grace", "Alan"]}

# İç içe comprehension (matrisi düzleştirme)
matrix = [[1, 2], [3, 4]]
flat = [x for row in matrix for x in row]

# Koşullu comprehension
evens = [x for x in range(20) if x % 2 == 0]

# Generator ifadesi — tembel (lazy), bellek dostu
total = sum(x**2 for x in range(1_000_000))

Genel kural: comprehension 2’den fazla iç içe geçme seviyesi veya birden fazla if gerektiriyorsa, okunabilirlik için normal bir döngüye veya adlandırılmış bir yardımcı fonksiyona dönüştürün.

# Fazla akıllıca — okunması zor
result = [y for x in data if x > 0 for y in transform(x) if y is not None]

# Daha iyi
def transform_positive(data):
    for x in data:
        if x <= 0:
            continue
        for y in transform(x):
            if y is not None:
                yield y

8. Iterator’lar & Generator’lar

Generator’lar tembel değerlendirmeyi (lazy evaluation) mümkün kılar ve büyük veya sonsuz diziler için bellek kullanımını ciddi şekilde azaltır.

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
first_ten = [next(fib) for _ in range(10)]

Devretme (delegation) için yield from

def flatten(nested):
    for item in nested:
        if isinstance(item, list):
            yield from flatten(item)
        else:
            yield item

list(flatten([1, [2, 3, [4, 5]], 6]))  # [1, 2, 3, 4, 5, 6]

Özel iterator protokolü

class Range:
    def __init__(self, start: int, stop: int) -> None:
        self.current = start
        self.stop = stop

    def __iter__(self) -> "Range":
        return self

    def __next__(self) -> int:
        if self.current >= self.stop:
            raise StopIteration
        value = self.current
        self.current += 1
        return value

Yalnızca bir kez üzerinden geçmeniz gerektiğinde — özellikle I/O pipeline’ları ve akış (streaming) verisinde — tam bir liste oluşturmak yerine generator’ları tercih edin.


9. Context Manager’lar

Kaynak yönetimi (dosyalar, kilitler, bağlantılar, işlemler) için her zaman with kullanın.

with open("data.txt") as f:
    content = f.read()

# Birden fazla context manager
with open("in.txt") as fin, open("out.txt", "w") as fout:
    fout.write(fin.read())

Kendi context manager’ınızı yazmak (sınıf tabanlı)

class Timer:
    def __enter__(self):
        import time
        self.start = time.perf_counter()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        import time
        self.elapsed = time.perf_counter() - self.start
        return False  # exception'ları yaymaya devam et

Kendi context manager’ınızı yazmak (fonksiyon tabanlı, contextlib)

from contextlib import contextmanager

@contextmanager
def timer():
    import time
    start = time.perf_counter()
    try:
        yield
    finally:
        print(f"Elapsed: {time.perf_counter() - start:.4f}s")

with timer():
    do_expensive_work()

try/except/pass yerine contextlib.suppress

from contextlib import suppress

with suppress(FileNotFoundError):
    os.remove("maybe_missing.txt")

Dinamik sayıda context manager için ExitStack

from contextlib import ExitStack

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # tüm dosyalar exception olsa bile çıkışta otomatik kapanır

10. Decorator’lar

Decorator’lar, kaynak kodunu değiştirmeden fonksiyonlara davranış eklemek için onları sarmalar — Açık/Kapalı İlkesi’nin (Open/Closed Principle) klasik uygulamasıdır.

import functools
import time

def timed(func):
    @functools.wraps(func)  # __name__, __doc__ vb. korur
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
        return result
    return wrapper

@timed
def slow_function():
    time.sleep(1)

Argümanlı decorator’lar

def retry(times: int = 3, exceptions: tuple = (Exception,)):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_exc = None
            for attempt in range(times):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exc = e
            raise last_exc
        return wrapper
    return decorator

@retry(times=5, exceptions=(ConnectionError,))
def fetch_data():
    ...

Sınıf tabanlı decorator’lar & önbellekleme (caching)

from functools import lru_cache, cache

@lru_cache(maxsize=128)
def fib(n: int) -> int:
    return n if n < 2 else fib(n - 1) + fib(n - 2)

@cache  # sınırsız önbellek, Python 3.9+
def expensive_lookup(key: str) -> str:
    ...

Her zaman functools.wraps kullanın — atlamak introspection, hata ayıklama araçlarını ve dokümantasyon üreticilerini bozar.


11. Python’da OOP: Sınıfları Doğru Yazmak

class Point:
    __slots__ = ("x", "y")

    def __init__(self, x: float, y: float) -> None:
        self.x = x
        self.y = y
class Point:
    def __init__(self, x: float, y: float) -> None:
        self.x = x
        self.y = y

    def __repr__(self) -> str:
        return f"Point(x={self.x!r}, y={self.y!r})"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Point):
            return NotImplemented
        return (self.x, self.y) == (other.x, other.y)

    def __hash__(self) -> int:
        return hash((self.x, self.y))
class Temperature:
    def __init__(self, celsius: float) -> None:
        self._celsius = celsius

    @property
    def celsius(self) -> float:
        return self._celsius

    @celsius.setter
    def celsius(self, value: float) -> None:
        if value < -273.15:
            raise ValueError("Below absolute zero")
        self._celsius = value

    @property
    def fahrenheit(self) -> float:
        return self._celsius * 9 / 5 + 32
class User:
    def __init__(self, name: str, email: str) -> None:
        self.name = name
        self.email = email

    @classmethod
    def from_dict(cls, data: dict) -> "User":
        return cls(name=data["name"], email=data["email"])

    @staticmethod
    def is_valid_email(email: str) -> bool:
        return "@" in email
from abc import ABC, abstractmethod

class PaymentProcessor(ABC):
    @abstractmethod
    def charge(self, amount: float) -> bool: ...

class StripeProcessor(PaymentProcessor):
    def charge(self, amount: float) -> bool:
        ...

12. Dataclass, NamedTuple & Attrs

__init__, __repr__, __eq__‘i elle yazmak yerine @dataclass kullanmayı tercih edin.

from dataclasses import dataclass, field

@dataclass(frozen=True, slots=True)
class Point:
    x: float
    y: float

@dataclass
class Order:
    items: list[str] = field(default_factory=list)
    total: float = 0.0

    def add_item(self, item: str, price: float) -> None:
        self.items.append(item)
        self.total += price

Unpacking’i de destekleyen hafif, tuple benzeri değiştirilemez kayıtlar için NamedTuple:

from typing import NamedTuple

class Point(NamedTuple):
    x: float
    y: float

p = Point(1.0, 2.0)
x, y = p  # tuple unpacking çalışır

attrs (üçüncü parti, dataclass’lardan öncesine dayanır) gelişmiş validator/converter’lar için bazı takımlar tarafından hâlâ tercih edilir, ancak dataclasses + pydantic (doğrulama için) modern ihtiyaçların çoğunu karşılar.


13. Hata Yönetimi & Exception’lar

# Kötü
try:
    risky()
except:
    pass

# İyi
try:
    risky()
except (ConnectionError, TimeoutError) as e:
    logger.warning("Network issue: %s", e)
    raise
class AppError(Exception):
    """Base exception for this application."""

class ValidationError(AppError):
    """Raised when input validation fails."""

class NotFoundError(AppError):
    """Raised when a resource is not found."""
try:
    parse(data)
except ValueError as e:
    raise ValidationError("Invalid input") from e
try:
    conn = connect()
except ConnectionError:
    logger.error("Could not connect")
else:
    # yalnızca exception fırlatılmadıysa çalışır
    process(conn)
finally:
    # her zaman çalışır — temizlik
    conn.close()
# Pythonic (EAFP)
try:
    value = my_dict["key"]
except KeyError:
    value = default

# Daha az Pythonic (LBYL) — eşzamanlı kodda race condition'a yatkın
if "key" in my_dict:
    value = my_dict["key"]
else:
    value = default

14. Modüller, Paketler & Proje Yapısı

Modern bir Python paketi için önerilen src-layout:

myproject/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── core.py
│       ├── models/
│       │   ├── __init__.py
│       │   └── user.py
│       └── utils/
│           ├── __init__.py
│           └── helpers.py
├── tests/
│   ├── __init__.py
│   ├── test_core.py
│   └── conftest.py
└── docs/
# mypackage/models/user.py içinde
from ..utils.helpers import normalize_email   # göreli, paket içinde
from mypackage.core import Settings           # mutlak, yine de sorun değil

15. Eşzamanlılık: Threading, Multiprocessing, Asyncio

Ne zaman ne kullanılır

İş yüküAraç
I/O-bound (ağ, disk, DB) — çok sayıda çakışan beklemeasyncio veya threading
CPU-bound (ağır hesaplama)multiprocessing veya native uzantılar (NumPy, Cython)
Tam bir async yeniden yazım olmadan basit paralel I/O görevlericoncurrent.futures.ThreadPoolExecutor
Basit paralel CPU görevlericoncurrent.futures.ProcessPoolExecutor

GIL (Global Interpreter Lock), saf Python kodu için thread’lerin CPU paralelliği sağlamadığı anlamına gelir — free-threaded (GIL’siz) bir build kullanmadığınız sürece yalnızca I/O-bound işler thread kullanımından fayda görür.

import asyncio
import aiohttp

async def fetch(session: aiohttp.ClientSession, url: str) -> str:
    async with session.get(url) as resp:
        return await resp.text()

async def main(urls: list[str]) -> list[str]:
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        return await asyncio.gather(*tasks)

asyncio.run(main(["https://example.com"] * 10))

Yapılandırılmış eşzamanlılık için asyncio.TaskGroup (3.11+)

async def main():
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(fetch(url1))
        task2 = tg.create_task(fetch(url2))
    # burada her iki task de garanti olarak tamamlanmıştır; exception'lar ExceptionGroup olarak yayılır

CPU-bound paralellik için ProcessPoolExecutor

from concurrent.futures import ProcessPoolExecutor

def heavy_computation(n: int) -> int:
    return sum(i * i for i in range(n))

with ProcessPoolExecutor() as executor:
    results = list(executor.map(heavy_computation, [10_000_000] * 4))

Windows/spawn tabanlı platformlarda multiprocessing giriş noktalarını her zaman if __name__ == "__main__": ile koruyun.


16. Performans Optimizasyonu

  1. Optimize etmeden önce ölçüncProfile, py-spy veya line_profiler kullanın.
  2. Yerleşik fonksiyonları ve standart kütüphaneyi tercih edin — C’de implemente edilmişlerdir.
  3. Sıcak (hot) döngülerde tekrarlanan öznitelik aramaları yerine yerel değişken referansları kullanın.
  4. Büyük sayısal veri kümeleri için liste yerine array veya NumPy kullanın.
  5. Örnek başına bellek yükünü azaltmak için __slots__ kullanın.
  6. Döngüde string birleştirme: += yerine "".join(parts) kullanın.
# Yavaş: tekrarlanan string kopyalarından dolayı O(n^2)
result = ""
for s in strings:
    result += s

# Hızlı: O(n)
result = "".join(strings)
  1. Pahalı saf hesaplamaları functools.lru_cache ile önbelleğe alın.
  2. Büyük ara listeleri somutlaştırmamak için generator’lar kullanın.
  3. Gerçekten CPU-bound darboğazlar için Cython, Numba veya sıcak yolları Rust’ta (PyO3 ile) yeniden yazmayı düşünün.
  4. Mikro-benchmark’lar için timeit kullanın:
import timeit
timeit.timeit("'-'.join(str(n) for n in range(100))", number=10000)

17. Test Yazma En İyi Pratikleri

def test_calculate_discount():
    assert calculate_discount(100, 10) == 90

def test_calculate_discount_invalid_percentage():
    import pytest
    with pytest.raises(ValueError):
        calculate_discount(100, 150)
import pytest

@pytest.fixture
def sample_user():
    return User(name="Ada", email="[email protected]")

def test_user_email(sample_user):
    assert sample_user.email == "[email protected]"
@pytest.mark.parametrize("price,pct,expected", [
    (100, 0, 100),
    (100, 50, 50),
    (200, 25, 150),
])
def test_discounts(price, pct, expected):
    assert calculate_discount(price, pct) == expected
from unittest.mock import patch

@patch("myapp.services.requests.get")
def test_fetch_user(mock_get):
    mock_get.return_value.json.return_value = {"id": 1, "name": "Ada"}
    result = fetch_user(1)
    assert result["name"] == "Ada"

18. Loglama

Atılabilir betikler dışında hiçbir şey için print() kullanmayın. logging modülünü kullanın.

import logging

logger = logging.getLogger(__name__)

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)

def process_order(order_id: int) -> None:
    logger.info("Processing order %s", order_id)
    try:
        ...
    except Exception:
        logger.exception("Failed to process order %s", order_id)
        raise

19. Güvenlik En İyi Pratikleri

import secrets
token = secrets.token_urlsafe(32)
# Kötü — SQL injection riski
cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")

# İyi — parametreli
cursor.execute("SELECT * FROM users WHERE name = ?", (name,))

20. Python’da Tasarım Desenleri

Python’un dinamik tiplemesi, first-class fonksiyonları ve duck typing özelliği, birçok klasik GoF (Gang of Four) deseninin daha basit veya gereksiz olduğu anlamına gelir — ancak bunları (ve Pythonic karşılıklarını) anlamak yine de önemlidir.

Yaratımsal (Creational) Desenler

Singleton

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

Pythonic alternatif: modülün kendisi zaten bir singleton’dur — modül seviyesi durum, çoğu durumda bir Singleton sınıfından daha basit ve daha deyimseldir.

Factory Method

from abc import ABC, abstractmethod

class Notifier(ABC):
    @abstractmethod
    def send(self, message: str) -> None: ...

class EmailNotifier(Notifier):
    def send(self, message: str) -> None:
        print(f"Email: {message}")

class SmsNotifier(Notifier):
    def send(self, message: str) -> None:
        print(f"SMS: {message}")

def notifier_factory(kind: str) -> Notifier:
    return {"email": EmailNotifier, "sms": SmsNotifier}[kind]()

Abstract Factory

class GUIFactory(ABC):
    @abstractmethod
    def create_button(self) -> "Button": ...
    @abstractmethod
    def create_checkbox(self) -> "Checkbox": ...

class WindowsFactory(GUIFactory):
    def create_button(self): return WindowsButton()
    def create_checkbox(self): return WindowsCheckbox()

class MacFactory(GUIFactory):
    def create_button(self): return MacButton()
    def create_checkbox(self): return MacCheckbox()

Builder

class RequestBuilder:
    def __init__(self) -> None:
        self._url = ""
        self._headers: dict[str, str] = {}
        self._method = "GET"

    def url(self, url: str) -> "RequestBuilder":
        self._url = url
        return self

    def header(self, key: str, value: str) -> "RequestBuilder":
        self._headers[key] = value
        return self

    def method(self, method: str) -> "RequestBuilder":
        self._method = method
        return self

    def build(self) -> dict:
        return {"url": self._url, "headers": self._headers, "method": self._method}

request = (
    RequestBuilder()
    .url("https://api.example.com")
    .header("Authorization", "Bearer token")
    .method("POST")
    .build()
)

Prototype

import copy

class Prototype:
    def clone(self):
        return copy.deepcopy(self)

Yapısal (Structural) Desenler

Adapter

class OldPrinter:
    def print_old(self, text: str) -> None:
        print(f"[OLD] {text}")

class NewPrinterInterface(ABC):
    @abstractmethod
    def print(self, text: str) -> None: ...

class PrinterAdapter(NewPrinterInterface):
    def __init__(self, old_printer: OldPrinter) -> None:
        self._old_printer = old_printer

    def print(self, text: str) -> None:
        self._old_printer.print_old(text)

Decorator (yapısal desen, @decorator sözdizimi değil)

class Coffee(ABC):
    @abstractmethod
    def cost(self) -> float: ...

class SimpleCoffee(Coffee):
    def cost(self) -> float:
        return 2.0

class MilkDecorator(Coffee):
    def __init__(self, coffee: Coffee) -> None:
        self._coffee = coffee

    def cost(self) -> float:
        return self._coffee.cost() + 0.5

coffee = MilkDecorator(SimpleCoffee())
print(coffee.cost())  # 2.5

Facade

class CPU:
    def freeze(self): ...
    def jump(self, position): ...
    def execute(self): ...

class Memory:
    def load(self, position, data): ...

class ComputerFacade:
    def __init__(self):
        self.cpu = CPU()
        self.memory = Memory()

    def start(self):
        self.cpu.freeze()
        self.memory.load(0, "boot_data")
        self.cpu.jump(0)
        self.cpu.execute()

Proxy

class RealImage:
    def __init__(self, filename: str) -> None:
        self.filename = filename
        self._load()

    def _load(self) -> None:
        print(f"Loading {self.filename}")

    def display(self) -> None:
        print(f"Displaying {self.filename}")

class ImageProxy:
    def __init__(self, filename: str) -> None:
        self.filename = filename
        self._real_image = None

    def display(self) -> None:
        if self._real_image is None:
            self._real_image = RealImage(self.filename)  # tembel (lazy) yükleme
        self._real_image.display()

Composite

class Component(ABC):
    @abstractmethod
    def render(self, indent: int = 0) -> str: ...

class Leaf(Component):
    def __init__(self, name: str) -> None:
        self.name = name

    def render(self, indent: int = 0) -> str:
        return "  " * indent + self.name

class Composite(Component):
    def __init__(self, name: str) -> None:
        self.name = name
        self.children: list[Component] = []

    def add(self, component: Component) -> None:
        self.children.append(component)

    def render(self, indent: int = 0) -> str:
        lines = ["  " * indent + self.name]
        for child in self.children:
            lines.append(child.render(indent + 1))
        return "\n".join(lines)

Davranışsal (Behavioral) Desenler

Observer

class Subject:
    def __init__(self) -> None:
        self._observers: list[Callable] = []

    def subscribe(self, observer: Callable) -> None:
        self._observers.append(observer)

    def notify(self, *args, **kwargs) -> None:
        for observer in self._observers:
            observer(*args, **kwargs)

subject = Subject()
subject.subscribe(lambda event: print(f"Received: {event}"))
subject.notify("user_created")

Strategy

class SortStrategy(ABC):
    @abstractmethod
    def sort(self, data: list) -> list: ...

class QuickSort(SortStrategy):
    def sort(self, data: list) -> list:
        return sorted(data)  # basitleştirilmiş

class Sorter:
    def __init__(self, strategy: SortStrategy) -> None:
        self._strategy = strategy

    def sort(self, data: list) -> list:
        return self._strategy.sort(data)

Pythonic alternatif: bir sınıfa sarmak yerine doğrudan bir fonksiyon geçin:

def sort_data(data: list, strategy: Callable[[list], list] = sorted) -> list:
    return strategy(data)

Command

class Command(ABC):
    @abstractmethod
    def execute(self) -> None: ...
    @abstractmethod
    def undo(self) -> None: ...

class AddTextCommand(Command):
    def __init__(self, document: list[str], text: str) -> None:
        self.document = document
        self.text = text

    def execute(self) -> None:
        self.document.append(self.text)

    def undo(self) -> None:
        self.document.remove(self.text)

State

class State(ABC):
    @abstractmethod
    def handle(self, context: "TrafficLight") -> None: ...

class RedState(State):
    def handle(self, context):
        print("Red -> Green")
        context.state = GreenState()

class GreenState(State):
    def handle(self, context):
        print("Green -> Red")
        context.state = RedState()

class TrafficLight:
    def __init__(self):
        self.state: State = RedState()

    def change(self):
        self.state.handle(self)

Template Method

class DataProcessor(ABC):
    def process(self) -> None:
        self.load()
        self.transform()
        self.save()

    @abstractmethod
    def load(self) -> None: ...
    @abstractmethod
    def transform(self) -> None: ...
    @abstractmethod
    def save(self) -> None: ...

class CsvProcessor(DataProcessor):
    def load(self): print("Loading CSV")
    def transform(self): print("Transforming CSV")
    def save(self): print("Saving CSV")

Chain of Responsibility

class Handler(ABC):
    def __init__(self) -> None:
        self._next: Handler | None = None

    def set_next(self, handler: "Handler") -> "Handler":
        self._next = handler
        return handler

    def handle(self, request) -> None:
        if self._next:
            self._next.handle(request)

class AuthHandler(Handler):
    def handle(self, request):
        if not request.get("authenticated"):
            print("Auth failed")
            return
        super().handle(request)

Iterator (dilde __iter__/__next__ ile yerleşik olarak bulunur, bkz. Bölüm 8)


21. Yaygın Tuzaklar & Anti-Pattern’ler

  1. Değiştirilebilir varsayılan argümanlar (Bölüm 6’da ele alındı) — Python’un #1 ayak tuzağı.
  2. Döngülerde geç bağlama (late binding) closure’lar:
# Hata: tüm lambda'lar aynı `i`'yi (son değeri) yakalar
funcs = [lambda: i for i in range(5)]
print([f() for f in funcs])  # [4, 4, 4, 4, 4]

# Çözüm: varsayılan argüman tanım anındaki değeri yakalar
funcs = [lambda i=i: i for i in range(5)]
print([f() for f in funcs])  # [0, 1, 2, 3, 4]
  1. Üzerinde iterasyon yaparken bir listeyi değiştirmek:
# Hata: bazı öğeler atlanır
for item in my_list:
    if condition(item):
        my_list.remove(item)

# Çözüm: bir kopya üzerinde iterasyon yapın veya yeni bir liste oluşturun
my_list = [item for item in my_list if not condition(item)]
  1. Değer eşitliği için == yerine is ile karşılaştırma (is kimliği kontrol eder, değeri değil — yalnızca None, True, False ve sentinel nesneler için güvenlidir).
  2. Exception‘ı çok geniş kapsamda yakalamak, gerçek hataları gizler.
  3. Kompozisyonun daha basit ve esnek olacağı yerlerde kalıtımı aşırı kullanmak.
  4. Kötü modül yapısından kaynaklanan dairesel import’lar.
  5. Tip kontrolleri için isinstance() yerine type() kullanmak, bu polimorfizmi bozar:
# Kötü
if type(obj) == list:
    ...

# İyi
if isinstance(obj, list):
    ...
  1. Dosyalar/kilitler/bağlantılar için context manager’ları göz ardı etmek, kaynak sızıntısı riski taşır.
  2. Global değiştirilebilir durum, eşzamanlı bağlamlarda kodu test etmeyi ve anlamayı zorlaştırır.
  3. Uygun polimorfizm/enum yerine string tabanlı dispatch:
# Kırılgan
if shape_type == "circle":
    ...
elif shape_type == "square":
    ...

# Daha iyi: Enum + dispatch dict veya sınıflar aracılığıyla polimorfizm
from enum import Enum, auto

class ShapeType(Enum):
    CIRCLE = auto()
    SQUARE = auto()

22. Araç Ekosistemi

AmaçAraç
Biçimlendirmeblack, ruff format
Lintingruff, flake8, pylint
Statik tiplememypy, pyright
Import sıralamaisort (veya ruff‘ın yerleşik özelliği)
Testpytest, hypothesis (property-based testing)
Kapsam (coverage)coverage.py
Bağımlılık yönetimipoetry, uv, pip-tools
Güvenlik taramasıbandit, pip-audit
Pre-commit hook’larıpre-commit
Dokümantasyonsphinx, mkdocs
Veri doğrulamapydantic
Görev çalıştırıcınox, tox, make

Yapılandırılmış araçlarla minimal bir pyproject.toml:

[project]
name = "mypackage"
version = "0.1.0"
requires-python = ">=3.11"

[tool.ruff]
line-length = 88
select = ["E", "F", "I", "UP", "B"]

[tool.mypy]
strict = true

[tool.pytest.ini_options]
testpaths = ["tests"]

23. Son Kontrol Listesi


Bu belge, 2026 yılı başı itibarıyla modern Python (3.10+) deyimlerini yansıtmaktadır. Dil özellikleri gelişmeye devam etmektedir — her zaman hedeflediğiniz Python sürümünün değişiklik günlüğünü (changelog) kontrol edin.