← Kursa Dön
📄 Text · 15 min

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:

  1. Bellekte yeni bir Phone nesnesi oluşturur.

  2. __init__ metodunu çağırır ve argümanları geçirir.

  3. __init__ içinde self.xxx = xxx ile nesneye özellikler eklenir.

  4. 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ıyor

Bu 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 = name

Perde 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şmedi

Class 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:

  1. Önce instance attribute aranır.

  2. Bulunamazsa class attribute aranır.

  3. O da bulunamazsa parent class'lara bakılır.

  4. 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 = value yazdığında her zaman instance attribute oluşturursun/güncellersin. Class attribute'u değiştirmek istiyorsan ClassName.xxx = value kullanmalı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)  # 2

Dikkat 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)  # True

Keyword-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:

  1. `del` nesneyi hemen silmez — sadece referans sayısını azaltır. Nesne garbage collector tarafından silinir.

  2. `__del__` ne zaman çağrılır, garanti yok. Python'un garbage collector'ı ne zaman çalışacağına kendisi karar verir.

  3. `__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 (with statement) 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ır

hasattr(), 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ışır

getattr() — Ö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)     # 8080

delattr() — Ö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.cache

Hepsi 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"))     # True

vars() 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)   # 20

Bu 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)  # ali

3. 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)  # 10

4. 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)  # True

5. 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 gereksiz

Neden? Çü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, _balance başında alt çizgi var. Bu, "bu özelliğe doğrudan erişme, deposit() ve withdraw() 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.