Salah satu fitur baru yang diperkenalkan di Python 3.7 adalah kelas Data. Mereka dirancang untuk mengotomatisasi pembuatan kode untuk kelas yang digunakan untuk menyimpan data. Terlepas dari kenyataan bahwa mereka menggunakan mekanisme kerja lain, mereka dapat dibandingkan dengan "tuple bernama yang bisa berubah dengan nilai default."
Pendahuluan
Semua contoh di atas memerlukan Python 3.7 atau lebih tinggi untuk operasi mereka.
Sebagian besar pengembang python harus menulis kelas-kelas ini secara teratur:
class RegularBook: def __init__(self, title, author): self.title = title self.author = author 
Sudah dalam contoh ini, redundansi terlihat. Judul dan pengidentifikasi penulis digunakan beberapa kali. Kelas nyata juga akan berisi metode yang ditimpa __eq__ dan __repr__ .
Modul dataclasses berisi dekorator @dataclass . Dengan menggunakannya, kode yang mirip akan terlihat seperti ini:
 from dataclasses import dataclass @dataclass class Book: title: str author: str 
Penting untuk dicatat bahwa anotasi jenis diperlukan . Semua bidang yang tidak memiliki tanda jenis akan diabaikan. Tentu saja, jika Anda tidak ingin menggunakan jenis tertentu, Anda dapat menentukan Any dari modul typing .
Apa yang Anda dapatkan sebagai hasilnya? Anda secara otomatis mendapatkan kelas, dengan metode yang diterapkan __init__ , __repr__ , __eq__ dan __eq__ . Selain itu, ini akan menjadi kelas reguler dan Anda dapat mewarisi darinya atau menambahkan metode sewenang-wenang.
 >>> book = Book(title="Fahrenheit 451", author="Bradbury") >>> book Book(title='Fahrenheit 451', author='Bradbury') >>> book.author 'Bradbury' >>> other = Book("Fahrenheit 451", "Bradbury") >>> book == other True 
Alternatif
Tuple atau kamus
Tentu saja, jika strukturnya cukup sederhana, Anda dapat menyimpan data dalam kamus atau tuple:
 book = ("Fahrenheit 451", "Bradbury") other = {'title': 'Fahrenheit 451', 'author': 'Bradbury'} 
Namun, pendekatan ini memiliki kelemahan:
- Harus diingat bahwa variabel berisi data yang terkait dengan struktur ini.
- Dalam hal kamus, Anda harus melacak nama-nama tombol. Inisialisasi kamus {'name': 'Fahrenheit 451', 'author': 'Bradbury'}juga akan benar secara formal.
- Dalam kasus tuple, Anda harus melacak urutan nilai, karena mereka tidak memiliki nama.
Ada opsi yang lebih baik:
Namedtuple
 from collections import namedtuple NamedTupleBook = namedtuple("NamedTupleBook", ["title", "author"]) 
Jika kita menggunakan kelas yang dibuat dengan cara ini, kita mendapatkan hal yang sama seperti menggunakan kelas data.
 >>> book = NamedTupleBook("Fahrenheit 451", "Bradbury") >>> book.author 'Bradbury' >>> book NamedTupleBook(title='Fahrenheit 451', author='Bradbury') >>> book == NamedTupleBook("Fahrenheit 451", "Bradbury")) True 
Namun terlepas dari kesamaan umum, tuple bernama memiliki keterbatasan. Mereka datang dari fakta bahwa tuple bernama masih tuple.
Pertama, Anda masih dapat membandingkan contoh kelas yang berbeda.
 >>> Car = namedtuple("Car", ["model", "owner"]) >>> book = NamedTupleBook("Fahrenheit 451", "Bradbury")) >>> book == Car("Fahrenheit 451", "Bradbury") True 
Kedua, tuple bernama tidak dapat diubah. Dalam beberapa situasi, ini berguna, tetapi saya ingin lebih banyak fleksibilitas.
Akhirnya, Anda dapat beroperasi pada tuple bernama juga yang biasa. Misalnya, beralih lagi.
Proyek lainnya
Jika tidak terbatas pada perpustakaan standar, Anda dapat menemukan solusi lain untuk masalah ini. Secara khusus, proyek ini menarik. Ia dapat melakukan lebih dari sekadar dataclass dan bekerja pada versi python yang lebih lama seperti 2.7 dan 3.4. Namun demikian, fakta bahwa itu bukan bagian dari perpustakaan standar mungkin merepotkan
Ciptaan
Anda dapat menggunakan dekorator @dataclass untuk membuat kelas data. Dalam hal ini, semua bidang kelas yang ditentukan dengan anotasi tipe akan digunakan dalam metode yang sesuai dari kelas yang dihasilkan.
Sebagai alternatif, ada fungsi make_dataclass , yang berfungsi serupa untuk membuat tupel bernama.
 from dataclasses import make_dataclass Book = make_dataclass("Book", ["title", "author"]) book = Book("Fahrenheit 451", "Bradbury") 
Nilai default
Salah satu fitur yang bermanfaat adalah kemudahan menambahkan nilai default ke bidang. Masih tidak perlu mendefinisikan ulang metode __init__ , cukup tentukan nilai langsung di kelas.
 @dataclass class Book: title: str = "Unknown" author: str = "Unknown author" 
Mereka akan diperhitungkan dalam metode __init__ dihasilkan
 >>> Book() Book(title='Unknown', author='Unknown author') >>> Book("Farenheit 451") Book(title='Farenheit 451', author='Unknown author') 
Tetapi seperti halnya kelas dan metode reguler, Anda harus berhati-hati menggunakan standar yang bisa berubah. Jika, misalnya, Anda perlu menggunakan daftar sebagai nilai default, ada cara lain, tetapi lebih pada itu di bawah ini.
Selain itu, penting untuk memantau urutan bidang mana dengan nilai default yang ditentukan, karena sama persis dengan urutannya dalam metode __init__
Kelas Data Tidak Berubah
Contoh tuple bernama tidak berubah. Dalam banyak situasi, ini adalah ide yang bagus. Untuk kelas data, Anda dapat melakukannya juga. Cukup tentukan parameter frozen=True saat membuat kelas, dan jika Anda mencoba mengubah bidangnya, pengecualian FrozenInstanceError akan FrozenInstanceError
 @dataclass(frozen=True) class Book: title: str author: str 
 >>> book = Book("Fahrenheit 451", "Bradbury") >>> book.title = "1984" dataclasses.FrozenInstanceError: cannot assign to field 'title' 
Pengaturan Kelas Data
Selain parameter frozen , dekorator @dataclass memiliki parameter lain:
- init: jika- True(default), metode- __init__dihasilkan. Jika kelas sudah memiliki metode- __init__yang ditentukan, parameter diabaikan.
- repr: memungkinkan (secara default) pembuatan metode- __repr__. String yang dihasilkan berisi nama kelas dan nama serta representasi dari semua bidang yang didefinisikan dalam kelas. Dalam hal ini, masing-masing bidang dapat dikecualikan (lihat di bawah)
- eq: memungkinkan (secara default) pembuatan metode- __eq__. Objek dibandingkan dengan cara yang sama seolah-olah mereka adalah tupel yang berisi nilai bidang yang sesuai. Selain itu, pencocokan jenis diperiksa.
- ordermemungkinkan (default tidak aktif) pembuatan metode- __lt__,- __le__,- __gt__dan- __ge__. Objek dibandingkan dengan cara yang sama dengan tupel nilai bidang yang sesuai. Pada saat yang sama, jenis objek juga diperiksa. Jika- orderditentukan, tetapi- eqtidak, pengecualian- ValueErrorakan dibuang. Juga, kelas tidak boleh berisi metode perbandingan yang sudah didefinisikan.
- unsafe_hashmemengaruhi pembuatan metode- __hash__. Perilaku juga tergantung pada nilai-nilai parameter- eqdan- frozen
Kustomisasi masing-masing bidang
Dalam sebagian besar situasi standar, ini tidak diperlukan, tetapi dimungkinkan untuk menyesuaikan perilaku kelas data hingga masing-masing bidang menggunakan fungsi bidang.
Default yang Dapat Diubah
Situasi khas yang disebutkan di atas adalah penggunaan daftar atau nilai default yang bisa berubah-ubah. Anda mungkin menginginkan kelas "rak buku" yang berisi daftar buku. Jika Anda menjalankan kode berikut:
 @dataclass class Bookshelf: books: List[Book] = [] 
interpreter akan melaporkan kesalahan:
 ValueError: mutable default <class 'list'> for field books is not allowed: use default_factory 
Namun, untuk nilai yang dapat diubah lainnya, peringatan ini tidak akan berfungsi dan akan menyebabkan perilaku program yang salah.
Untuk menghindari masalah, disarankan untuk menggunakan parameter default_factory dari fungsi field . Nilainya dapat berupa objek atau fungsi apa pun yang disebut tanpa parameter.
Versi kelas yang benar terlihat seperti ini:
 @dataclass class Bookshelf: books: List[Book] = field(default_factory=list) 
Pilihan lain
Selain default_factory ditentukan, fungsi bidang memiliki parameter berikut:
- default: nilai- default. Parameter ini diperlukan karena bidang ajakan menggantikan nilai bidang default.
- init: memungkinkan (default) penggunaan suatu bidang dalam metode- __init__
- repr: memungkinkan (default) penggunaan suatu bidang dalam metode- __repr__
- comparetermasuk (default) penggunaan bidang dalam metode perbandingan (- __eq__,- __le__dan lainnya)
- hash: mungkin nilai boolean atau- None. Jika- True, bidang digunakan untuk menghitung hash. Jika- Noneditentukan (secara default), nilai parameter- comparedigunakan.
 Salah satu alasan untuk menentukan- hash=Falseuntuk- compare=Truediberikan- compare=Truemungkin kesulitan menghitung hash bidang sementara itu diperlukan untuk perbandingan.
- metadata: kamus khusus atau- None. Nilai tersebut dibungkus dalam- MappingProxyTypesehingga menjadi tidak berubah. Parameter ini tidak digunakan oleh kelas data itu sendiri dan dimaksudkan untuk ekstensi pihak ketiga.
Memproses setelah inisialisasi
Metode __init__ dibuat secara otomatis memanggil metode __post_init__ , jika didefinisikan dalam kelas. Sebagai aturan, ini disebut dalam bentuk self.__post_init__() , namun, jika variabel tipe InitVar didefinisikan di kelas, mereka akan dilewatkan sebagai parameter metode.
Jika metode __init__ belum dihasilkan, maka __post_init__ tidak akan dipanggil.
Misalnya, tambahkan deskripsi buku yang dihasilkan
 @dataclass class Book: title: str author: str desc: str = None def __post_init__(self): self.desc = self.desc or "`%s` by %s" % (self.title, self.author) 
 >>> Book("Fareneheit 481", "Bradbury") Book(title='Fareneheit 481', author='Bradbury', desc='`Fareneheit 481` by Bradbury') 
Parameter untuk inisialisasi saja
Salah satu kemungkinan yang terkait dengan metode __post_init__ adalah parameter yang hanya digunakan untuk inisialisasi. Jika, ketika mendeklarasikan sebuah field, tentukan InitVar sebagai tipenya, nilainya akan diteruskan sebagai parameter dari metode __post_init__ . Tidak ada cara lain yang bidang yang digunakan dalam kelas data.
 @dataclass class Book: title: str author: str gen_desc: InitVar[bool] = True desc: str = None def __post_init__(self, gen_desc: str): if gen_desc and self.desc is None: self.desc = "`%s` by %s" % (self.title, self.author) 
 >>> Book("Fareneheit 481", "Bradbury") Book(title='Fareneheit 481', author='Bradbury', desc='`Fareneheit 481` by Bradbury') >>> Book("Fareneheit 481", "Bradbury", gen_desc=False) Book(title='Fareneheit 481', author='Bradbury', desc=None) 
Warisan
Ketika Anda menggunakan dekorator @dataclass , ia melewati semua kelas induk dimulai dengan objek dan untuk setiap kelas data menemukan itu menyimpan bidang dalam kamus yang diurutkan, kemudian menambahkan properti dari kelas yang diproses. Semua metode yang dihasilkan menggunakan bidang dari kamus yang dipesan.
Akibatnya, jika kelas induk mendefinisikan nilai default, Anda harus mendefinisikan bidang dengan nilai default.
Karena kamus yang diurutkan menyimpan nilai-nilai dalam urutan penyisipan, untuk kelas-kelas berikut
 @dataclass class BaseBook: title: Any = None author: str = None @dataclass class Book(BaseBook): desc: str = None title: str = "Unknown" 
metode __init__ dengan tanda tangan ini akan dihasilkan:
 def __init__(self, title: str="Unknown", author: str=None, desc: str=None)