Modül ve Import Sistemi
Programın büyüdükçe her şeyi tek dosyaya yazmak sürdürülemez hale gelir. 500 satırlık bir dosyada kaybolursun, fonksiyon isimlerini hatırlayamazsın, farklı projelerde aynı kodu tekrar yazarsın. İşte modüller tam bu sorunu çözer.
Modül Nedir?
Modül, Python kodunu içeren bir dosyadır. Her .py dosyası bir modüldür. Bu kadar basit.
🧰 Araç Kutusu Analojisi
Modülleri bir araç kutusu gibi düşün. Evde tamir yapacaksan tüm aletleri bir torbaya atmazsın — tornavida takımı bir bölmede, çekiçler bir bölmede, boya malzemeleri ayrı bir kutuda. Her kutunun içinde ne olduğunu bilirsin ve sadece ihtiyacın olanı alırsın.
Python modülleri de böyle:
mathkutusu → matematiksel aletleroskutusu → işletim sistemi aletlerijsonkutusu → JSON işleme aletlerisenin_modul.py→ senin özel aletlerin
Her alete tek tek erişebilirsin (from math import sqrt) ya da tüm kutuyu alabilirsin (import math).
Kendi Modülünü Oluştur
# matematik_yardimci.py
"""Matematik yardımcı fonksiyonları."""
PI = 3.14159265358979
def daire_alan(yaricap):
"""Dairenin alanını hesaplar."""
return PI * yaricap ** 2
def daire_cevre(yaricap):
"""Dairenin çevresini hesaplar."""
return 2 * PI * yaricap
def faktoriyel(n):
"""n! hesaplar."""
if n <= 1:
return 1
return n * faktoriyel(n - 1)
def asal_mi(sayi):
"""Sayının asal olup olmadığını kontrol eder."""
if sayi < 2:
return False
for i in range(2, int(sayi ** 0.5) + 1):
if sayi % i == 0:
return False
return TrueBu dosya artık bir modül. Başka dosyalardan kullanabiliriz.
import Yöntemleri
Python'da modül import etmenin birkaç yolu var. Her birinin yeri ve zamanı farklı.
1. import modül_adı
import matematik_yardimci
alan = matematik_yardimci.daire_alan(5)
print(f"Alan: {alan}")
cevre = matematik_yardimci.daire_cevre(5)
print(f"Çevre: {cevre}")
print(f"PI: {matematik_yardimci.PI}")Tüm modülü import eder. Her kullanımda modül_adı.fonksiyon yazarsın. Avantajı: Nereden geldiği açık, isim çakışması olmaz.
2. from modül import öğeler
from matematik_yardimci import daire_alan, PI
alan = daire_alan(5) # Doğrudan kullanabilirsin
print(f"PI: {PI}")
# faktoriyel(5) # NameError — import etmedik!Sadece ihtiyacın olan öğeleri import eder. Kısa ve okunabilir. Ama dikkat: aynı isimde başka bir fonksiyonunuz varsa çakışma olur.
3. from modül import * (Kaçın!)
from matematik_yardimci import *
# Tüm public isimler import edilir
alan = daire_alan(5)
print(asal_mi(17))Bu kullanımdan kaçın. Neden? Hangi isimlerin import edildiğini bilemezsin, isim çakışmaları oluşabilir, IDE'ler kodu analiz edemez.
# Tehlikeli senaryo
from modül_a import * # count() fonksiyonu var
from modül_b import * # count() fonksiyonu da burada var!
count() # Hangisi çalışır? modül_b'ninki!4. as ile Takma Ad (Alias)
import matematik_yardimci as mat
alan = mat.daire_alan(5)
print(mat.PI)
# Yaygın alias'lar
import numpy as np
import pandas as pd
import matplotlib.pyplot as pltUzun modül isimlerini kısaltmak için kullanılır. Topluluğun benimsediği standart kısaltmalar var (np, pd, plt).
# from ... import ... as da çalışır
from matematik_yardimci import daire_alan as alan_hesapla
sonuc = alan_hesapla(10)Import Stili Önerileri
# DOĞRU — standart kütüphaneler, üçüncü parti, yerel modüller ayrı gruplar
import os
import sys
import json
import requests
import numpy as np
from matematik_yardimci import daire_alan
from veritabani import baglan
# YANLIŞ — karışık import
from os import *
import requests, json, sys
from matematik_yardimci import *PEP 8'e göre import'lar şu sırada ve gruplar halinde yazılır:
Standart kütüphane (
os,sys,json)Üçüncü parti paketler (
requests,numpy)Yerel modüller (
matematik_yardimci)
Her grup arasında boş satır bırak.
__name__ == "__main__" Pattern'i
Python'da en sık karşılaşacağın pattern'lerden biri:
if __name__ == "__main__":
# Bu kod sadece dosya doğrudan çalıştırıldığında çalışır
passAma bu ne demek?
__name__ Değişkeni
Her Python dosyasının özel bir __name__ değişkeni var:
Dosya doğrudan çalıştırılırsa:
__name__="__main__"Dosya import edilirse:
__name__= modül adı
# hesapla.py
print(f"__name__ = {__name__}")
def topla(a, b):
return a + b
if __name__ == "__main__":
# Test kodu — sadece doğrudan çalışınca
print(topla(3, 5))# Doğrudan çalıştır
$ python hesapla.py
__name__ = __main__
8
# Başka dosyadan import et
$ python -c "import hesapla"
__name__ = hesapla
# topla(3, 5) ÇALIŞMAZ — if bloğu devreye girmezNeden Kullanılır?
# veritabani.py
"""Veritabanı yardımcı modülü."""
def baglan(host, port):
"""Veritabanına bağlanır."""
print(f"Bağlanılıyor: {host}:{port}")
return {"baglanti": True}
def sorgu_calistir(baglanti, sql):
"""SQL sorgusu çalıştırır."""
print(f"Sorgu: {sql}")
return []
# Bu pattern olmadan test kodu her import'ta çalışır!
if __name__ == "__main__":
# Modülü test et
conn = baglan("localhost", 5432)
sonuc = sorgu_calistir(conn, "SELECT * FROM users")
print(f"Sonuç: {sonuc}")if __name__ == "__main__": olmadan baglan() ve sorgu_calistir() test kodları, modül import edildiğinde de çalışırdı. Bu pattern ile test kodunu izole edersin.
Bu pattern ayrıca bir dosyanın hem modül hem de script olarak kullanılmasını sağlar. Çok yaygın bir Python convention'ıdır.
Modül Arama Sırası: sys.path
import modül yazdığında Python bu modülü nerede arar?
Önce mevcut dizin (script'in bulunduğu klasör)
PYTHONPATHortam değişkenindeki dizinlerStandart kütüphane dizinleri
Site-packages (pip ile yüklenen paketler)
import sys
# Python'ın modül aradığı dizinleri gör
for yol in sys.path:
print(yol)Çıktı gibi bir şey olur:
/home/kullanici/projeler/uygulama ← mevcut dizin
/usr/lib/python3.11
/usr/lib/python3.11/lib-dynload
/home/kullanici/.local/lib/python3.11/site-packages
/usr/lib/python3.11/site-packagessys.path'e Dizin Ekleme
import sys
# Runtime'da dizin ekle
sys.path.insert(0, "/home/kullanici/ozel_moduller")
# Şimdi o dizindeki modülleri import edebilirsin
import ozel_modulBu çalışır ama iyi bir pratik değil. Daha iyi yöntemler:
Paketi
pip install -e .ile kur (editable mode)PYTHONPATHortam değişkenini ayarlaDüzgün proje yapısı kullan (bunu ileride göreceğiz)
Modül Bulunamazsa
try:
import olmayan_modul
except ModuleNotFoundError:
print("Modül bulunamadı! pip install ile yükleyin.")Üçüncü parti paketlerin yüklü olup olmadığını kontrol etmek için kullanışlı.
dir() ile Modül İçeriğini Keşfetme
dir() fonksiyonu bir modülün (veya herhangi bir nesnenin) tüm isimlerini listeler:
import math
# math modülündeki tüm isimler
print(dir(math))
# ['__doc__', '__name__', ..., 'ceil', 'cos', 'e', 'exp', 'floor',
# 'log', 'pi', 'pow', 'sin', 'sqrt', 'tan', ...]__ ile başlayanlar özel isimler. Bunları filtreleyebilirsin:
import json
# Sadece public isimleri göster
public = [isim for isim in dir(json) if not isim.startswith("_")]
print(public)
# ['JSONDecodeError', 'JSONDecoder', 'JSONEncoder', 'dump', 'dumps', 'load', 'loads', ...]help() ile Dokümantasyon
import math
# Modül hakkında bilgi
help(math)
# Belirli fonksiyon hakkında bilgi
help(math.sqrt)
# sqrt(x, /)
# Return the square root of x.Modül Metadata'sı
import json
print(json.__name__) # json
print(json.__file__) # /usr/lib/python3.11/json/__init__.py
print(json.__doc__[:80]) # JSON (JavaScript Object Notation)...
print(json.__version__) # Bazı modüllerde var — hepsi değil__all__: Public API Tanımlama
from modül import * kullanıldığında hangi isimlerin import edileceğini __all__ listesi belirler:
# yardimcilar.py
"""Yardımcı fonksiyonlar modülü."""
__all__ = ["topla", "cikar", "carp"] # Sadece bunlar public
def topla(a, b):
return a + b
def cikar(a, b):
return a - b
def carp(a, b):
return a * b
def _dahili_yardimci():
"""Bu private — dışarıdan kullanılmamalı."""
pass
def gizli_fonksiyon():
"""__all__'da yok — from * ile import edilmez."""
passfrom yardimcilar import *
topla(1, 2) # ✅ Çalışır — __all__'da var
cikar(5, 3) # ✅ Çalışır
gizli_fonksiyon() # ❌ NameError — __all__'da yok
_dahili_yardimci() # ❌ NameError — private ve __all__'da yokAma doğrudan import hâlâ çalışır:
from yardimcilar import gizli_fonksiyon # ✅ Çalışır!__all__ sadece from ... import *'ı etkiler. Doğrudan import'u engellemez — bu Python'ın felsefesi: "Hepimiz yetişkiniz."
💡 İpucu: Her modülde
__all__tanımlamak iyi bir pratik. Bu, modülünün public API'sini açıkça belirtir vefrom ... import *kullanımını güvenli hale getirir. Ama yine defrom ... import *yerine açık import tercih et.
Circular Import Sorunu ve Çözümleri
İki modül birbirini import ederse circular import oluşur:
# modül_a.py
from modül_b import fonksiyon_b
def fonksiyon_a():
return "A"
# modül_b.py
from modül_a import fonksiyon_a
def fonksiyon_b():
return "B"Bu çalışmaz! Python modülü yüklerken diğerini import etmeye çalışır, ama o da ilkini bekler — kısır döngü.
Çözüm 1: Import'u Fonksiyon İçine Taşı
# modül_a.py
def fonksiyon_a():
from modül_b import fonksiyon_b # Lazy import
return fonksiyon_b() + " ve A"Import ihtiyaç anında yapılır. Performans kaybı ihmal edilebilir (Python modülü cache'ler).
Çözüm 2: Modül Yapısını Yeniden Düzenle
# Ortak kodları üçüncü bir modüle taşı
# ortak.py
def ortak_fonksiyon():
return "ortak"
# modül_a.py
from ortak import ortak_fonksiyon
# modül_b.py
from ortak import ortak_fonksiyonÇözüm 3: Import'u Dosya Sonunda Yap
# modül_a.py
def fonksiyon_a():
return "A"
# Dosya sonunda import — tüm tanımlamalar yapıldıktan sonra
from modül_b import fonksiyon_bEn iyi çözüm genellikle Çözüm 2 — modül yapısını yeniden düzenlemek. Circular import genellikle kötü tasarımın işaretidir.
Relative Import
Paket (package) içindeki modüller birbirini relative import ile import edebilir:
mypackage/
├── __init__.py
├── core.py
├── utils.py
└── sub/
├── __init__.py
└── helper.py# mypackage/core.py
from . import utils # Aynı dizindeki modül
from .utils import format_tarih # Aynı dizindeki modülden fonksiyon
from .sub import helper # Alt paketten modül
from .sub.helper import yardim # Alt paketten fonksiyon# mypackage/sub/helper.py
from .. import core # Üst dizindeki modül
from ..utils import format_tarih # Üst dizindeki modülden fonksiyon.= mevcut paket (dizin)..= üst paket...= iki üst paket (nadiren kullanılır)
Relative vs Absolute Import
# Absolute import — tam yol
from mypackage.utils import format_tarih
from mypackage.sub.helper import yardim
# Relative import — göreli yol
from .utils import format_tarih
from .sub.helper import yardimPEP 8 absolute import'u tercih eder — daha açık ve okunabilir. Ama paket içi import'larda relative import da kabul edilebilir, özellikle paket adı uzunsa veya paket yeniden adlandırılabiliyorsa.
⚠️ Dikkat: Relative import sadece paket içinde çalışır. Doğrudan
python dosya.pyile çalıştırılan script'lerde relative import çalışmaz. Script'i paket içinden çalıştırmak içinpython -m paket.modülkullan.
reload(): Modülü Yeniden Yükleme
Python bir modülü ilk import'ta yükler ve cache'ler. Aynı modülü tekrar import etsen bile yeniden yüklenmez:
import matematik_yardimci
# ... matematik_yardimci.py dosyasını değiştirdin ...
import matematik_yardimci # YENİDEN YÜKLEMEZ — cache'den gelirGeliştirme sırasında (özellikle REPL/Jupyter'da) modülü değiştirip yeniden yüklemek isteyebilirsin:
import importlib
import matematik_yardimci
# Modülü değiştirdikten sonra:
importlib.reload(matematik_yardimci)
# Şimdi güncel hali yüklendireload() Dikkat Edilecekler
import importlib
import mymodule
# reload ÖNCE modül import edilmiş olmalı
importlib.reload(mymodule)
# from ... import ile alınan isimler GÜNCELLENMEZler
from mymodule import fonksiyon # Bu eski kalır!
importlib.reload(mymodule) # mymodule güncellendi
# fonksiyon hâlâ ESKİ versiyonu gösterir!
# Doğru yöntem:
import mymodule
importlib.reload(mymodule)
mymodule.fonksiyon() # Güncel versiyonu kullanırreload() genellikle geliştirme ve debug amaçlı kullanılır. Production kodunda kullanma.
Modül Türleri
Python'da üç tür modül var:
1. Yerleşik Modüller (Built-in)
C ile yazılmış, Python yorumlayıcısına gömülü:
import sys
print(sys.builtin_module_names)
# ('_abc', '_io', '_thread', 'builtins', 'sys', ...)2. Standart Kütüphane Modülleri
Python ile birlikte gelen modüller:
import os, json, math, datetime, collections, pathlib
# Hepsi Python kurulumu ile gelir — pip gerekmez3. Üçüncü Parti Modüller
pip ile yüklenen paketler:
import requests # pip install requests
import numpy # pip install numpy
import flask # pip install flaskPaketler (Packages)
Birden fazla modülü organize eden dizin yapısı pakettir. Bir dizinin paket olması için __init__.py dosyası içermesi gerekir (Python 3.3+'da opsiyonel ama önerilen).
mypackage/
├── __init__.py ← Paketi tanımlar
├── core.py
├── utils.py
└── models/
├── __init__.py ← Alt paket
├── user.py
└── product.py# __init__.py — Paket public API'si
from .core import ana_fonksiyon
from .utils import yardimci_fonksiyon
__all__ = ["ana_fonksiyon", "yardimci_fonksiyon"]# Kullanım
import mypackage
mypackage.ana_fonksiyon()
from mypackage import ana_fonksiyon
from mypackage.models.user import User__init__.py'nin Rolü
__init__.py bir paket import edildiğinde ilk çalışan dosyadır. Genellikle:
Public API'yi tanımlar (
__all__)Alt modüllerden önemli isimleri import eder
Paket seviyesi değişkenler tanımlar (
__version__)Boş bırakılır (sadece dizini paket olarak işaretler)
# mypackage/__init__.py
"""MyPackage — Harika bir paket."""
__version__ = "1.0.0"
__author__ = "Ali Yılmaz"
from .core import Engine
from .utils import helper
# Kullanıcı şöyle yapabilir:
# from mypackage import Engine
# print(mypackage.__version__)Namespace Paketleri (Python 3.3+)
Python 3.3'ten itibaren __init__.py olmadan da paket oluşturabilirsin — bunlara namespace package denir:
myns/
├── modül_a.py
└── modül_b.py
# __init__.py YOK!from myns import modül_aNamespace paketleri birden fazla dizine yayılabilir — büyük organizasyonlarda faydalı. Ama basit projelerde __init__.py kullanmaya devam et.
Pratik: Küçük Bir Kütüphane
Öğrendiklerimizle bir yardımcı kütüphane oluşturalım:
textutils/
├── __init__.py
├── clean.py
├── analyze.py
└── transform.py# textutils/clean.py
"""Metin temizleme fonksiyonları."""
def bosluk_temizle(metin):
"""Fazla boşlukları temizler."""
return " ".join(metin.split())
def noktalama_temizle(metin):
"""Noktalama işaretlerini kaldırır."""
import string
return metin.translate(str.maketrans("", "", string.punctuation))# textutils/analyze.py
"""Metin analiz fonksiyonları."""
from collections import Counter
def kelime_say(metin):
"""Kelimeleri sayar."""
return len(metin.split())
def en_sik_kelimeler(metin, n=5):
"""En sık kullanılan n kelimeyi döndürür."""
kelimeler = metin.lower().split()
return Counter(kelimeler).most_common(n)# textutils/transform.py
"""Metin dönüştürme fonksiyonları."""
def bas_harfler_buyuk(metin):
"""Her kelimenin baş harfini büyük yapar."""
return metin.title()
def ters_cevir(metin):
"""Metni tersine çevirir."""
return metin[::-1]# textutils/__init__.py
"""TextUtils — Metin işleme kütüphanesi."""
__version__ = "1.0.0"
from .clean import bosluk_temizle, noktalama_temizle
from .analyze import kelime_say, en_sik_kelimeler
from .transform import bas_harfler_buyuk
__all__ = [
"bosluk_temizle", "noktalama_temizle",
"kelime_say", "en_sik_kelimeler",
"bas_harfler_buyuk"
]Kullanım:
from textutils import bosluk_temizle, kelime_say, en_sik_kelimeler
metin = " Python çok güzel bir dil. Python ile her şey mümkün. "
temiz = bosluk_temizle(metin)
print(temiz)
print(f"Kelime sayısı: {kelime_say(temiz)}")
print(f"En sık: {en_sik_kelimeler(temiz, 3)}")Özet
Bu derste Python modül sistemini öğrendik:
Modül bir
.pydosyasıdır.importile yüklersin,from ... importile belirli öğeleri alırsın.asile takma ad verirsin.from ... import *'dan kaçın.`__name__ == "__main__"` pattern'i bir dosyanın hem modül hem script olarak kullanılmasını sağlar. Doğrudan çalıştırılınca test kodu çalışır, import edilince çalışmaz.
sys.path Python'ın modül arama yollarını içerir. Mevcut dizin, PYTHONPATH, standart kütüphane ve site-packages sırasıyla aranır.
`__all__` listesi modülün public API'sini tanımlar ve
from ... import *ile hangi isimlerin geleceğini belirler.Circular import iki modülün birbirini import etmesinden kaynaklanır. Çözüm: lazy import (fonksiyon içinde), üçüncü modüle çıkarma veya yapıyı yeniden düzenleme.
Paketler
__init__.pyiçeren dizinlerdir. İç içe modülleri organize eder. Relative import (.,..) paket içi modülleri birbirine bağlar.
AI Asistan
Sorularını yanıtlamaya hazır