Pendahuluan — object yang “bicara” saat di-print
Di Attribute & __init__ (#42) kita isi state. Di Composition (#47) kita susun siapa punya siapa. Hari ini: bagaimana object menampilkan diri dan dibandingkan — lewat special methods (kadang disebut dunder method: nama ber-underscore ganda) seperti __str__, __repr__, __eq__, plus shortcut modern @dataclass.
Tanpa special method, print(buku) sering hanya alamat memori yang sulit dibaca. Dengan special method, object ikut “sopan” di log, debugger, dan koleksi.
Prasyarat: Nyaman dengan Attribute (#42) dan sudah baca Composition (#47). Fondasi: Encapsulation (#43) · Class & Object (#41) · Mengenal OOP (#40).
Default yang kurang ramah
Class sederhana tanpa special method:
class Buku:
def __init__(self, judul, penulis):
self.judul = judul
self.penulis = penulis
b = Buku("ESP32 Praktis", "Budi")
print(b)
print([b])
Output kurang lebih (alamat hex berbeda tiap jalankan):
<__main__.Buku object at 0x...>
[<__main__.Buku object at 0x...>]
Itu bukan bug — Python belum tahu cara menampilkan objectmu. Kita ajarkan lewat special method.
__str__ — untuk manusia
__str__ dipanggil oleh str(obj) dan print(obj). Isinya sebaiknya singkat dan mudah dibaca orang.
class Buku:
def __init__(self, judul, penulis):
self.judul = judul
self.penulis = penulis
def __str__(self):
return f"{self.judul} oleh {self.penulis}"
b = Buku("ESP32 Praktis", "Budi")
print(b)
print(str(b))
Output:
ESP32 Praktis oleh Budi
ESP32 Praktis oleh Budi
__repr__ — untuk developer
__repr__ dipakai saat object muncul di list/dict, REPL, dan log debug. Idealnya jelas dan (bila masuk akal) mirip cara membuat ulang object.
class Buku:
def __init__(self, judul, penulis):
self.judul = judul
self.penulis = penulis
def __str__(self):
return f"{self.judul} oleh {self.penulis}"
def __repr__(self):
return f"Buku(judul={self.judul!r}, penulis={self.penulis!r})"
b = Buku("ESP32 Praktis", "Budi")
print(b) # __str__
print([b]) # __repr__ di dalam list
print(repr(b)) # __repr__ eksplisit
Output:
ESP32 Praktis oleh Budi
[Buku(judul='ESP32 Praktis', penulis='Budi')]
Buku(judul='ESP32 Praktis', penulis='Budi')
Kalau hanya __repr__ yang ada, print juga memakainya sebagai fallback. Kalau hanya __str__, list masih memakai default alamat memori — jadi sering tulis keduanya.
__str__; developer/debug baca __repr__.__eq__ — kapan dua object “sama”?
Tanpa __eq__, dua instance dengan data sama tetap != karena dibandingkan identitas (alamat), bukan isi:
class Buku:
def __init__(self, judul, penulis):
self.judul = judul
self.penulis = penulis
a = Buku("ESP32 Praktis", "Budi")
b = Buku("ESP32 Praktis", "Budi")
print("tanpa __eq__, a == b?", a == b)
print("a is b?", a is b)
Output:
tanpa __eq__, a == b? False
a is b? False
Sekarang tambahkan __eq__ supaya perbandingan memakai nilai field:
class Buku:
def __init__(self, judul, penulis):
self.judul = judul
self.penulis = penulis
def __eq__(self, other):
if not isinstance(other, Buku):
return NotImplemented
return self.judul == other.judul and self.penulis == other.penulis
a = Buku("ESP32 Praktis", "Budi")
b = Buku("ESP32 Praktis", "Budi")
c = Buku("Belajar Python", "Sari")
print("a == b?", a == b)
print("a == c?", a == c)
print("a is b?", a is b)
Output:
a == b? True
a == c? False
a is b? False
== soal kesetaraan nilai; is soal object yang sama di memori. Untuk koleksi di Composition (#47), __eq__ membantu cek “buku sudah ada di katalog?” tanpa bergantung pada identitas instance.
@dataclass — class data tanpa boilerplate
Banyak class hanya menyimpan field + ingin __init__, __repr__, dan __eq__ otomatis. Modul dataclasses (stdlib) mengurangi boilerplate itu.
from dataclasses import dataclass
@dataclass
class Buku:
judul: str
penulis: str
tahun: int = 2024
b1 = Buku("ESP32 Praktis", "Budi", 2023)
b2 = Buku("ESP32 Praktis", "Budi", 2023)
print(b1)
print("b1 == b2?", b1 == b2)
print("repr:", repr(b1))
Output kurang lebih:
Buku(judul='ESP32 Praktis', penulis='Budi', tahun=2023)
b1 == b2? True
repr: Buku(judul='ESP32 Praktis', penulis='Budi', tahun=2023)
Catatan: default @dataclass memakai __repr__ bergaya field; kalau mau teks ramah manusia terpisah, tetap tulis __str__ sendiri.
from dataclasses import dataclass
@dataclass
class Buku:
judul: str
penulis: str
def __str__(self):
return f"{self.judul} oleh {self.penulis}"
print(Buku("Belajar Python", "Sari"))
print([Buku("Belajar Python", "Sari")])
Output:
Belajar Python oleh Sari
[Buku(judul='Belajar Python', penulis='Sari')]
Class biasa vs dataclass — kapan pilih apa?
- Dataclass — banyak field, sedikit perilaku khusus; ingin
__init__/__repr__/__eq__otomatis - Class biasa — validasi kompleks di
__init__, banyak method bisnis, atau warisan/ABC yang sudah ramai (lihat ABC (#46)) - Campur — dataclass untuk model data; class biasa untuk layanan yang punya koleksi model (Composition (#47))
Dataclass bukan “pengganti OOP”. Ia mengurangi boilerplate di class yang memang berpusat pada data.
Pola Dasar — special methods & dataclass
-
1
Tulis
__str__dulu Kalau object sering di-print ke log/UI, buat teks singkat untuk manusia. -
2
Tambah
__repr__untuk debug Tampilkan nama class + field penting;!rmembantu string ber-quote. -
3
Definisikan
__eq__bila nilai penting Bandingkan field yang masuk akal; kembalikanNotImplementeduntuk tipe asing. -
4
Pertimbangkan
@dataclassField banyak + perilaku tipis? Biarkan stdlib generate boilerplate. -
5
Jangan paksa dataclass ke mana-mana Layanan kompleks, validasi ketat, atau API tebal — class biasa (atau property) sering lebih jujur.
__str__ manusia · __repr__ debug · __eq__ nilai · dataclass untuk data-heavy.Kode lengkap — salin & jalankan
Simpan sebagai buku_special_methods.py, lalu python buku_special_methods.py:
from dataclasses import dataclass
@dataclass
class Buku:
judul: str
penulis: str
tahun: int
def __str__(self):
return f"{self.judul} · {self.penulis} ({self.tahun})"
class Katalog:
"""Composition: punya daftar Buku (dataclass)."""
def __init__(self, nama):
self.nama = nama
self.items = []
def tambah(self, buku):
if buku in self.items:
return False
self.items.append(buku)
return True
def __str__(self):
return f"Katalog {self.nama}: {len(self.items)} buku"
def __repr__(self):
return f"Katalog(nama={self.nama!r}, items={self.items!r})"
kat = Katalog("Kota A")
b1 = Buku("ESP32 Praktis", "Budi", 2023)
b2 = Buku("ESP32 Praktis", "Budi", 2023) # nilai sama, instance beda
b3 = Buku("Belajar Python", "Sari", 2024)
print(b1)
print("tambah b1:", kat.tambah(b1))
print("tambah b2 (duplikat nilai):", kat.tambah(b2))
print("tambah b3:", kat.tambah(b3))
print(kat)
print("items:", kat.items)
print("b1 == b2?", b1 == b2)
Output yang diharapkan (kurang lebih):
ESP32 Praktis · Budi (2023)
tambah b1: True
tambah b2 (duplikat nilai): False
tambah b3: True
Katalog Kota A: 2 buku
items: [Buku(judul='ESP32 Praktis', penulis='Budi', tahun=2023), Buku(judul='Belajar Python', penulis='Sari', tahun=2024)]
b1 == b2? True
Di sini dataclass memberi __eq__ otomatis; Katalog class biasa memakai composition + __str__/__repr__ sendiri — pola yang sering muncul di proyek nyata.
Kesalahan umum
| Gejala | Penyebab tipikal | Perbaikan |
|---|---|---|
print masih alamat memori |
Belum ada __str__/__repr__ |
Tambah minimal salah satu; idealnya keduanya |
List menampilkan jelek meski print bagus |
Hanya __str__, tanpa __repr__ |
Tulis __repr__ juga |
Dua buku “sama” tapi == False |
Belum __eq__ (atau bukan dataclass) |
Implement __eq__ atau pakai @dataclass |
Bingung == vs is |
Menyamakan kesetaraan nilai dengan identitas | == nilai; is object yang sama di memori |
| Dataclass dipakai untuk service tebal | Memaksa shortcut ke class yang banyak side-effect | Class biasa + composition; dataclass untuk model data |
TypeError: unhashable type setelah tulis __eq__ |
Custom __eq__ membuat object tidak hashable (default) |
Untuk Seri 3: bandingkan dengan == / in list. Jangan masukkan ke set/dict key sebelum paham __hash__ |
Latihan singkat
- Tambah
__str__pada classEbook(lihat pola di Inheritance (#44)) yang menampilkan format file. - Ubah
Katalog.tambahagar menolak duplikat berdasarkanjudulsaja (bukan seluruh field). - Bandingkan: class
Bukumanual dengan__init__/__repr__/__eq__vs versi@dataclass— mana yang lebih pendek?
FAQ singkat
Apakah harus selalu tulis __str__ dan __repr__?
Tidak wajib, tapi sangat membantu begitu object sering di-print atau debug. Dataclass sudah memberi __repr__ default.
Apa bedanya dengan Encapsulation (#43)?
Encapsulation mengatur akses/validasi field. Special methods mengatur bagaimana object ditampilkan dan dibandingkan.
Apakah dataclass bisa inheritance?
Bisa, dengan hati-hati (urutan field default). Untuk belajar Seri 3, mulai dari dataclass “datar” dulu.
Kenapa kembalikan NotImplemented di __eq__?
Supaya Python bisa mencoba perbandingan dari sisi object lain, bukan langsung False yang menyesatkan.
Kenapa muncul unhashable type setelah __eq__?
Python menonaktifkan hash default bila kamu tulis __eq__. Untuk katalog di list, buku in items memakai == — aman. set/dict key butuh __hash__ konsisten (topik lanjutan).
Apakah ini menggantikan Composition (#47)?
Tidak. Special methods membuat tiap object lebih sopan; composition tetap menjawab “siapa punya siapa”.
Kesimpulan & langkah berikutnya
__str__ untuk manusia, __repr__ untuk debug, __eq__ untuk kesetaraan nilai. @dataclass memotong boilerplate class data-heavy — tetap kombinasikan dengan composition untuk layanan yang mengelola koleksi.
Artikel ini adalah #48 (ini) — langkah kesembilan Seri 3 setelah Composition vs Inheritance (#47).
Lanjut ke artikel berikutnya — Capstone: Sistem Perpustakaan Mini: gabungkan class, encapsulation, inheritance, polymorphism, ABC, composition, dan special methods dalam satu CLI kecil. (Belum di-hyperlink sampai artikel itu live.)
Seri 3 progress: 9/10 artikel live. Kamu di langkah #48 (ini). Prasyarat: Composition (#47) · Attribute (#42) · Encapsulation (#43) · Abstraction (#46) · Polymorphism (#45) · Inheritance (#44) · Class & Object (#41) · Mengenal OOP (#40).