← Kursa Dön
📄 Text · 15 min

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şi

Her TodoList nesnesi kendi tasks listesine sahip. work.add() çağrıldığında sadece work nesnesinin tasks'ına eklenir.

Instance Method Kuralları

  1. İlk parametre her zaman self.

  2. self üzerinden nesnenin tüm özellik ve metodlarına erişebilir.

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

Factory 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())  # 2

Farklı 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))  # True

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

Bu 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

ÖzellikInstance MethodClass MethodStatic Method
İlk parametreselfclsYok
DecoratorYok@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ımNesne davranışıFactory, sınıf ayarlarıUtility, doğrulama

Karar Akışı

Metod yazarken kendine şu soruları sor:

  1. Nesnenin verilerine (`self.xxx`) erişmem gerekiyor mu?

- Evet → Instance method

  1. Sınıfın kendisine (`cls`) erişmem gerekiyor mu? (Factory, class attribute değiştirme)

- Evet → Class method

  1. 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 10

String 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 self dö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 2024

Bu ö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ğıranInstanceClassStaticInstance attrClass 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ım

Bu ö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 given

2. 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 self yapmalı. 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.