ProgramlamaKıdemli Python Geliştirici

Python'da __new__ metodu nasıl çalışır ve __init__'ten neyle farklıdır? Ne zaman ve neden __new__'ü __init__'den daha fazla yeniden tanımlamak gerekir? Ayrıntılı bir kullanım örneği verin.

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap.

Python'da bir sınıf nesnesinin oluşturulması iki aşamalı bir süreçtir:

  1. Öncelikle yeni bir sınıf örneği (instance) ayırmak ve geri döndürmekten sorumlu olan statik __new__ metodu çağrılır.
  2. Ardından ise oluşturulan nesneyi başlatan __init__ çağrılır.

Farklar:

  • __new__ — oluşturma (herhangi bir yeni nesne döndürebilir; zorunlu parametre — sınıf).
  • __init__ — başlatma (zaten ayrılmış bir instance ile çalışır).
  • Genellikle __new__, değiştirilemez türlerin (tuple, str, int) miras alınması, singleton oluşturulması ve "fabrika" kalıbı için yeniden tanımlanır.

Kullanım örneği:

class MyStr(str): def __new__(cls, value): print("__new__ çağrıldı") instance = super().__new__(cls, value.upper()) # oluşturulmadan önce değeri değiştiriyoruz return instance def __init__(self, value): print("__init__ çağrıldı", value) s = MyStr('abc') # __new__ çağrıldı -> __init__ çağrıldı abc print(s) # 'ABC'

Yanıltıcı soru.

Eğer sınıfta __new__ tanımlanmadan, değiştirilemez bir nesne __init__ ile başlatılabilir mi?

Cevap ve örnek:

Hayır! Örneğin, değiştirilemez türler için (str, int, tuple gibi) alanlar/ değerler üzerinde herhangi bir değişiklik __new__ içinde yapılmalıdır, aksi takdirde __init__ içinde değeri değiştirmek mümkün değildir.

class MyTuple(tuple): def __init__(self, items): print(f'__init__! {items}') def __new__(cls, items): print(f'__new__! {items}') return super().__new__(cls, map(str, items)) t = MyTuple([1, 2, 3]) print(t) # ('1', '2', '3')

Konuyla ilgili ince detaylar yüzünden gerçek hatalar örnekleri


Hikaye

Büyük bir Django projesinde, özel string türlerini saklamak için str'den miras aldık. Değeri __init__ içinde değiştirmeye çalıştık - ancak sonuç değişmedi çünkü str değiştirilemez. Sadece __new__'ü öğrenip yeniden tanımladıktan sonra düzelttik.


Hikaye

Singleton kalıbını uygularken, __new__ içinde mevcut örneği geri döndürme mantığını unuttuk: sınıfın birçok çağrısı yine de yeni nesneler oluşturdu ve bu hafıza parçalanmasına neden oldu.


Hikaye

Veri serileştirme kütüphanesinde, önbellekleme sırasında __init__'i yeniden tanımlanmış bir sınıf kullandık, ancak önbellek için __new__ ile aynı nesnenin geri döndürülmesi gerektiğini gözden kaçırdık. Bu sebeple, her yeni çağrıda farklı nesneler oluşturulduğu için önbellek bozuldu.