Proje Yapısı ve __init__.py
Tek dosyalık script'ler küçük işler için yeterli. Ama projen büyüdüğünde, başkalarıyla çalıştığında veya paketini PyPI'da yayınlamak istediğinde düzgün bir proje yapısına ihtiyacın var. Bu derste Python projelerinin nasıl organize edildiğini, modern araçları ve paket dağıtımı temellerini öğreneceğiz.
Neden Proje Yapısı Önemli?
Şu kodu düşün:
projem/
├── main.py (800 satır)
├── utils.py (500 satır)
├── test.py (200 satır)
├── veri.json
└── requirements.txtBu yapı küçük projeler için çalışır. Ama proje büyüdüğünde:
main.py3000 satıra ulaşır — aradığını bulamazsınutils.pyher şeyin çöplüğü olurTestler ana kodla karışır
Başka biri projeyi gördüğünde ne olduğunu anlamaz
🏗️ Bina Planı Analojisi
Proje yapısını bir bina planı gibi düşün. Küçük bir kulübe inşa ediyorsan plan olmadan da olur. Ama apartman yapıyorsan plan şart — temeller, katlar, borular, elektrik hepsi planlı olmalı. Yoksa bir katı değiştirmeye çalışırken diğer kat çöker.
İyi bir proje yapısı:
Okunabilirlik: Yeni geliştirici projeyi hızla anlar
Bakım kolaylığı: Değişiklik yapmak kolay
Test edilebilirlik: Testler kolay yazılır ve çalıştırılır
Dağıtılabilirlik: Paket olarak paylaşılabilir
Temel Proje Yapısı
Python projelerinin standart yapısı şöyledir:
myproject/
├── myproject/ # Kaynak kodu (paket)
│ ├── __init__.py # Paket tanımlayıcı
│ ├── core.py # Ana iş mantığı
│ ├── utils.py # Yardımcı fonksiyonlar
│ └── config.py # Yapılandırma
├── tests/ # Testler
│ ├── __init__.py
│ ├── test_core.py
│ └── test_utils.py
├── docs/ # Dokümantasyon
│ └── index.md
├── pyproject.toml # Proje metadata ve yapılandırma
├── requirements.txt # Bağımlılıklar
├── README.md # Proje açıklaması
├── LICENSE # Lisans
└── .gitignore # Git ignore kurallarıHer öğenin rolünü inceleyelim.
__init__.py: Paketi Tanımla
__init__.py bir dizini Python paketi yapar. Bu dosya paket import edildiğinde çalışır.
Boş __init__.py
En basit hali — sadece dizini paket olarak işaretler:
# myproject/__init__.py
# Boş dosya — sadece dizini paket yaparPublic API Tanımlama
Daha kullanışlı hali — önemli isimleri dışarı açar:
# myproject/__init__.py
"""MyProject — Harika bir Python paketi."""
__version__ = "1.0.0"
__author__ = "Ali Yılmaz"
from .core import Engine, process_data
from .utils import format_output, validate_input
__all__ = [
"Engine",
"process_data",
"format_output",
"validate_input"
]Bu sayede kullanıcılar şöyle import yapabilir:
# Kolay kullanım — __init__.py sayesinde
from myproject import Engine, process_data
# __init__.py olmasaydı:
from myproject.core import Engine
from myproject.core import process_dataAlt Paketlerde __init__.py
myproject/
├── __init__.py
├── models/
│ ├── __init__.py
│ ├── user.py
│ └── product.py
└── services/
├── __init__.py
├── auth.py
└── payment.py# myproject/models/__init__.py
from .user import User, UserProfile
from .product import Product, Category
# Kullanım: from myproject.models import User💡 İpucu:
__init__.py'da çok fazla import yapmaktan kaçın. Büyük paketlerde bu dosya karmaşıklaşabilir ve circular import sorunlarına yol açabilir. Sadece en önemli public API'yi açığa çıkar.
pyproject.toml: Modern Proje Yapılandırma
pyproject.toml Python projelerinin tek yapılandırma dosyasıdır (PEP 518, PEP 621). Eskiden setup.py, setup.cfg, MANIFEST.in gibi birden fazla dosya gerekiyordu — artık pyproject.toml hepsini birleştirir.
Temel pyproject.toml
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.backends._legacy:_Backend"
[project]
name = "myproject"
version = "1.0.0"
description = "Harika bir Python paketi"
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.9"
authors = [
{name = "Ali Yılmaz", email = "ali@example.com"}
]
keywords = ["utility", "tools"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"requests>=2.28.0",
"click>=8.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=23.0",
"mypy>=1.0",
]
docs = [
"sphinx>=7.0",
"sphinx-rtd-theme",
]
[project.urls]
Homepage = "https://github.com/ali/myproject"
Documentation = "https://myproject.readthedocs.io"
Repository = "https://github.com/ali/myproject"
Issues = "https://github.com/ali/myproject/issues"
[project.scripts]
myproject = "myproject.cli:main"pyproject.toml Bölümleri
| Bölüm | Açıklama |
|---|---|
[build-system] | Derleme araçları (setuptools, wheel) |
[project] | Proje metadata (ad, versiyon, bağımlılıklar) |
[project.optional-dependencies] | Opsiyonel bağımlılıklar (dev, test, docs) |
[project.urls] | Proje linkleri |
[project.scripts] | CLI komutları (entry points) |
setup.py vs pyproject.toml
Eski Yöntem: setup.py
# setup.py (ESKİ — kullanma)
from setuptools import setup, find_packages
setup(
name="myproject",
version="1.0.0",
description="Harika bir paket",
author="Ali Yılmaz",
packages=find_packages(),
install_requires=[
"requests>=2.28.0",
"click>=8.0.0",
],
python_requires=">=3.9",
)Modern Yöntem: pyproject.toml
Yukarıda gösterilen TOML formatı. Avantajları:
Deklaratif: Python kodu çalıştırmaya gerek yok
Standart: PEP 621 ile resmi standart
Birleşik: Tüm araç yapılandırmaları tek dosyada
Güvenli: Rastgele kod çalıştırma riski yok
# Araç yapılandırmaları da pyproject.toml'da
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
[tool.black]
line-length = 88
target-version = ["py39"]
[tool.mypy]
python_version = "3.9"
warn_return_any = true
warn_unused_configs = true
[tool.ruff]
line-length = 88
select = ["E", "F", "W", "I"]⚠️ Dikkat: Yeni projelerde her zaman
pyproject.tomlkullan.setup.pygeriye dönük uyumluluk için hâlâ destekleniyor ama artık önerilmiyor. Bazı eski projelerde görebilirsin — modernize etmek iyi bir alışkanlık.
Entry Points: CLI Komutları
[project.scripts] bölümü ile paketini bir komut satırı aracına dönüştürebilirsin:
[project.scripts]
myproject = "myproject.cli:main"Bu yapılandırma pip install myproject yapıldığında myproject komutunu terminalde kullanılabilir hale getirir.
# myproject/cli.py
"""Komut satırı arayüzü."""
import click
@click.command()
@click.option("--name", "-n", default="Dünya", help="Selamlanacak isim")
@click.option("--count", "-c", default=1, help="Tekrar sayısı")
def main(name, count):
"""MyProject CLI aracı."""
for _ in range(count):
print(f"Merhaba, {name}!")
if __name__ == "__main__":
main()# Paketi yükle (geliştirme modunda)
pip install -e .
# Artık terminalde kullanabilirsin
myproject --name Ali --count 3
# Merhaba, Ali!
# Merhaba, Ali!
# Merhaba, Ali!click Olmadan (argparse ile)
# myproject/cli.py
import argparse
from myproject.core import process_data
def main():
parser = argparse.ArgumentParser(description="MyProject CLI")
parser.add_argument("input_file", help="Girdi dosyası")
parser.add_argument("-o", "--output", default="output.txt", help="Çıktı dosyası")
parser.add_argument("-v", "--verbose", action="store_true", help="Detaylı çıktı")
args = parser.parse_args()
if args.verbose:
print(f"İşleniyor: {args.input_file}")
result = process_data(args.input_file)
with open(args.output, "w") as f:
f.write(result)
print(f"Sonuç yazıldı: {args.output}")
if __name__ == "__main__":
main()Setuptools Temelleri
Setuptools Python paketlerini derlemek ve dağıtmak için standart araçtır.
Paketi Yükleme (Geliştirme Modu)
# Editable mode — kodu değiştirdiğinde yeniden yükleme gerekmez
pip install -e .
# Normal yükleme
pip install .-e (editable) modu geliştirme sırasında çok kullanışlı. Kaynak kodda değişiklik yapınca otomatik yansır — sürekli pip install yapmana gerek kalmaz.
Paket Derleme
# Build araçlarını yükle
pip install build
# Paketi derle
python -m build
# Oluşan dosyalar:
# dist/
# ├── myproject-1.0.0.tar.gz (source distribution)
# └── myproject-1.0.0-py3-none-any.whl (wheel - binary)find_packages() / find_namespace_packages()
Setuptools hangi dizinlerin paket olduğunu otomatik bulabilir:
# pyproject.toml
[tool.setuptools.packages.find]
where = ["."]
include = ["myproject*"]
exclude = ["tests*"]Veya setup.py ile:
from setuptools import setup, find_packages
setup(
packages=find_packages(exclude=["tests", "tests.*"]),
)MANIFEST.in: Paket Dağıtımı
Kaynak dağıtımına (sdist) hangi dosyaların dahil edileceğini kontrol eder:
# MANIFEST.in
include LICENSE
include README.md
include pyproject.toml
recursive-include myproject *.py *.pyi
recursive-include tests *.py
exclude .gitignore
prune docs/_build
prune .githubModern setuptools ile çoğu dosya otomatik dahil edilir ama özel dosyalar için MANIFEST.in hâlâ gerekebilir.
# pyproject.toml alternatifi
[tool.setuptools.package-data]
myproject = ["data/*.json", "templates/*.html"]src Layout vs Flat Layout
Python projelerinde iki yaygın dizin yapısı var:
Flat Layout (Düz)
myproject/
├── myproject/
│ ├── __init__.py
│ ├── core.py
│ └── utils.py
├── tests/
│ └── test_core.py
└── pyproject.tomlAvantajları:
Basit ve anlaşılır
Daha az dizin seviyesi
Küçük-orta projeler için ideal
Dezavantajı:
Proje dizininden
import myprojectyapabilirsin — ama bu yüklü paketi değil, yerel dizini import eder. Bu kafa karıştırabilir.
src Layout
myproject/
├── src/
│ └── myproject/
│ ├── __init__.py
│ ├── core.py
│ └── utils.py
├── tests/
│ └── test_core.py
└── pyproject.tomlAvantajları:
src/dizini Python path'inde olmadığı için kazara yerel import yapılamazpip install -e .ile kurulmuş paketi kullanmaya zorlarBüyük ve karmaşık projeler için daha güvenli
Dezavantajı:
Biraz daha fazla dizin seviyesi
Hangisini Seçmeli?
Başlangıç / küçük proje: Flat layout
Kütüphane / açık kaynak proje: src layout
Takım projesi: src layout (daha güvenli)
# src layout için pyproject.toml
[tool.setuptools.packages.find]
where = ["src"]Gerçek Dünya Proje Yapısı Örnekleri
Küçük Kütüphane
textutils/
├── src/
│ └── textutils/
│ ├── __init__.py
│ ├── clean.py
│ ├── analyze.py
│ └── transform.py
├── tests/
│ ├── test_clean.py
│ ├── test_analyze.py
│ └── test_transform.py
├── pyproject.toml
├── README.md
├── LICENSE
└── .gitignoreWeb Uygulaması (Flask)
webapp/
├── webapp/
│ ├── __init__.py # Flask app factory
│ ├── config.py # Yapılandırma
│ ├── models/
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── post.py
│ ├── routes/
│ │ ├── __init__.py
│ │ ├── auth.py
│ │ └── api.py
│ ├── services/
│ │ ├── __init__.py
│ │ ├── auth_service.py
│ │ └── email_service.py
│ ├── templates/
│ │ ├── base.html
│ │ └── index.html
│ └── static/
│ ├── css/
│ └── js/
├── tests/
│ ├── conftest.py # Test fixtures
│ ├── test_auth.py
│ └── test_api.py
├── migrations/ # Veritabanı migration'ları
├── pyproject.toml
├── requirements.txt
├── .env.example # Örnek env dosyası
├── Dockerfile
├── docker-compose.yml
└── README.mdCLI Aracı
mytool/
├── src/
│ └── mytool/
│ ├── __init__.py
│ ├── cli.py # Click/argparse komutları
│ ├── core.py # İş mantığı
│ ├── config.py # Yapılandırma
│ └── output.py # Çıktı formatlama
├── tests/
│ ├── test_cli.py
│ └── test_core.py
├── pyproject.toml
├── README.md
└── LICENSEVeri Bilimi Projesi
datascience_project/
├── data/
│ ├── raw/ # Ham veri (gitignore)
│ ├── processed/ # İşlenmiş veri
│ └── external/ # Dış kaynaklar
├── notebooks/
│ ├── 01_exploration.ipynb
│ ├── 02_cleaning.ipynb
│ └── 03_modeling.ipynb
├── src/
│ └── project/
│ ├── __init__.py
│ ├── data_loader.py
│ ├── preprocessing.py
│ ├── features.py
│ └── models.py
├── tests/
│ └── test_preprocessing.py
├── models/ # Eğitilmiş model dosyaları
├── reports/
│ └── figures/
├── pyproject.toml
├── requirements.txt
└── README.mdPratik: Sıfırdan Proje Oluşturma
Adım adım bir proje oluşturalım:
1. Dizin Yapısını Oluştur
mkdir -p calculator/src/calculator
mkdir -p calculator/tests
cd calculator2. Kaynak Kodunu Yaz
# src/calculator/__init__.py
"""Calculator — Basit hesap makinesi paketi."""
__version__ = "0.1.0"
from .operations import add, subtract, multiply, divide# src/calculator/operations.py
"""Temel matematiksel işlemler."""
def add(a, b):
"""İki sayıyı toplar."""
return a + b
def subtract(a, b):
"""İki sayıyı çıkarır."""
return a - b
def multiply(a, b):
"""İki sayıyı çarpar."""
return a * b
def divide(a, b):
"""İki sayıyı böler."""
if b == 0:
raise ZeroDivisionError("Sıfıra bölünemez!")
return a / b# src/calculator/cli.py
"""Komut satırı arayüzü."""
import argparse
from .operations import add, subtract, multiply, divide
OPERATIONS = {
"add": add,
"sub": subtract,
"mul": multiply,
"div": divide,
}
def main():
parser = argparse.ArgumentParser(description="Calculator CLI")
parser.add_argument("operation", choices=OPERATIONS.keys(),
help="İşlem türü")
parser.add_argument("a", type=float, help="Birinci sayı")
parser.add_argument("b", type=float, help="İkinci sayı")
args = parser.parse_args()
try:
result = OPERATIONS[args.operation](args.a, args.b)
print(f"Sonuç: {result}")
except ZeroDivisionError as e:
print(f"Hata: {e}")
exit(1)
if __name__ == "__main__":
main()3. Testleri Yaz
# tests/test_operations.py
"""Temel işlem testleri."""
import pytest
from calculator import add, subtract, multiply, divide
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
assert add(0, 0) == 0
def test_subtract():
assert subtract(5, 3) == 2
assert subtract(0, 5) == -5
def test_multiply():
assert multiply(3, 4) == 12
assert multiply(-2, 3) == -6
assert multiply(0, 100) == 0
def test_divide():
assert divide(10, 2) == 5.0
assert divide(7, 2) == 3.5
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
divide(10, 0)4. pyproject.toml
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "calculator"
version = "0.1.0"
description = "Basit hesap makinesi"
requires-python = ">=3.9"
license = {text = "MIT"}
[project.scripts]
calc = "calculator.cli:main"
[tool.setuptools.packages.find]
where = ["src"]
[tool.pytest.ini_options]
testpaths = ["tests"]5. Diğer Dosyalar
# README.md
# Calculator
Basit bir hesap makinesi paketi.
## Kurulum
```bash
pip install -e .Kullanım
from calculator import add, multiply
print(add(2, 3)) # 5
print(multiply(4, 5)) # 20CLI
calc add 2 3 # Sonuç: 5.0
calc mul 4 5 # Sonuç: 20.0
```gitignore
# .gitignore
__pycache__/
*.pyc
*.pyo
*.egg-info/
dist/
build/
.venv/
.pytest_cache/
.mypy_cache/6. Kur ve Test Et
# Virtual environment
python -m venv .venv
source .venv/bin/activate
# Geliştirme modunda kur
pip install -e ".[dev]"
# Testleri çalıştır
pytest -v
# CLI'ı dene
calc add 10 5
calc div 10 3PyPI'da Yayınlama (Kısaca)
Paketini dünya ile paylaşmak istersen:
# Build araçlarını yükle
pip install build twine
# Paketi derle
python -m build
# PyPI'a yükle (hesap gerekli)
twine upload dist/*
# Test PyPI'a yükle (denemek için)
twine upload --repository testpypi dist/*Artık herkes pip install myproject ile paketini yükleyebilir.
Özet
Bu derste Python proje yapılandırmasını ve paket oluşturmayı öğrendik:
Standart proje yapısı: kaynak kodu (
myproject/), testler (tests/), yapılandırma (pyproject.toml), dokümantasyon (README.md) ayrı dizinlerde organize edilir.`__init__.py` bir dizini Python paketi yapar. Public API'yi burada tanımla (
__all__, import'lar) ama aşırıya kaçma.`pyproject.toml` modern Python projelerinin tek yapılandırma dosyası. Proje metadata, bağımlılıklar, araç ayarları ve CLI entry point'ler burada tanımlanır.
setup.pyyerine bunu kullan.Entry points (
[project.scripts]) ile paketini CLI aracına dönüştürebilirsin.pip install -e .ile geliştirme modunda kur.src layout büyük projeler için daha güvenli (kazara yerel import engellenir), flat layout küçük projeler için daha basit. Başlangıçta flat layout, büyüdükçe src layout'a geç.
Gerçek dünya projelerinde models, services, routes gibi alt paketler kullanılır. Proje türüne göre (web app, CLI, veri bilimi) yapı farklılık gösterir ama temel prensipler aynı: ayır, organize et, test et.
AI Asistan
Sorularını yanıtlamaya hazır