- Dataclass neyi çözer?
- Varsayılan değerler ve değişebilir alanlar tuzağı
- dataclass parametreleri: frozen, order, slots ve kw_only
- __post_init__ ile doğrulama ve türetilmiş alanlar
- Dataclass doğrulama yapmaz: Pydantic ne zaman devreye girer?
- Sık hatalar ve kontrol listesi
- Veri sınıflarını ve doğrulamayı nerede çalışırsınız?
- Sık sorulan sorular
Python'da dataclass ile veri sınıfı yazma ve doğrulama
Python dataclass ile alan tanımlama, default_factory, frozen, slots ve __post_init__ kullanımını öğrenin; Pydantic ile doğrulamanın farkını görün.
Dataclass neyi çözer?
Veri taşıyan bir sınıf yazdığınızda aynı kalıbı tekrar tekrar elle kurarsınız: __init__ içinde her alanı atamak, hata ayıklarken okunabilir bir __repr__ yazmak, nesneleri karşılaştırmak için __eq__ tanımlamak. Alan sayısı arttıkça bu kod hem uzar hem de alan eklerken bir yerde unutulur.
dataclasses modülündeki @dataclass dekoratörü, sınıf gövdesindeki tip ipuçlu alanlardan bu metotları sizin yerinize üretir. Python belgesine göre dekoratör, ayarlarınıza göre __init__, __repr__, __eq__ ve istenirse sıralama metotlarını sınıfa ekler; sınıfın kendisini döndürür.
from dataclasses import dataclass
@dataclass class Product: ad: str fiyat: float stokta: bool = True
kalem = Product("kalem", fiyat_degeri) print(kalem)
Çıktı, alan adlarıyla birlikte okunabilir bir gösterimdir. Product nesneleri, tüm alanları eşitse eşit sayılır. Sınıfta kendi __init__ metodunuzu yazarsanız dekoratör onu ezmez; yalnız tanımlı olmayanları üretir.
Dataclass'ı kullanmanın ölçütü basittir: sınıfın asıl işi veriyi bir arada tutmaksa (sipariş satırı, yapılandırma, API yanıtı gibi), dataclass uygundur. Sınıfın asıl işi karmaşık davranış ve kapsülleme ise, normal sınıf yazmak daha doğrudur.
Varsayılan değerler ve değişebilir alanlar tuzağı
Tip ipucunun yanına bir değer yazarsanız o alan varsayılan alır; varsayılanlı alanlar varsayılansız alanlardan sonra gelmelidir. Asıl tuzak liste, sözlük ve küme gibi değişebilir varsayılanlardır. Sınıf düzeyindeki varsayılan tüm nesneler arasında paylaşılacağı için, dataclass bunu en baştan engeller:
@dataclass class Sepet: kalemler: list = [] # ValueError
Belgeye göre bu tanım ValueError verir ve çözüm field(default_factory=...) kullanmaktır. Fabrika fonksiyonu her nesne oluşturulduğunda sıfır argümanla çağrılır, böylece her sepet kendi listesine sahip olur:
from dataclasses import dataclass, field
@dataclass class Sepet: sahip: str kalemler: list[str] = field(default_factory=list)
Belgeye göre denetim, Python'un güncel sürümlerinde türün değil değerin hashlenebilir olup olmadığına bakar; hashlenemeyen her varsayılan reddedilir. Ayrıca field() ile bir alanı repr çıktısından ya da karşılaştırmadan çıkarabilir, init dışında bırakabilir veya alana metaveri ekleyebilirsiniz. Aynı alanda hem default hem default_factory vermek hatadır.
dataclass parametreleri: frozen, order, slots ve kw_only
Dekoratöre verdiğiniz parametreler üretilen davranışı değiştirir. Belgedeki ana parametreler şunlardır:
- frozen=True: Nesne salt okunur olur. Alana atama yapmaya çalışmak FrozenInstanceError fırlatır. Değişmez veri nesneleri, paylaşım ve önbellek anahtarı için güvenlidir.
- order=True: Alanları sırayla karşılaştıran küçüktür/büyüktür metotlarını üretir. eq=False ile birlikte kullanılırsa hata verir.
- kw_only=True: Tüm alanları yalnızca anahtar kelimeyle verilebilir yapar. Alan sayısı çok olduğunda yanlış sırada değer vermeyi önler.
- slots=True: Nesne __slots__ kullanır; yeni nitelik eklenemez ve bellek kullanımı genelde azalır. Bu seçenek yeni bir sınıf döndürür.
- eq: Varsayılan olarak açıktır ve alan alan karşılaştırır.
Hash davranışı bu parametrelere bağlıdır. Belgeye göre eq açık ve frozen açıksa hash üretilir; eq açık ama frozen kapalıysa nesneler hashlenemez olur. Yani değişebilir bir dataclass nesnesini sözlük anahtarı ya da küme elemanı olarak kullanamazsınız. Bunu gerekiyorsa frozen ile çözün.
Bazı alanları anahtar kelimeye zorlamak için KW_ONLY işaretçisi de vardır: bu işaretten sonra gelen alanlar yalnız adla verilir. Miras yoluyla gelen alan sırası sorunlarını (varsayılanlı alanın sonrasında varsayılansız alan gelmesi) çözmek için kw_only kullanışlıdır.
__post_init__ ile doğrulama ve türetilmiş alanlar
Üretilen __init__ alanları atadıktan sonra __post_init__ metodunu çağırır; varsa. Bu kanca şu işlere yarar: başka alanlardan türetilen bir değeri hesaplamak ve girdileri denetlemek.
from dataclasses import dataclass, field
@dataclass class Fatura: ara_toplam: float vergi_orani: float toplam: float = field(init=False)
def __post_init__(self): if not isinstance(self.ara_toplam, (int, float)): raise TypeError("ara toplam sayı olmalı") self.toplam = self.ara_toplam + self.ara_toplam * self.vergi_orani
Burada toplam alanı init=False ile oluşturucudan çıkarılmış, değeri post-init içinde hesaplanmıştır. Kendi projenizde para tutarları için kayan nokta yerine Decimal gibi bir tür kullanmak yuvarlama hatalarını önler.
Belgenin vurguladığı bir ayrıntı daha var: üretilen __init__ üst sınıfların __init__ metodunu çağırmaz. Dataclass bir üst sınıftan türüyorsa ve onun başlatma koduna ihtiyaç varsa, __post_init__ içinde elle çağırmanız gerekir. InitVar ile işaretlenen sahte alanlar oluşturucuya girer ama nesne alanı olmaz; yalnızca __post_init__'e parametre olarak aktarılır.
Dataclass doğrulama yapmaz: Pydantic ne zaman devreye girer?
Tip ipuçları Python'da çalışma anında zorlanmaz. Bir dataclass alanına fiyat: float yazmış olmanız, nesneyi oluştururken metin geçmenizi engellemez; hata ancak o değeri kullandığınız yerde, uzaktan ortaya çıkar. Dışarıdan gelen veriyle çalışıyorsanız (API gövdesi, dosya, form), bu açık tehlikelidir.
Bu noktada Pydantic gibi doğrulama kütüphaneleri kullanılır. Pydantic belgesine göre BaseModel'den türeyen sınıfta alanlar tip ipuçlarıyla tanımlanır ve nesne oluşturulurken veri doğrulanır. Geçersiz girdi, hangi alanın neden başarısız olduğunu listeleyen tek bir ValidationError fırlatır. Varsayılan modda uyumlu girdiler tanımlı türe çevrilir; örneğin sayı içeren bir metin tamsayıya dönüştürülür, katı moda geçilirse bu dönüştürme kapanır. model_validate bir sözlükten model üretir, model_dump modeli sözlüğe geri çevirir.
from pydantic import BaseModel, ValidationError
class Siparis(BaseModel): alici: str adet: int
try: Siparis(alici="Ayşe", adet="bir") except ValidationError as hata: print(hata)
Pratik ayrım şudur: kendi kodunuzun içinde dolaşan, güvendiğiniz veri için dataclass yeterli ve hafiftir. Sistem sınırından, yani güvenmediğiniz kaynaktan giren veri için doğrulama kütüphanesi kullanın. Pydantic ayrıca dataclass benzeri bir arayüzü de destekler; böylece çoğu zaman aynı alan tanımlarıyla doğrulamayı ekleyebilirsiniz.
Sık hatalar ve kontrol listesi
- Değişebilir varsayılan: list ya da dict varsayılanı yazmak ValueError verir; field(default_factory=...) kullanın.
- Alan sırası: varsayılanlı alandan sonra varsayılansız alan gelirse TypeError alırsınız. Çözüm sırayı değiştirmek ya da kw_only kullanmaktır.
- Değişmeyen nesne beklerken frozen unutmak: nesne sessizce değiştirilir, önbellek ya da küme içinde tutarsızlık doğar.
- Tipleri güvence sanmak: dataclass doğrulama yapmaz; sistem sınırında ayrıca doğrulayın.
- order=True kullanıp alan sırasının anlamını düşünmemek: karşılaştırma alanları tanım sırasıyla yapar, yanlış alan başta ise sıralama beklenmedik çıkar.
- slots=True sonrası dinamik nitelik eklemeye çalışmak: AttributeError verir.
- Dataclass içinde ağır iş mantığı biriktirmek: sınıf büyüdükçe veri taşıyıcı olmaktan çıkar, ayrı bir sınıfa ya da fonksiyona taşıyın.
Bitirmeden önce kendinize sorun: bu nesne değişecek mi, sözlük anahtarı olacak mı, dışarıdan mı geliyor, kaç alanı var. Cevaplar sizi doğru parametre kombinasyonuna götürür.
Veri sınıflarını ve doğrulamayı nerede çalışırsınız?
Mindset'in Python ile Nesne Yönelimli Programlama eğitiminin modüllerinden biri Veri Sınıfları, Tipler ve Doğrulama başlığını taşır; öncesinde sınıf temelleri, kalıtım ve veri modeli konuları gelir, sonrasında tasarım desenleri ve metaclass ile kalite konuları yer alır.
Bu içerik, API ve servis yazan, konfigürasyon ya da iş nesnelerini sözlüklerle taşıyıp zamanla karmaşadan şikayet eden Python ekipleri için uygundur. Kurumsal ekipler için pratik kazanım, veri nesnelerinin ortak bir yapıda (tipli, doğrulanan, değişmezliği kararlaştırılmış) tanımlanmasıdır; böylece kod incelemesinde 'bu sözlükte hangi anahtarlar var' tartışması azalır.
İlgili Mindset programı
Python ile Nesne Yönelimli Programlama
Sık sorulan sorular
Dataclass nesnesini sözlüğe ya da JSON'a nasıl çeviririm?
dataclasses modülündeki asdict fonksiyonu nesneyi alan adlarıyla sözlüğe çevirir; iç içe dataclass'lar da çevrilir. Sözlüğü json.dumps ile metne dönüştürebilirsiniz. Tarih gibi JSON'un doğrudan tanımadığı türler için özel bir dönüştürücü gerekir. Tersi yönde, dışarıdan gelen JSON'u nesneye çevirmek ve doğrulamak istiyorsanız Pydantic daha rahat bir yoldur.
namedtuple ile dataclass arasındaki fark nedir?
namedtuple, alanlara ada göre erişilebilen değişmez bir demettir; indekslenebilir, açılabilir ve hafiftir. Dataclass ise normal bir sınıftır: alanlar değiştirilebilir (frozen demezseniz), metot ekleyebilir, varsayılan fabrikası tanımlayabilir ve miras alabilirsiniz. Demet davranışına ihtiyacınız yoksa dataclass çoğu zaman daha esnektir.
Neden bazı dataclass nesneleri sözlük anahtarı olarak kullanılamıyor?
Belgeye göre eq açık ve frozen kapalı olduğunda __hash__ None yapılır; yani nesne hashlenemez. Bunun mantığı basittir: nesnenin alanları değişebiliyorsa ve karşılaştırma alanlara bağlıysa, hash değeri de değişir ve sözlük bozulur. Anahtar olarak kullanılacaksa frozen=True verin.
slots=True ne zaman kullanılmalı?
Çok sayıda küçük nesne oluşturuluyorsa (büyük veri satırı yığınları gibi) slots bellek tasarrufu sağlar ve yanlış yazılmış nitelik adlarını erken yakalar, çünkü tanımsız niteliğe atama yapılamaz. Dinamik nitelik ekleyen kod ya da bazı çoklu kalıtım senaryoları ile uyumsuz olabilir; bu yüzden gerçek bir ihtiyaç varsa açın.