Methods, @classmethod, @staticmethod
Sınıflarımızda fonksiyon tanımlayabildiğimizi gördük. Ama Python'da her metod aynı değil. Bazen metod nesneyle çalışır, bazen sınıfın kendisiyle, bazen de ikisiyle de alakası yoktur. Bu üç farklı metod tipi — instance method, class method ve static method — farklı durumlar için tasarlanmış araçlar.
Bu derste her birini öğrenip, ne zaman hangisini kullanacağını netleştireceğiz.
Instance Methods: Nesneye Bağlı
Instance method, sınıfın en yaygın metod türüdür. İlk parametresi self — yani nesnenin kendisi. Bu metod, nesnenin verilerine erişir ve onları değiştirebilir.
Analoji: Bir araba düşün. "Hızlan", "fren yap", "korna çal" gibi komutlar hep o araba için geçerli. Başka birinin arabasını senin direksiyonundan hızlandıramazsın. İşte instance method de böyle — her nesne kendi metodlarını kendi verileri üzerinde çalıştırır.
class TodoList:
def __init__(self, name):
self.name = name
self.tasks = []
def add(self, task):
"""Yeni görev ekler."""
self.tasks.append({"task": task, "done": False})
def complete(self, index):
"""Görevi tamamlanmış olarak işaretler."""
if 0 <= index < len(self.tasks):
self.tasks[index]["done"] = True
def show(self):
"""Görevleri listeler."""
print(f"\n📋 {self.name}")
for i, t in enumerate(self.tasks):
mark = "✅" if t["done"] else "⬜"
print(f" {i}. {mark} {t['task']}")
# Her nesne kendi listesine sahip
work = TodoList("İş")
personal = TodoList("Kişisel")
work.add("Raporu bitir")
work.add("Toplantıya katıl")
personal.add("Market alışverişi")
work.complete(0)
work.show()
personal.show()Çıktı:
📋 İş
0. ✅ Raporu bitir
1. ⬜ Toplantıya katıl
📋 Kişisel
0. ⬜ Market alışverişiHer TodoList nesnesi kendi tasks listesine sahip. work.add() çağrıldığında sadece work nesnesinin tasks'ına eklenir.
Instance Method Kuralları
İlk parametre her zaman
self.selfüzerinden nesnenin tüm özellik ve metodlarına erişebilir.Nesne üzerinden çağrılır:
obj.method().
class Calculator:
def __init__(self):
self.history = []
def add(self, a, b):
result = a + b
self._log(f"{a} + {b} = {result}") # Başka instance method çağırma
return result
def _log(self, entry):
self.history.append(entry)
calc = Calculator()
calc.add(3, 5)
calc.add(10, 20)
print(calc.history) # ['3 + 5 = 8', '10 + 20 = 30']@classmethod: Sınıfa Bağlı Metodlar
Class method, nesneyle değil sınıfın kendisiyle çalışır. İlk parametresi self yerine cls (class'ın kısaltması) olur ve @classmethod decorator'ı ile işaretlenir.
class Pizza:
def __init__(self, size, toppings):
self.size = size
self.toppings = toppings
@classmethod
def margherita(cls, size):
"""Margherita pizza oluşturur."""
return cls(size, ["mozzarella", "domates", "fesleğen"])
@classmethod
def pepperoni(cls, size):
"""Pepperoni pizza oluşturur."""
return cls(size, ["mozzarella", "pepperoni", "biber"])
# Normal oluşturma
p1 = Pizza("large", ["mozzarella", "mantar", "zeytin"])
# Factory method ile oluşturma
p2 = Pizza.margherita("medium")
p3 = Pizza.pepperoni("large")
print(p2.toppings) # ['mozzarella', 'domates', 'fesleğen']
print(p3.size) # largeFactory Method Pattern
Class method'un en yaygın kullanımı factory method (fabrika metodu) — alternatif constructor'lar oluşturmak. cls(...) çağrısı sınıfın kendisini çağırır, yani yeni bir nesne oluşturur.
Neden Pizza(...) yerine cls(...) yazıyoruz? Çünkü kalıtım durumunda cls alt sınıfı temsil eder:
class SpecialPizza(Pizza):
pass
# cls = SpecialPizza olur, Pizza değil
sp = SpecialPizza.margherita("large")
print(type(sp)) # <class '__main__.SpecialPizza'>cls ile Class Attribute'lara Erişim
class Employee:
raise_rate = 1.05 # %5 zam
employee_count = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.employee_count += 1
def apply_raise(self):
self.salary *= self.raise_rate
@classmethod
def set_raise_rate(cls, rate):
"""Tüm çalışanlar için zam oranını değiştirir."""
cls.raise_rate = rate
@classmethod
def get_count(cls):
return cls.employee_count
e1 = Employee("Ali", 10000)
e2 = Employee("Ayşe", 12000)
Employee.set_raise_rate(1.10) # %10 zam
e1.apply_raise()
print(e1.salary) # 11000.0
print(Employee.get_count()) # 2Farklı Veri Formatlarından Nesne Oluşturma
Bu, class method'un en güzel kullanımlarından biri:
class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
@classmethod
def from_string(cls, person_str):
"""'Ad-Soyad-Yaş' formatından oluşturur."""
first, last, age = person_str.split("-")
return cls(first, last, int(age))
@classmethod
def from_dict(cls, data):
"""Sözlükten oluşturur."""
return cls(data["first_name"], data["last_name"], data["age"])
@classmethod
def from_json(cls, json_str):
"""JSON string'den oluşturur."""
import json
data = json.loads(json_str)
return cls(data["first_name"], data["last_name"], data["age"])
# Farklı kaynaklardan aynı nesne
p1 = Person("Ali", "Yılmaz", 30)
p2 = Person.from_string("Veli-Demir-25")
p3 = Person.from_dict({"first_name": "Ayşe", "last_name": "Kaya", "age": 28})
print(p2.first_name) # Veli
print(p3.age) # 28@staticmethod: Bağımsız Utility
Static method, ne nesneye ne de sınıfa bağlıdır. self veya cls parametresi almaz. Temelde normal bir fonksiyondur ama mantıksal olarak sınıfla ilişkili olduğu için sınıfın içinde tanımlanır.
class MathHelper:
@staticmethod
def is_prime(n):
"""Sayının asal olup olmadığını kontrol eder."""
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
@staticmethod
def factorial(n):
"""Faktöriyel hesaplar."""
if n <= 1:
return 1
result = 1
for i in range(2, n + 1):
result *= i
return result
@staticmethod
def gcd(a, b):
"""En büyük ortak böleni bulur."""
while b:
a, b = b, a % b
return a
# Nesne oluşturmadan çağırabilirsin
print(MathHelper.is_prime(17)) # True
print(MathHelper.factorial(5)) # 120
print(MathHelper.gcd(48, 18)) # 6
# Nesne üzerinden de çağırabilirsin (ama gereksiz)
m = MathHelper()
print(m.is_prime(7)) # TrueStatic Method Ne Zaman Kullanılır?
Static method, sınıfın verilerine erişim gerektirmeyen ama sınıfla kavramsal olarak ilişkili olan fonksiyonlar için kullanılır.
class Validator:
"""Doğrulama utility'leri."""
@staticmethod
def is_valid_email(email):
return "@" in email and "." in email.split("@")[-1]
@staticmethod
def is_valid_phone(phone):
cleaned = phone.replace(" ", "").replace("-", "")
return cleaned.startswith("+90") and len(cleaned) == 13
@staticmethod
def is_valid_tc(tc):
return len(tc) == 11 and tc.isdigit() and tc[0] != "0"
# Kullanım
print(Validator.is_valid_email("ali@mail.com")) # True
print(Validator.is_valid_phone("+90 555 123 4567")) # True
print(Validator.is_valid_tc("12345678901")) # TrueBu fonksiyonlar modül düzeyinde de tanımlanabilirdi. Ama Validator sınıfı altında gruplanmaları kodu daha organize eder.
Ne Zaman Hangisi? Karar Tablosu
| Özellik | Instance Method | Class Method | Static Method |
|---|---|---|---|
| İlk parametre | self | cls | Yok |
| Decorator | Yok | @classmethod | @staticmethod |
| Nesne verilerine erişir? | ✅ Evet | ❌ Hayır | ❌ Hayır |
| Sınıf verilerine erişir? | ✅ Evet (self üzerinden) | ✅ Evet | ❌ Hayır |
| Nesne üzerinden çağrılır? | ✅ Evet | ✅ Evet | ✅ Evet |
| Sınıf üzerinden çağrılır? | ⚠️ Teknik olarak (ama self lazım) | ✅ Evet | ✅ Evet |
| Tipik kullanım | Nesne davranışı | Factory, sınıf ayarları | Utility, doğrulama |
Karar Akışı
Metod yazarken kendine şu soruları sor:
Nesnenin verilerine (`self.xxx`) erişmem gerekiyor mu?
- Evet → Instance method
Sınıfın kendisine (`cls`) erişmem gerekiyor mu? (Factory, class attribute değiştirme)
- Evet → Class method
Ne nesneye ne sınıfa erişmem gerekmiyor ama mantıksal olarak bu sınıfla ilgili mi?
- Evet → Static method - Hayır → Modül düzeyinde fonksiyon
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
# Instance method: nesne verisini kullanıyor
def to_fahrenheit(self):
return self.celsius * 9/5 + 32
# Class method: alternatif constructor
@classmethod
def from_fahrenheit(cls, fahrenheit):
celsius = (fahrenheit - 32) * 5/9
return cls(celsius)
# Static method: bağımsız utility
@staticmethod
def is_boiling(celsius):
return celsius >= 100
t = Temperature(100)
print(t.to_fahrenheit()) # 212.0
t2 = Temperature.from_fahrenheit(72)
print(t2.celsius) # 22.22...
print(Temperature.is_boiling(99)) # False💡 İpucu: Şüpheye düştüğünde instance method yaz. Zamanla "aa bu self kullanmıyor, static olabilir" diye fark edersin. Erken optimizasyon yapma — önce çalışsın, sonra düzelt.
Method Chaining: return self
Method chaining, birden fazla metodu art arda . ile çağırma tekniğidir. Bunu sağlamak için metod self döndürmelidir.
jQuery biliyorsan bu konsepti tanıyorsundur: $("p").css("color", "red").slideUp().fadeIn(). Python'da da benzer şey yapabiliriz.
class QueryBuilder:
def __init__(self, table):
self.table = table
self._conditions = []
self._order = None
self._limit = None
def where(self, condition):
self._conditions.append(condition)
return self # Chaining için self döndür!
def order_by(self, field, direction="ASC"):
self._order = f"{field} {direction}"
return self
def limit(self, n):
self._limit = n
return self
def build(self):
query = f"SELECT * FROM {self.table}"
if self._conditions:
query += " WHERE " + " AND ".join(self._conditions)
if self._order:
query += f" ORDER BY {self._order}"
if self._limit:
query += f" LIMIT {self._limit}"
return query
# Method chaining
query = (
QueryBuilder("users")
.where("age > 18")
.where("city = 'Istanbul'")
.order_by("name")
.limit(10)
.build()
)
print(query)
# SELECT * FROM users WHERE age > 18 AND city = 'Istanbul' ORDER BY name ASC LIMIT 10String Builder Örneği
class HTMLBuilder:
def __init__(self):
self._parts = []
def add_heading(self, text, level=1):
self._parts.append(f"<h{level}>{text}</h{level}>")
return self
def add_paragraph(self, text):
self._parts.append(f"<p>{text}</p>")
return self
def add_list(self, items):
li_items = "".join(f"<li>{item}</li>" for item in items)
self._parts.append(f"<ul>{li_items}</ul>")
return self
def build(self):
return "\n".join(self._parts)
html = (
HTMLBuilder()
.add_heading("Merhaba Dünya")
.add_paragraph("Bu bir test sayfasıdır.")
.add_list(["Python", "JavaScript", "Go"])
.build()
)
print(html)⚠️ Dikkat: Method chaining kullanışlıdır ama her yerde kullanma. Eğer her metod
selfdöndürüyorsa ve bazı metodların bir değer döndürmesi gerekiyorsa, API kafa karıştırıcı olabilir. Genellikle builder pattern ve fluent interface tasarımlarında kullanılır.
__str__() Ön Bakış
Python'da bir nesneyi print() ile yazdırdığında genellikle şöyle bir şey görürsün:
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
rex = Dog("Rex", "Golden Retriever")
print(rex) # <__main__.Dog object at 0x7f9b8c0a3d90>Pek kullanışlı değil, değil mi? İşte __str__ metodu bunu düzeltir:
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
def __str__(self):
return f"{self.name} ({self.breed})"
rex = Dog("Rex", "Golden Retriever")
print(rex) # Rex (Golden Retriever)
print(str(rex)) # Rex (Golden Retriever)__str__ bir magic method (dunder method). Python, print() veya str() çağrıldığında otomatik olarak bu metodu arar. Magic methods'ı ayrı bir derste detaylıca göreceğiz — şimdilik __str__'in "nesneyi insanlar için okunabilir bir stringe çeviren metod" olduğunu bil.
__str__ vs __repr__ (Kısa Fark)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"({self.x}, {self.y})"
def __repr__(self):
return f"Point({self.x}, {self.y})"
p = Point(3, 5)
print(str(p)) # (3, 5) — kullanıcı için
print(repr(p)) # Point(3, 5) — geliştirici için__str__: Kullanıcı dostu, güzel görünsün.__repr__: Geliştirici dostu, nesneyi yeniden oluşturabilecek bilgi versin.
Detaylar magic methods dersinde!
Pratik: Date Sınıfı
Tüm metod türlerini kullanan gerçek bir örnek yapalım — bir Date (tarih) sınıfı:
class Date:
"""Basit bir tarih sınıfı."""
# Class attribute: ay isimleri
MONTH_NAMES = [
"", "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran",
"Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"
]
DAYS_IN_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
def __init__(self, day, month, year):
"""Tarih oluşturur (gün, ay, yıl)."""
if not self._is_valid_date(day, month, year):
raise ValueError(f"Geçersiz tarih: {day}/{month}/{year}")
self.day = day
self.month = month
self.year = year
# ─── Instance Methods ─────────────────────────
def format_long(self):
"""'15 Ocak 2024' formatında döner."""
return f"{self.day} {self.MONTH_NAMES[self.month]} {self.year}"
def format_short(self):
"""'15/01/2024' formatında döner."""
return f"{self.day:02d}/{self.month:02d}/{self.year}"
def is_weekend(self):
"""Zeller's formula ile hafta sonu kontrolü."""
# Basitleştirilmiş versiyon
import datetime
d = datetime.date(self.year, self.month, self.day)
return d.weekday() >= 5
def add_days(self, n):
"""n gün eklenmiş yeni tarih döner."""
import datetime
d = datetime.date(self.year, self.month, self.day)
new_date = d + datetime.timedelta(days=n)
return Date(new_date.day, new_date.month, new_date.year)
def __str__(self):
return self.format_long()
# ─── Class Methods (Factory) ──────────────────
@classmethod
def from_string(cls, date_str):
"""'DD/MM/YYYY' formatından oluşturur."""
parts = date_str.split("/")
day, month, year = int(parts[0]), int(parts[1]), int(parts[2])
return cls(day, month, year)
@classmethod
def from_iso(cls, iso_str):
"""'YYYY-MM-DD' ISO formatından oluşturur."""
parts = iso_str.split("-")
year, month, day = int(parts[0]), int(parts[1]), int(parts[2])
return cls(day, month, year)
@classmethod
def today(cls):
"""Bugünün tarihini döner."""
import datetime
t = datetime.date.today()
return cls(t.day, t.month, t.year)
# ─── Static Methods (Utility) ─────────────────
@staticmethod
def is_leap_year(year):
"""Artık yıl kontrolü."""
return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
@staticmethod
def days_in_month(month, year):
"""Aydaki gün sayısını döner."""
if month == 2 and Date.is_leap_year(year):
return 29
return Date.DAYS_IN_MONTH[month]
@staticmethod
def _is_valid_date(day, month, year):
"""Tarihin geçerli olup olmadığını kontrol eder."""
if not (1 <= month <= 12):
return False
max_day = Date.days_in_month(month, year)
return 1 <= day <= max_day# Kullanım
d1 = Date(15, 1, 2024)
print(d1) # 15 Ocak 2024
print(d1.format_short()) # 15/01/2024
# Factory method'lar
d2 = Date.from_string("23/04/2024")
d3 = Date.from_iso("2024-12-31")
d4 = Date.today()
print(d2) # 23 Nisan 2024
print(d3) # 31 Aralık 2024
print(d4) # (bugünün tarihi)
# Static method
print(Date.is_leap_year(2024)) # True
print(Date.is_leap_year(2023)) # False
print(Date.days_in_month(2, 2024)) # 29
# Instance method
d5 = d1.add_days(30)
print(d5) # 14 Şubat 2024Bu örnekte üç metod türü de yerinde kullanılıyor:
Instance methods (
format_long,add_days): Nesnenin verisiyle çalışıyor.Class methods (
from_string,today): Alternatif yollarla nesne oluşturuyor.Static methods (
is_leap_year,days_in_month): Bağımsız utility hesaplamaları.
Metod Tipleri Arası Çağrılar
Metod tipleri birbirini çağırabilir mi? Evet, ama bazı kurallar var:
class Report:
file_format = "PDF"
def __init__(self, title, data):
self.title = title
self.data = data
# Instance method → herhangi bir şeyi çağırabilir
def generate(self):
header = self._create_header() # instance method
body = self._format_data() # instance method
filename = self.make_filename(self.title) # static method
return f"{header}\n{body}\n→ {filename}"
def _create_header(self):
return f"=== {self.title} ==="
def _format_data(self):
return "\n".join(f" • {item}" for item in self.data)
# Class method → cls ve static erişebilir, self erişemez
@classmethod
def set_format(cls, fmt):
cls.file_format = fmt
@classmethod
def get_format_info(cls):
ext = cls._get_extension() # static method çağırabilir
return f"Format: {cls.file_format} (.{ext})"
# Static method → sadece parametre olarak aldıklarıyla çalışır
@staticmethod
def make_filename(title):
clean = title.lower().replace(" ", "_")
return f"{clean}.pdf"
@staticmethod
def _get_extension():
return "pdf"
report = Report("Satış Raporu", ["Q1: 100K", "Q2: 150K", "Q3: 120K"])
print(report.generate())
print(Report.get_format_info())Erişim Kuralları Özet
| Çağıran | Instance | Class | Static | Instance attr | Class attr |
|---|---|---|---|---|---|
| Instance method | ✅ | ✅ | ✅ | ✅ | ✅ |
| Class method | ❌ | ✅ | ✅ | ❌ | ✅ |
| Static method | ❌ | ❌* | ❌* | ❌ | ❌* |
*Static method, sınıf adını yazarak class attribute'a veya static method'a erişebilir ama cls veya self üzerinden erişemez.
İleri Teknikler: Metod Olarak Fonksiyon
Python'da metodlar aslında fonksiyondur. Nesneye bağlandığında bound method olur:
class Greeter:
def __init__(self, name):
self.name = name
def greet(self):
return f"Merhaba, ben {self.name}!"
g = Greeter("Ali")
# Bound method — nesneye bağlı
method = g.greet
print(method) # <bound method Greeter.greet of <...>>
print(method()) # Merhaba, ben Ali!
# Unbound function — sınıftan erişim
func = Greeter.greet
print(func) # <function Greeter.greet at 0x...>
print(func(g)) # Merhaba, ben Ali! — self'i elle geçirmemiz lazımBu özellik, fonksiyonları başka yerlere geçirirken işe yarar:
class Processor:
def __init__(self):
self.operations = []
def register(self, func):
self.operations.append(func)
def run(self, data):
for op in self.operations:
data = op(data)
return data
class TextOps:
@staticmethod
def strip_spaces(text):
return text.strip()
@staticmethod
def to_upper(text):
return text.upper()
@staticmethod
def add_period(text):
return text + "." if not text.endswith(".") else text
proc = Processor()
proc.register(TextOps.strip_spaces)
proc.register(TextOps.to_upper)
proc.register(TextOps.add_period)
result = proc.run(" hello world ")
print(result) # HELLO WORLD.💡 İpucu: Metodları birinci sınıf (first-class) vatandaş olarak kullanabilmen Python'un güçlü yanlarından biri. Fonksiyonel ve OOP paradigmalarını rahatça karıştırabilirsin.
Yaygın Hatalar
1. self'i Parametre Olarak Geçmek
class Foo:
def bar(self):
return "merhaba"
f = Foo()
# DOĞRU
print(f.bar())
# YANLIŞ — self zaten otomatik geçiyor
# print(f.bar(f)) # TypeError: bar() takes 1 argument but 2 were given2. Static Method İçinde self Kullanmaya Çalışmak
class Wrong:
def __init__(self, x):
self.x = x
@staticmethod
def show():
# print(self.x) # NameError: 'self' is not defined
print("Static method'da self yok!")3. Class Method'da Instance Attribute'a Erişmeye Çalışmak
class Wrong:
def __init__(self, name):
self.name = name
@classmethod
def show(cls):
# print(cls.name) # Bu class attribute arar, instance değil!
print(f"Sınıf: {cls.__name__}")Tüm Metod Türleri Bir Arada: Pratik Örnek
class FileManager:
"""Dosya yönetim sınıfı."""
supported_formats = [".txt", ".csv", ".json", ".xml"]
_instance_count = 0
def __init__(self, base_path):
self.base_path = base_path
self.files = []
FileManager._instance_count += 1
# Instance methods
def add_file(self, filename):
ext = self.get_extension(filename)
if ext not in self.supported_formats:
raise ValueError(f"Desteklenmeyen format: {ext}")
self.files.append(filename)
return self # chaining
def list_files(self):
return [f"{self.base_path}/{f}" for f in self.files]
# Class methods
@classmethod
def add_format(cls, fmt):
if not fmt.startswith("."):
fmt = "." + fmt
cls.supported_formats.append(fmt)
@classmethod
def get_instance_count(cls):
return cls._instance_count
# Static methods
@staticmethod
def get_extension(filename):
return "." + filename.rsplit(".", 1)[-1] if "." in filename else ""
@staticmethod
def is_valid_filename(filename):
invalid_chars = '<>:"|?*'
return not any(c in filename for c in invalid_chars)
# Kullanım
fm = FileManager("/documents")
fm.add_file("notes.txt").add_file("data.csv") # chaining!
print(fm.list_files())
# ['/documents/notes.txt', '/documents/data.csv']
FileManager.add_format("md")
fm.add_file("readme.md") # Artık .md destekleniyor
print(FileManager.get_extension("photo.jpg")) # .jpg
print(FileManager.is_valid_filename("my:file.txt")) # FalseÖzet
Instance method (
self): Nesnenin verilerine erişir ve değiştirir. En yaygın metod türü.Class method (
@classmethod,cls): Sınıfın kendisiyle çalışır. En yaygın kullanım: factory method (alternatif constructor).Static method (
@staticmethod): Ne nesneye ne sınıfa bağlı. Sınıfla ilişkili utility fonksiyonlar için.Method chaining için metod
return selfyapmalı. Builder ve fluent interface pattern'lerinde kullanılır.`__str__` metodu nesneyi okunabilir stringe çevirir — detaylar magic methods dersinde.
Şüpheye düştüğünde instance method yaz. Zamanla "buna self gerekmiyor" diye fark edip refactor edersin.
AI Asistan
Sorularını yanıtlamaya hazır