Tutorial Python: Menguasai Modul DataClasses
Dataclasses adalah modul Python yang pertama kali diperkenalkan di Python 3.7. Modul dataclasses
bertujuan untuk memudahkan pembuatan kelas di mana fungsi utamanya adalah untuk menyimpan data. Dengan menggunakan dataclasses
, kita bisa membuat kelas data secara lebih efisien dan praktis karena modul ini mampu mengurangi banyak boilerplate code yang biasanya diperlukan saat mendefinisikan kelas biasa di Python. Selain itu, dataclasses
secara otomatis menyediakan metode seperti __init__
, __repr__
, __eq__
, dan lainnya, sehingga kita tidak perlu mendefinisikannya secara manual.
Berikut ini adalah beberapa keuntungan menggunakan dataclasses
untuk menyimpan struktur data dibandingkan kelas biasa:
- Otomatisasi metode standar:
dataclasses
secara otomatis menghasilkan metode-metode standar seperti__init__
,__repr__
,__eq__
dan__hash__
(jika diperlukan). Hal ini menjadikan proses pembuatan kelas lebih efisien dan mengurangi kesalahan. - Kemudahan penggunaan: membuat kelas data dengan default values, validasi tipe, dan fitur lainnya lebih mudah dilakukan dengan dataclasses. Kita bisa dengan cepat dan sederhana mendefinisikan atribut-atribut kelas sesuai kebutuhan tanpa perlu menulis kode tambahan.
- Immutability:
dataclasses
memungkinkan kita untuk membuat kelas yang immutable dengan mudah cukup menggunakan parameterfrozen=True
. Dengan ini, objek yang dibuat dari kelas tersebut tidak bisa diubah setelah dibuat, sehingga untuk keperluan tertentu menjadi lebih aman dan stabil. - Pengelolaan metadata:
dataclasses
memungkinkan penambahan metadata ke dalam field menggunakan fungsifield()
. Adanya metadata akan mempermudah pengelolaan informasi tambahan yang mungkin diperlukan dalam konteks tertentu tanpa mengganggu struktur utama dari kelas data.
Secara keseluruhan, dataclasses
memberikan cara yang lebih efisien, praktis, dan fleksibel untuk mendefinisikan kelas data di Python, sehingga sangat bermanfaat bagi pengembang yang sering bekerja dengan aplikasi berbasis data (data-driven applications).
Membuat Dataclasses
Pembuatan dataclasses
cukup dengan mendefinisikan kelas seperti biasa dan menambahkan dekorator @dataclass
. Misalkan kita membuat sebuah dataclass
dengan nama Employee
dengan 5 atribut yaitu id
, name
, tenure
, position
dan salary
.
note: Tipe data dalam bahasa python bersifat dinamis (dinamic typing), yaitu ditentukan saat kode dijalankan. Sehingga suatu variabel dapat menyimpan tipe apapun sesuai nilai yang diberikan. Untuk membuat kode lebih mudah dibaca kita dapat menambahkan informasi tipe data pada variabel (type hinting) seperti pada contoh kode ini (misal id: int
). Namun, perlu diingat hal ini hanya bertujuan agar kode mudah dibaca saja dan tidak akan menyebabkan error ketika diberikan nilai dengan tipe apapun yang berbeda.
Python
from dataclasses import dataclass @dataclass class Employee: id: int name: str tenure: int position: str salary: float
Inisiasi Objek Dataclasses
Setelah dataclass
Employee
dibuat, maka kita dapat menggunakannya untuk membuat objek dataclass
tersebut. Mari kita buat satu objek dari Employee
dengan menentukan nilai setiap atributnya.
Python
# Membuat objek Employee tonystark = Employee( id="AVG-01", name="Tony Stark", tenure=10, position="Manager", salary=500_000, ) # Mencetak objek print(tonystark)
# OUTPUT Employee(id='AVG-01', name='Tony Stark', tenure=10, position='Manager', salary=500000)
Contoh dataclass
di atas, mengharuskan kita untuk menetapkan nilai dari setiap atributnya. Jika suatu objek diinisiasi tanpa beberapa atributnya maka akan menyebabkan error, misalkan seperti contoh berikut:
Python
# Membuat Objek Employee (Error) # ada atribut yang tidak ditentukan steveroger = Employee( id="AVG-02", name="Steve Roger", )
# OUTPUT Traceback (most recent call last): File "C:\Users\cahya\OneDrive\Documents\11. sainsdata.id\Posts (041-080)\080. Dataclasses Python\employee.py", line 29, in <module> steveroger = Employee( ^^^^^^^^^ TypeError: Employee.__init__() missing 3 required positional arguments: 'tenure', 'position', and 'salary'
Nilai Default
Terkadang, kita mungkin ingin menetapkan nilai default untuk beberapa atribut dalam sebuah dataclass
. Praktik ini sangat bermanfaat ketika ada nilai-nilai tertentu yang sering digunakan atau secara logis lebih masuk akal jika memiliki nilai default. Dengan memberikan nilai default, kita dapat mengurangi kebutuhan untuk menetapkan nilai-nilai tersebut setiap kali membuat instance baru dari kelas, sehingga kode menjadi lebih bersih dan efisien.
Mari kita perbarui dataclass
Employee
dengan menambahkan nilai default misalkan untuk atribut tenure
, position
dan salary
. Nilai default untuk tenure
kita atur menjadi 0
, position
menjadi "Staf"
dan salary
menjadi 50.000
. Selanjutnya mari buat dua objek lagi dan memanfaatkan nilai default yang sudah ditentukan.
Objek pertama yaitu steveroger
, diinisiasi tanpa atribut tenure
, position
dan salary
. Dengan demikian nilai ketiga atribut tersebut akan menggunakan nilai defaultnya. Objek berikutnya adalah kamalakhan
, di mana kita inisiasi tanpa atribut tenure
sehingga juga akan menggunakan nilai defaultnya yaitu 0
.
Python
@dataclass class Employee: id: int name: str tenure: int = 0 position: str = "Staf" salary: float = 50_000 # Membuat objek baru hanya dengan atribut id dan name steveroger = Employee( id="AVG-02", name="Steve Roger" ) # Membuat objek baru tanpa atribut tenure kamalakhan = Employee( id="AVG-03", name="Kamala Khan", position="Intern", salary=25_000 print(kamalakhan) # merubah nilai atribut tenure kamalakhan.tenure = 1 print(kamalakhan)
# OUTPUT Employee(id='AVG-02', name='Steve Roger', tenure=0, position='Staf', salary=50000) Employee(id='AVG-03', name='Kamala Khan', tenure=0, position='Intern', salary=25000) Employee(id='AVG-03', name='Kamala Khan', tenure=1, position='Intern', salary=25000)
Frozen Dataclasses
Pada kondisi tertentu kita mungkin memerlukan data-data yang tidak dapat dirubah (immutable) setelah dibuat. Penggunaan dataclass
memungkinkan kita untuk menyimpan data yang bersifat immutable tersebut. dataclass
immutable
dapat dibuat dengan menambahkan parameter frozen=True
pada dekorator-nya.
Ketika dataclass
diatur dengan frozen=True
maka atribut dari objek dataclass
tersebut tidak dapat dimodifikasi dan akan menghasilkan error jika dilakukan.
Python
@dataclass(frozen=True) class Employee: id: int name: str tenure: int = 0 position: str = "Staf" salary: float = 50_000 brucebanner = Employee(id="AVG-4", name="Bruce Banner") print(brucebanner, "\n") # Merubah Nilai salary (Error) brucebanner.salary = 10_000_000
# OUTPUT Employee(id='AVG-4', name='Bruce Banner', tenure=0, position='Staf', salary=50000) Traceback (most recent call last): File "C:\Users\cahya\OneDrive\Documents\11. sainsdata.id\Posts (041-080)\080. Dataclasses Python\employee.py", line 65, in <module> brucebanner.salary = 10_000_000 ^^^^^^^^^^^^^^^^^^ File "<string>", line 4, in __setattr__ dataclasses.FrozenInstanceError: cannot assign to field 'salary'
Method
Seperti kelas pada umumnya, dataclass
juga dapat memiliki method untuk mengerjakan berbagai fungsi. Pembuatan method-nya juga sama dengan kelas biasa. Contoh di bawah ini adalah dataclass
Employee
dengan tambahan 2 method untuk menghitung nilai pajak dan untuk menambahkan salary
dengan nilai tertentu.
Python
@dataclass class Employee: id: int name: str tenure: int = 0 position: str = "Staf" salary: float = 50_000 # method menghitung tax berdasarkan salary def calculate_tax(self): if self.salary > 100_000: return 0.15 * self.salary elif self.salary > 50_000: return 0.10 * self.salary else: return 0.05 * self.salary # method penambahan salary def increase_salary(self, nominal): self.salary += nominal # membuat objek baru natasharomanoff = Employee( id="AVG-05", name="Natasha Romanoff", position="Supervisor", salary=250_000, ) print(natasharomanoff) # menghitung Tax objek natasharomanoff nr_tax = natasharomanoff.calculate_tax() print(f"Tax: {nr_tax}") # menaikkan Salary objek natasharomanoff natasharomanoff.increase_salary(50_000) print(f"New Salary: {natasharomanoff.salary}") print(f"New Tax: {natasharomanoff.calculate_tax()}")
# OUTPUT Employee(id='AVG-05', name='Natasha Romanoff', tenure=0, position='Supervisor', salary=250000) Tax: 37500.0 New Salary: 300000 New Tax: 45000.0
Perbandingan Objek
Secara default dataclass
secara otomatis memiliki method __eq__, sehingga kita dapat membandingkan kesamaan dua buah objek. Objek akan dianggap sama, jika dan hanya jika nilai pada setiap atributnya juga sama. Selain itu, ojek-objek dari suatu dataclass
juga dapat diatur sehingga memiliki urutan tertentu tanpa mendefinisikannya secara eksplisit. Untuk membandingkan objek menggunakan operator <
, >
, <=
, atau >=
, kita bisa menggunakan decorator @dataclass(order=True)
. Pengaturan ini akan membuat secara otomatis metode perbandingan (__lt__
, __le__
, __gt__
, __ge__
) berdasarkan urutan field yang didefinisikan dalam dataclass
.
Pada contoh dataclass
yang kita miliki, maka objek dengan atribut id
yang lebih kecil akan dianggap lebih kecil. Jika nilai atribut tersebut sama, maka akan dilihat pada atribut berikutnya yaitu name
dan seterusnya sampai atribut terakhir yaitu salary
. Dengan sifat ini, maka kumpulan objek-objek (misalkan dalam bentuk list) dapat secara langsung diurutkan baik itu secara menaik (ascending) maupun menurun (descending).
Python
@dataclass(order=True) class Employee: id: int name: str tenure: int = 0 position: str = "Staf" salary: float = 50_000 # method menghitung tax berdasarkan salary def calculate_tax(self): if self.salary > 100_000: return 0.15 * self.salary elif self.salary > 50_000: return 0.10 * self.salary else: return 0.05 * self.salary # method penambahan salary def increase_salary(self, nominal): self.salary += nominal # Membuat 4 objek Employee em_1 = Employee(id="AVG-10", name="Peter Parker", tenure=8) em_2 = Employee(id="AVG-10", name="Peter Parker", tenure=10) em_3 = Employee(id="AVG-08", name="Thor", tenure=12) em_4 = Employee(id="AVG-09", name="Peter Quill", tenure=6) print(em_1 == em_2) # False print(em_1 < em_2) # True (karena em_2.tenure > em_1.tenure) print(em_3 < em_4) # True (karena em_3.id < em_3.id) # Membuat list dengan 4 objek Employee list_em = [em_1, em_2, em_3, em_4] # Mengurutkan elemen ascending list_em.sort() print("\nPengurutan Ascending:") for em in list_em: print(em) # Mengurutkan elemen descending list_em.sort(reverse=True) print("\nPengurutan Descending") for em in list_em: print(em)
# OUTPUT False True True Pengurutan Ascending: Employee(id='AVG-08', name='Thor', tenure=12, position='Staf', salary=50000) Employee(id='AVG-09', name='Peter Quill', tenure=6, position='Staf', salary=50000) Employee(id='AVG-10', name='Peter Parker', tenure=8, position='Staf', salary=50000) Employee(id='AVG-10', name='Peter Parker', tenure=10, position='Staf', salary=50000) Pengurutan Descending Employee(id='AVG-10', name='Peter Parker', tenure=10, position='Staf', salary=50000) Employee(id='AVG-10', name='Peter Parker', tenure=8, position='Staf', salary=50000) Employee(id='AVG-09', name='Peter Quill', tenure=6, position='Staf', salary=50000) Employee(id='AVG-08', name='Thor', tenure=12, position='Staf', salary=50000)
Field dan Metadata
Metadata adalah informasi tambahan yang kita tambahkan ke field untuk menyediakan detail tambahan yang mungkin berguna untuk berbagai tujuan. Berikut beberapa fungsi dari metadata pada dataclass
:
- Dokumentasi: metadata dapat digunakan untuk mendokumentasikan tujuan atau penggunaan field tertentu. Ini sangat berguna untuk memberikan penjelasan tambahan kepada pengembang lain yang menggunakan kelas tersebut.
- Validasi: metadata dapat digunakan untuk menyimpan aturan validasi untuk field. Misalnya batasan nilai atau tipe data yang valid untuk field tertentu.
- Konfigurasi: metadata dapat menyimpan konfigurasi tambahan yang digunakan oleh alat atau library eksternal. Misalnya pada ORM (Object-Relational Mapping), dapat menggunakan metadata untuk memetakan field ke kolom database.
- User Interface: metadata dapat digunakan untuk menentukan bagaimana field ditampilkan atau diedit dalam antarmuka pengguna. Misalnya pada label atau hint untuk input form.
- Analitik: metadata dapat menyimpan informasi tambahan yang berguna untuk analitik atau pemrosesan data lebih lanjut.
Pada contoh di bawah ini, kita membuat nilai atribut tenure
dan salary
menggunakan field
. Di dalam field
kita dapat menentukan nilai default-nya pada parameter default
dan parameter metadata
berupa dictionary
yang dapat berisi informasi apapun. Misalkan untuk atribut tenure
kita tambahkan metadata help
dan untuk salary
berupa metadata dengan tiga key yaitu help
, min
dan max
.
Kita juga menambahkan method baru yaitu check_salary_range
di mana dilakukan pengecekan nilai minimum dan maksimum yang tersimpan di dalam metadata, dan jika nilai salary
tidak sesuai akan menampilkan informasi ketidaksesuaian tersebut. Denganca ra yang sama tentunya kita juga dapat mengecek metadata lainnya misalkan help
.
Perlu diingat metadata bukanlah bentuk validasi isian data atribut. Nilai apapun yang disimpan di dalam metadata tidak akan menghalangi atribut tersebut untuk memiliki nilai yang tidak sesuai. Namun tentunya informasi di dalamnya bisa menjadi dokumentasi yang nantinya dapat diakses jika diperlukan.
Python
from dataclasses import dataclass, Field @dataclass(order=True) class Employee: id: int name: str tenure: int = field(default=0, metadata={"help": "Tenure (in months)"}) position: str = "Staf" salary: float = field( default=50_000, metadata={ "help": "Annual Salary in USD", "min": 10_000, "max": 1_000_000, }, ) # method menghitung tax berdasarkan salary def calculate_tax(self): if self.salary > 100_000: return 0.15 * self.salary elif self.salary > 50_000: return 0.10 * self.salary else: return 0.05 * self.salary # method penambahan salary def increase_salary(self, nominal): self.salary += nominal # method pengecekan nilai salary berdasarkan metadata def check_salary_range(self): info = self.__dataclass_fields__["salary"].metadata min = info.get("min", 0) # ambil nilai min dari metadata (jika tidak ada maka 0) max = info.get("max", 0) # ambil nilai max dari metadata (jika tidak ada maka 0) if self.salary > max or self.salary < min: print("Nilai Salary di luar batas yang ditentukan ") logan = Employee(id="AVG-99", name="Logan", salary=2_000_000) print(logan) logan.check_salary_range()
# OUTPUT Employee(id='AVG-99', name='Logan', tenure=0, position='Staf', salary=2000000) Nilai Salary di luar batas yang ditentukan
Misalkan untuk keperluan dokumentasi kita tambahkan fungsi menampilkan semua informasi help
bagi dataclass
Employee
.
Python
def print_help(cls): for field_name, field_info in cls.__dataclass_fields__.items(): help_text = field_info.metadata.get("help", "No help available") print(f"{field_name}: {help_text}") # Menggunakan fungsi print_help print_help(Employee)
# OUTPUT id: No help available name: No help available tenure: Tenure (in months) position: No help available salary: Annual Salary in USD
Kesimpulan
dataclasses
di Python menyediakan teknik yang sederhana dan efisien untuk mendefinisikan kelas yang digunakan terutama untuk menyimpan data. Dengan menggunakan decorator @dataclass
, kita bisa secara otomatis menghasilkan metode-metode umum dan mengurangi jumlah kode yang harus kita tulis. Untuk informasi selengkapmya, silahkan melihat dokumentasi resmi Python tentang dataclasses
di sini.