Constructor (__init__) ve Attributes
Bir önceki derste sınıf ve nesne kavramlarını öğrendik. Ama fark ettin mi, her sınıfta __init__ diye gizemli bir metod var? İki alt çizgiyle başlayıp iki alt çizgiyle bitiyor — biraz ürkütücü görünüyor ama aslında çok basit bir amacı var.
Bu derste __init__ metodunu, self parametresini ve nesne özelliklerini yönetmenin tüm yollarını öğreneceğiz. Hazırsan başlayalım.
__init__ Metodu: Constructor
__init__, bir nesne oluşturulduğunda otomatik olarak çağrılan özel bir metoddur. İngilizce'de buna constructor (yapıcı) denir. Görevi, nesnenin başlangıç durumunu ayarlamaktır.
Şöyle düşün: Yeni bir telefon aldığında, ilk açılışta sana dil, saat dilimi, Wi-Fi gibi şeyleri sorar. İşte __init__ de bir nesnenin "ilk açılış kurulumu" gibi.
class Phone:
def __init__(self, brand, model, storage):
self.brand = brand
self.model = model
self.storage = storage
self.is_on = False
self.battery = 100
def turn_on(self):
self.is_on = True
return f"{self.brand} {self.model} açılıyor..."
phone = Phone("Samsung", "Galaxy S24", 256)
print(phone.brand) # Samsung
print(phone.storage) # 256
print(phone.is_on) # False
print(phone.turn_on()) # Samsung Galaxy S24 açılıyor...Phone("Samsung", "Galaxy S24", 256) yazdığında Python şunları yapar:
Bellekte yeni bir
Phonenesnesi oluşturur.__init__metodunu çağırır ve argümanları geçirir.__init__içindeself.xxx = xxxile nesneye özellikler eklenir.Oluşturulan nesneyi döndürür.
__init__ vs __new__
Teknik olarak nesneyi oluşturan metod __new__, başlatan metod __init__'tir. Ama pratikte __new__'a neredeyse hiç dokunmazsın. Yüzde 99 ihtimalle __init__ yeterli.
class Demo:
def __new__(cls):
print("1. __new__ çağrıldı — nesne oluşturuluyor")
instance = super().__new__(cls)
return instance
def __init__(self):
print("2. __init__ çağrıldı — nesne başlatılıyor")
d = Demo()
# 1. __new__ çağrıldı — nesne oluşturuluyor
# 2. __init__ çağrıldı — nesne başlatılıyorBu ayrımı bilmek güzel ama __new__ ile uğraşmak genellikle meta-programming konusudur. Şimdilik __init__'e odaklan.
self Parametresi: Nesnenin Kendisi
__init__ ve diğer instance metodlarında gördüğün self, nesnenin kendisine referanstır. Python, bir metod çağrıldığında nesneyi otomatik olarak ilk argüman olarak geçirir.
class Cat:
def __init__(self, name, color):
self.name = name # "Bu nesnenin adı = name"
self.color = color # "Bu nesnenin rengi = color"
def introduce(self):
# self = bu metodu çağıran nesne
return f"Ben {self.name}, {self.color} bir kediyim."
cat1 = Cat("Boncuk", "beyaz")
cat2 = Cat("Karamel", "turuncu")
print(cat1.introduce()) # Ben Boncuk, beyaz bir kediyim.
print(cat2.introduce()) # Ben Karamel, turuncu bir kediyim.cat1.introduce() çağrıldığında Python bunu aslında Cat.introduce(cat1) olarak çalıştırır. Yani self = cat1. cat2.introduce() için self = cat2.
self Zorunlu mu?
Evet, ilk parametre olarak her zaman geçmelisin. Ama ismi self olmak zorunda değil — bu sadece bir convention. Yani teknik olarak this, me, nesne de yazabilirsin. Ama yapma. Tüm Python dünyası self kullanır ve farklı bir isim kullanmak kodu okunamaz hale getirir.
# Teknik olarak çalışır ama YAPMA
class Weird:
def __init__(banana, name): # banana? ciddi misin?
banana.name = name
# Doğru yol
class Normal:
def __init__(self, name):
self.name = namePerde Arkası: Metod Çağrısının Gerçek Hali
class Dog:
def bark(self):
return f"{self.name} havlıyor!"
def __init__(self, name):
self.name = name
rex = Dog("Rex")
# Bu ikisi aynı şey:
print(rex.bark()) # Rex havlıyor!
print(Dog.bark(rex)) # Rex havlıyor!rex.bark() bir kısayol (syntactic sugar). Python arka planda bunu Dog.bark(rex) olarak çalıştırır. Bu yüzden self parametresi gerekli — Python nesneyi geçirmek için bir yer lazım.
Instance Attributes vs Class Attributes
Bu ayrım çok önemli ve sık karıştırılıyor. Hadi netleştirelim.
Instance Attributes
self.xxx ile tanımlanan özellikler. Her nesnenin kendi kopyası vardır.
class Student:
def __init__(self, name, grade):
self.name = name # Instance attribute
self.grade = grade # Instance attribute
s1 = Student("Ali", 85)
s2 = Student("Veli", 92)
# Her nesnenin kendi değeri
print(s1.name) # Ali
print(s2.name) # Veli
# Birini değiştirmek diğerini etkilemez
s1.name = "Ahmet"
print(s1.name) # Ahmet
print(s2.name) # Veli — değişmediClass Attributes
Sınıf gövdesinde (metod dışında) tanımlanan özellikler. Tüm nesneler aynı değeri paylaşır.
class Student:
school = "Python Academy" # Class attribute
def __init__(self, name):
self.name = name # Instance attribute
s1 = Student("Ali")
s2 = Student("Veli")
# Tüm öğrenciler aynı okula gidiyor
print(s1.school) # Python Academy
print(s2.school) # Python Academy
# Class attribute'u sınıf üzerinden değiştir
Student.school = "Advanced Academy"
print(s1.school) # Advanced Academy
print(s2.school) # Advanced Academy — ikisi de değişti!Arama Sırası
Bir nesne üzerinde özellik arandığında Python şu sırayı izler:
Önce instance attribute aranır.
Bulunamazsa class attribute aranır.
O da bulunamazsa parent class'lara bakılır.
Hiçbirinde yoksa
AttributeError.
class Config:
debug = False # Class attribute
def __init__(self, name):
self.name = name
c1 = Config("dev")
c2 = Config("prod")
print(c1.debug) # False — class attribute'dan geliyor
# Instance'a özel debug ayarı
c1.debug = True # c1'e instance attribute eklendi!
print(c1.debug) # True — instance attribute (öncelikli)
print(c2.debug) # False — hâlâ class attribute'dan geliyor
print(Config.debug) # False — class attribute değişmedi⚠️ Dikkat:
self.xxx = valueyazdığında her zaman instance attribute oluşturursun/güncellersin. Class attribute'u değiştirmek istiyorsanClassName.xxx = valuekullanmalısın. Bu karışıklık, özellikle yeni başlayanları çok yanıltır.
Class Attributes: Sayaç Örneği
Class attribute'ların en klasik kullanımı nesne sayacıdır:
class Player:
player_count = 0 # Toplam oyuncu sayısı
def __init__(self, username):
self.username = username
Player.player_count += 1 # ClassName üzerinden değiştir!
def __del__(self):
Player.player_count -= 1
p1 = Player("xKnight")
p2 = Player("ShadowHunter")
p3 = Player("FireMage")
print(Player.player_count) # 3
del p2
print(Player.player_count) # 2Dikkat et: Player.player_count += 1 diyoruz, self.player_count += 1 değil. Eğer self kullanırsak, Python instance attribute oluşturur ve class attribute'u değiştirmez — ince ama kritik bir fark.
Class Attribute Kullanım Alanları
class APIClient:
base_url = "https://api.example.com" # Sabit, paylaşılan
timeout = 30 # Varsayılan, değiştirilebilir
max_retries = 3
def __init__(self, token):
self.token = token # Her client'ın kendi token'ı
class Color:
# Sabit değerler (enum-like)
RED = "#FF0000"
GREEN = "#00FF00"
BLUE = "#0000FF"
print(Color.RED) # #FF0000__init__ ile Zorunlu ve Opsiyonel Parametreler
__init__ normal bir fonksiyon gibi parametre alır. Default değerler vererek opsiyonel parametreler oluşturabilirsin:
class User:
def __init__(self, username, email, age=None, is_active=True):
self.username = username # Zorunlu
self.email = email # Zorunlu
self.age = age # Opsiyonel (default: None)
self.is_active = is_active # Opsiyonel (default: True)
# Sadece zorunlu parametreler
u1 = User("ali", "ali@mail.com")
# Tüm parametreler
u2 = User("veli", "veli@mail.com", age=25, is_active=False)
print(u1.age) # None
print(u2.age) # 25
print(u1.is_active) # TrueKeyword-Only Arguments
Parametreleri sadece isimle geçmeye zorlamak istersen * kullanabilirsin:
class Connection:
def __init__(self, host, port, *, timeout=30, ssl=True):
self.host = host
self.port = port
self.timeout = timeout
self.ssl = ssl
# Bu çalışır
conn = Connection("localhost", 5432, timeout=60, ssl=False)
# Bu çalışmaz — timeout positional olamaz
# conn = Connection("localhost", 5432, 60, False) # TypeError*'dan sonraki parametreler mutlaka isimle geçirilmeli. Bu, kodun okunabilirliğini artırır — Connection("localhost", 5432, 60, False) yazdığında 60 ve False'un ne olduğu belli değil.
__init__ İçinde Doğrulama (Validation)
class Product:
def __init__(self, name, price, stock=0):
if not name:
raise ValueError("Ürün adı boş olamaz!")
if price < 0:
raise ValueError("Fiyat negatif olamaz!")
if stock < 0:
raise ValueError("Stok negatif olamaz!")
self.name = name
self.price = price
self.stock = stock
def apply_discount(self, percent):
if not 0 <= percent <= 100:
raise ValueError("İndirim 0-100 arası olmalı!")
self.price *= (1 - percent / 100)
# Geçersiz veri
try:
p = Product("", 100)
except ValueError as e:
print(e) # Ürün adı boş olamaz!
# Geçerli veri
p = Product("Laptop", 15000, stock=10)
p.apply_discount(20)
print(p.price) # 12000.0💡 İpucu:
__init__içinde gelen verileri doğrulamak çok iyi bir pratiktir. "Garbage in, garbage out" — kötü veriyi baştan engelle, sonra debug etmekle uğraşma.
__del__: Destructor
__init__ nesneyi oluştururken çalışır, __del__ ise nesne yok edilirken çalışır. Buna destructor denir.
class TempFile:
def __init__(self, filename):
self.filename = filename
print(f"'{filename}' oluşturuldu.")
def __del__(self):
print(f"'{self.filename}' siliniyor...")
f = TempFile("data.tmp") # 'data.tmp' oluşturuldu.
del f # 'data.tmp' siliniyor...Ama __del__ ile ilgili önemli uyarılar var:
`del` nesneyi hemen silmez — sadece referans sayısını azaltır. Nesne garbage collector tarafından silinir.
`__del__` ne zaman çağrılır, garanti yok. Python'un garbage collector'ı ne zaman çalışacağına kendisi karar verir.
`__del__` içinde exception olursa, sessizce yutulur. Debug etmek zor olur.
class Resource:
def __init__(self, name):
self.name = name
self._file = open(f"{name}.log", "w")
def write(self, data):
self._file.write(data + "\n")
def __del__(self):
self._file.close() # Dosyayı kapat
print(f"{self.name}: kaynak temizlendi")⚠️ Dikkat: Kaynak yönetimi (dosya, ağ bağlantısı, vs.) için
__del__yerine context manager (withstatement) kullan.__del__güvenilir değildir ve ne zaman çağrılacağı belirsizdir. Context manager'ları ileride göreceğiz.
# Kötü — __del__'e güvenme
class BadFile:
def __init__(self, path):
self.f = open(path, "w")
def __del__(self):
self.f.close() # Çağrılacağı garanti değil!
# İyi — context manager kullan
with open("data.txt", "w") as f:
f.write("güvenli!") # with bloğu bitince dosya kapanırhasattr(), getattr(), setattr(), delattr()
Python'da nesne özelliklerine dinamik olarak erişmek, kontrol etmek ve değiştirmek için dört yerleşik fonksiyon var:
hasattr() — Özellik Var mı?
class Car:
def __init__(self, brand):
self.brand = brand
car = Car("Toyota")
print(hasattr(car, "brand")) # True
print(hasattr(car, "color")) # False
# Güvenli erişim
if hasattr(car, "color"):
print(car.color)
else:
print("color özelliği yok") # Bu çalışırgetattr() — Özellik Oku (Güvenli)
class Settings:
def __init__(self):
self.theme = "dark"
self.language = "tr"
s = Settings()
# Normal erişim
print(s.theme) # dark
# getattr ile erişim — default değer verebilirsin
print(getattr(s, "theme")) # dark
print(getattr(s, "font_size", 14)) # 14 — yok ama default var
# print(getattr(s, "font_size")) # AttributeError — default yok!getattr'ın güzelliği: özellik adını string olarak geçirebilirsin. Bu, dinamik programlamada çok işe yarar:
class Player:
def __init__(self):
self.health = 100
self.mana = 50
self.strength = 30
player = Player()
# Kullanıcıdan hangi stat'ı görmek istediğini sor
stats = ["health", "mana", "strength"]
for stat in stats:
value = getattr(player, stat)
print(f"{stat}: {value}")setattr() — Özellik Ayarla
class Config:
pass
config = Config()
# Dinamik özellik ekleme
setattr(config, "debug", True)
setattr(config, "version", "2.0")
print(config.debug) # True
print(config.version) # 2.0
# Sözlükten toplu ayarlama
defaults = {"host": "localhost", "port": 8080, "workers": 4}
for key, value in defaults.items():
setattr(config, key, value)
print(config.host) # localhost
print(config.port) # 8080delattr() — Özellik Sil
class Temp:
def __init__(self):
self.data = [1, 2, 3]
self.cache = {"key": "value"}
t = Temp()
print(hasattr(t, "cache")) # True
delattr(t, "cache")
print(hasattr(t, "cache")) # False
# del ile aynı şey:
# del t.cacheHepsi Bir Arada: Dinamik Nesne
class DynamicObject:
"""Sözlük gibi çalışan dinamik nesne."""
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def has(self, name):
return hasattr(self, name)
def get(self, name, default=None):
return getattr(self, name, default)
obj = DynamicObject(name="Ali", age=25, city="İstanbul")
print(obj.name) # Ali
print(obj.get("email", "N/A")) # N/A
print(obj.has("age")) # Truevars() ve __dict__
Her nesnenin özelliklerini bir sözlük olarak görmek istiyorsan vars() veya __dict__ kullanabilirsin:
class Hero:
game = "RPG" # Class attribute
def __init__(self, name, level, hp):
self.name = name # Instance attribute
self.level = level
self.hp = hp
hero = Hero("Gandalf", 99, 5000)
# Instance attributes
print(vars(hero))
# {'name': 'Gandalf', 'level': 99, 'hp': 5000}
# __dict__ aynı şey
print(hero.__dict__)
# {'name': 'Gandalf', 'level': 99, 'hp': 5000}
# Class attributes vars'da görünmez!
# Class'ın kendi dict'i:
print(vars(Hero))
# {'game': 'RPG', '__init__': ..., '__dict__': ..., ...}vars() ve __dict__ sadece instance attribute'ları gösterir. Class attribute'lar instance'ın __dict__'inde yer almaz.
__dict__ ile Özellik Değiştirme
__dict__ normal bir sözlüktür, doğrudan manipüle edebilirsin:
hero = Hero("Frodo", 10, 100)
# Doğrudan dict üzerinden güncelleme
hero.__dict__["hp"] = 200
hero.__dict__["weapon"] = "Sting"
print(hero.hp) # 200
print(hero.weapon) # Sting
# Toplu güncelleme
hero.__dict__.update({"level": 20, "hp": 500})
print(hero.level) # 20Bu teknik bazı meta-programming senaryolarında işe yarar ama günlük kodda setattr() veya doğrudan erişim daha temiz.
vars() ile Debug
class Order:
def __init__(self, order_id, items, total):
self.order_id = order_id
self.items = items
self.total = total
self.status = "pending"
order = Order("ORD-001", ["Laptop", "Mouse"], 15500)
# Debug için harika
print("Order details:")
for key, value in vars(order).items():
print(f" {key}: {value}")
# Order details:
# order_id: ORD-001
# items: ['Laptop', 'Mouse']
# total: 15500
# status: pending__init__ Desenleri (Patterns)
Pratikte sıkça karşılaşacağın __init__ desenleri:
1. Zorunlu + Opsiyonel
class Logger:
def __init__(self, name, level="INFO", output="console"):
self.name = name
self.level = level
self.output = output
self.messages = []
logger = Logger("app")
logger = Logger("app", level="DEBUG", output="file")2. Sözlükten Oluşturma
class User:
def __init__(self, username, email, age=None):
self.username = username
self.email = email
self.age = age
# API'den gelen JSON data
data = {"username": "ali", "email": "ali@mail.com", "age": 25}
# ** ile unpack
user = User(**data)
print(user.username) # ali3. Kopya Oluşturma
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def copy(self):
"""Noktanın kopyasını oluşturur."""
return Point(self.x, self.y)
p1 = Point(3, 5)
p2 = p1.copy()
p2.x = 10
print(p1.x) # 3 — değişmedi
print(p2.x) # 104. Init'te Hesaplama
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
# Hesaplanan değerler
self.area = width * height
self.perimeter = 2 * (width + height)
self.is_square = width == height
r = Rectangle(5, 5)
print(r.area) # 25
print(r.is_square) # True5. Zincirleme Validasyon
class Email:
def __init__(self, address):
self._validate(address)
self.address = address
self.username, self.domain = address.split("@")
def _validate(self, address):
if not isinstance(address, str):
raise TypeError("Email string olmalı!")
if "@" not in address:
raise ValueError("Geçersiz email formatı!")
if address.count("@") > 1:
raise ValueError("Birden fazla @ işareti!")
email = Email("ali@example.com")
print(email.username) # ali
print(email.domain) # example.com
try:
bad = Email("invalid-email")
except ValueError as e:
print(e) # Geçersiz email formatı!__init__ İçinde self Kullanarak Metod Çağırma
__init__ içinden nesnenin diğer metodlarını çağırabilirsin:
class DataProcessor:
def __init__(self, raw_data):
self.raw_data = raw_data
self.cleaned_data = None
self.stats = {}
# Init sırasında temizlik ve analiz yap
self._clean()
self._analyze()
def _clean(self):
"""Veriyi temizler."""
self.cleaned_data = [x.strip().lower() for x in self.raw_data if x.strip()]
def _analyze(self):
"""Basit istatistik çıkarır."""
self.stats = {
"total": len(self.cleaned_data),
"unique": len(set(self.cleaned_data)),
}
data = DataProcessor([" Ali ", "veli", " ALI", "", "ayşe "])
print(data.cleaned_data) # ['ali', 'veli', 'ali', 'ayşe']
print(data.stats) # {'total': 4, 'unique': 3}Bu pattern'de dikkat: _clean ve _analyze başında alt çizgi var. Bu, "bu metodlar dışarıdan çağrılmak için değil, iç kullanım için" demek. Convention meselesi — Python seni durdurmaz ama amacı belli eder.
__init__ Döndürmemeli (Return Yok!)
__init__ hiçbir zaman bir değer döndürmemeli. None dışında bir şey return edersen TypeError alırsın:
class Bad:
def __init__(self, x):
self.x = x
return x # YANLIŞ!
# b = Bad(5) # TypeError: __init__() should return None
class Good:
def __init__(self, x):
self.x = x
# return yok — veya "return None" yazabilirsin ama gereksizNeden? Çünkü __init__'in işi nesneyi başlatmak, döndürmek değil. Nesneyi döndüren __new__ metodu. __init__ sadece self üzerinde düzenleme yapar.
Gerçek Dünya Örneği: Banka Hesabı
Tüm kavramları bir araya getirelim:
class BankAccount:
"""Banka hesabı yönetim sınıfı."""
bank_name = "PyBank" # Class attribute
_total_accounts = 0 # Class attribute (private)
_interest_rate = 0.05 # %5 faiz
def __init__(self, owner, balance=0, account_type="checking"):
# Validation
if balance < 0:
raise ValueError("Başlangıç bakiyesi negatif olamaz!")
if account_type not in ("checking", "savings"):
raise ValueError("Hesap tipi 'checking' veya 'savings' olmalı!")
# Instance attributes
self.owner = owner
self._balance = balance # "Private" — doğrudan erişilmemeli
self.account_type = account_type
self.transactions = []
# Hesap numarası oluştur
BankAccount._total_accounts += 1
self.account_number = f"PB-{BankAccount._total_accounts:04d}"
# İlk işlemi kaydet
if balance > 0:
self.transactions.append(f"Açılış: +{balance} TL")
def deposit(self, amount):
if amount <= 0:
raise ValueError("Yatırılacak miktar pozitif olmalı!")
self._balance += amount
self.transactions.append(f"Yatırma: +{amount} TL")
return self._balance
def withdraw(self, amount):
if amount <= 0:
raise ValueError("Çekilecek miktar pozitif olmalı!")
if amount > self._balance:
raise ValueError("Yetersiz bakiye!")
self._balance -= amount
self.transactions.append(f"Çekme: -{amount} TL")
return self._balance
def get_balance(self):
return self._balance
def get_statement(self):
header = f"\n{'='*40}"
header += f"\n{self.bank_name} — Hesap Özeti"
header += f"\nHesap No: {self.account_number}"
header += f"\nHesap Sahibi: {self.owner}"
header += f"\nHesap Tipi: {self.account_type}"
header += f"\n{'='*40}"
body = "\n".join(f" • {t}" for t in self.transactions)
footer = f"\n{'─'*40}\nBakiye: {self._balance} TL"
return f"{header}\n{body}{footer}"
@classmethod
def get_total_accounts(cls):
return cls._total_accounts# Kullanım
acc = BankAccount("Ali Yılmaz", 1000, "savings")
acc.deposit(500)
acc.deposit(250)
acc.withdraw(300)
print(acc.get_statement())
print(f"\nToplam hesap: {BankAccount.get_total_accounts()}")Çıktı:
========================================
PyBank — Hesap Özeti
Hesap No: PB-0001
Hesap Sahibi: Ali Yılmaz
Hesap Tipi: savings
========================================
• Açılış: +1000 TL
• Yatırma: +500 TL
• Yatırma: +250 TL
• Çekme: -300 TL
──────────────────────────────────────────
Bakiye: 1450 TL
Toplam hesap: 1💡 İpucu: Dikkat et,
_balancebaşında alt çizgi var. Bu, "bu özelliğe doğrudan erişme,deposit()vewithdraw()metodlarını kullan" demek. Böylece her işlem kaydedilebiliyor ve validation yapılabiliyor. Encapsulation'ın ta kendisi!
Özet
`__init__` nesne oluşturulduğunda otomatik çağrılan constructor'dır. Nesnenin başlangıç durumunu ayarlar.
`self` parametresi nesnenin kendisine referanstır. Python metod çağrısında nesneyi otomatik olarak ilk argüman geçirir.
Instance attributes (
self.x) her nesneye özeldir; class attributes tüm nesneler tarafından paylaşılır.__init__'te zorunlu ve opsiyonel parametreler kullanabilir, doğrulama (validation) yapabilirsin.`__del__` destructor'dır ama güvenilmez — kaynak yönetimi için context manager tercih et.
`hasattr()`, `getattr()`, `setattr()`, `delattr()` ile özelliklere dinamik erişim sağlarsın.
`vars()` ve `__dict__` nesnenin instance attribute'larını sözlük olarak gösterir.
AI Asistan
Sorularını yanıtlamaya hazır