Sepanjang Section ini kita menulis kode prosedural: variabel, loop, fungsi, modul, hingga penyelamatan program lewat try-except pada chapter Penanganan Error . Begitu membuka dokumentasi library data science, bentuknya tiba-tiba berubah menjadi model.fit(X, y), df.head(), atau ax.plot(hari, penjualan) — kode yang penuh tanda titik. Titik-titik itu bukan gaya penulisan; itu adalah tanda bahwa kita sedang bekerja dengan object. Pada chapter ini kita pelajari konsep di baliknya — pemrograman berorientasi object (object-oriented programming, OOP) — dengan sasaran yang terbatas dan praktis: cukup untuk membaca kode library seperti scikit-learn dan pandas, bukan untuk menjadi arsitek OOP. Pemahaman ini adalah pintu terakhir sebelum kita memasuki tiga library inti data science.

Class: Cetakan yang Menyimpan Data dan Perilaku #

Kita mulai dari dua kata kuncinya. Class adalah cetakan: definisi yang menyatakan data apa saja yang dimiliki setiap wujud dan perilaku apa saja yang bisa dilakukan wujud tersebut. Object (disebut juga instance) adalah satu wujud hasil pencetakan. Satu class bisa dicetak berkali-kali, dan setiap object membawa salinan datanya sendiri. Proses mencetak object dari sebuah class disebut instansiasi.

Empat istilah berikut akan muncul terus-menerus di dokumentasi library, jadi kita petakan dulu ke bentuk kodenya:

IstilahWujud pada kodePeran
Classclass TarifTaksi:Cetakan: definisi atribut dan method
Object/instancetaksi_bandung = TarifTaksi(8000, 2500)Satu wujud hasil cetakan
Atributtaksi_bandung.tarif_per_kmData yang menempel pada satu object
Methodtaksi_bandung.hitung_tarif(5)Fungsi yang menempel pada satu object

Dua nama khusus menyertainya: __init__ adalah method yang otomatis berjalan ketika object dibuat, dan self adalah nama baku untuk parameter pertama setiap method — ia menunjuk object yang sedang memanggil method tersebut. Keduanya akan terlihat wujudnya pada contoh pertama.

Class Pertama: TarifTaksi #

Kasusnya sederhana: tarif taksi online terdiri dari tarif buka pintu dan tarif per kilometer, dan kedua angka tersebut berbeda antar kota. Alih-alih memakai dua variabel lepas dan satu fungsi yang menerima banyak parameter, kita bungkus setelan tarif menjadi satu class TarifTaksi yang punya method penghitung:

python
class TarifTaksi:
    """Tarif taksi online: tarif buka pintu + tarif per kilometer."""

    def __init__(self, tarif_buka, tarif_per_km):
        self.tarif_buka = tarif_buka
        self.tarif_per_km = tarif_per_km

    def hitung_tarif(self, jarak_km):
        return self.tarif_buka + self.tarif_per_km * jarak_km


taksi_bandung = TarifTaksi(8000, 2500)
taksi_surabaya = TarifTaksi(10000, 3000)

print("Bandung 5 km  :", taksi_bandung.hitung_tarif(5))
print("Surabaya 5 km :", taksi_surabaya.hitung_tarif(5))
print("Bandung 8 km  :", taksi_bandung.hitung_tarif(8))

Output yang Dihasilkan:

text
Bandung 5 km  : 20500
Surabaya 5 km : 25000
Bandung 8 km  : 28000

Mari kita telaah baris-baris kode di atas:

  1. Baris 1 (class TarifTaksi:): mendefinisikan cetakan baru. Seluruh definisi atribut dan method hidup di blok indentasi di bawahnya, sama seperti isi sebuah fungsi.
  2. Baris 4-6 (__init__): method ini berjalan otomatis pada saat instansiasi. Parameter tarif_buka dan tarif_per_km berasal dari pemanggilan TarifTaksi(8000, 2500); nilai keduanya lalu disimpan sebagai atribut lewat self.tarif_buka dan self.tarif_per_km.
  3. Baris 8-9 (hitung_tarif): method penghitung membaca tarif dari self — data yang sudah menempel pada object — dan hanya menerima jarak_km dari pemanggil. Karena itulah pemanggilan taksi_bandung.hitung_tarif(5) cukup satu argumen: tarif tidak perlu dikirim ulang.
  4. Baris 12-13: dua instansiasi menghasilkan dua object dengan setelan tarif berbeda — 20500 berasal dari 8000 + 2500*5, sedangkan 25000 dari 10000 + 3000*5.

Inilah perbedaan mendasar dengan fungsi murni: fungsi menerima semua data lewat parameter setiap kali dipanggil, sedangkan object menyimpan datanya sendiri dan method tinggal membacanya dari self.

Object yang Mandiri: Pelanggan dan isinstance #

Setiap object memiliki salinan atributnya sendiri, dan perubahan pada satu object tidak menyentuh object lain dari class yang sama. Kita buktikan pada class Pelanggan Toko Kopi Kita, sekaligus memperkenalkan isinstance — fungsi untuk memeriksa asal-usul sebuah object:

python
class Pelanggan:
    """Pelanggan Toko Kopi Kita dengan poin loyalitas."""

    def __init__(self, nama, poin):
        self.nama = nama
        self.poin = poin

    def hitung_diskon(self):
        """Diskon 10 persen bila poin minimal 50."""
        if self.poin >= 50:
            return 10
        return 0


budi = Pelanggan("Budi", 65)
siti = Pelanggan("Siti", 20)

print(f"Diskon {budi.nama}: {budi.hitung_diskon()}%")
print(f"Diskon {siti.nama}: {siti.hitung_diskon()}%")

budi.poin = 40   # poin Budi ditukar; hanya object budi yang berubah
print(f"Setelah tukar poin, diskon Budi : {budi.hitung_diskon()}%")
print(f"Diskon Siti tidak terpengaruh  : {siti.hitung_diskon()}%")

print("budi adalah Pelanggan :", isinstance(budi, Pelanggan))
print("budi adalah angka int :", isinstance(budi, int))

Output yang Dihasilkan:

text
Diskon Budi: 10%
Diskon Siti: 0%
Setelah tukar poin, diskon Budi : 0%
Diskon Siti tidak terpengaruh  : 0%
budi adalah Pelanggan : True
budi adalah angka int : False

Ada tiga perilaku yang per dicatat. Pertama, budi.hitung_diskon() mengembalikan 10 karena 65 poin melampaui batas 50, sementara Siti dengan 20 poin belum berhak diskon. Kedua, penugasan budi.poin = 40 mengubah atribut milik budi saja — diskon Siti tetap 0 dan poin Siti tidak tersentuh; tiap instans benar-benar terpisah. Ketiga, isinstance(budi, Pelanggan) mengembalikan True sedangkan isinstance(budi, int) mengembalikan False; fungsi ini berguna ketika sebuah library mengembalikan object dan kita perlu memastikan jenisnya sebelum diproses.

Pola .fit() dan .predict() pada API Library #

Sekarang kita tahu mengapa tanda titik itu penting. Seluruh API scikit-learn dibangun dari object yang punya siklus dua tahap: latih dengan model.fit(X, y), lalu pakai dengan model.predict(X). Alih-alih langsung memakai scikit-learn, kita tiru pola tersebut pada class buatan sendiri — SederhanaRataRata, model yang memprediksi penjualan selalu dengan rata-rata hasil pelatihan:

python
class SederhanaRataRata:
    """Model yang selalu memprediksi rata-rata target data latih."""

    def fit(self, X, y):
        """Pelajari pola dari data: simpan rata-rata y pada object."""
        self.rata_ = sum(y) / len(y)
        return self

    def predict(self, X):
        """Prediksi setiap baris X dengan rata-rata tersebut."""
        return [self.rata_] * len(X)


# X: jumlah pengunjung kafe per hari; y: gelas kopi susu terjual
X = [120, 150, 90, 200, 175]
y = [70, 85, 55, 110, 90]

model = SederhanaRataRata()
model.fit(X, y)                 # 1. latih: object menyimpan hasil belajarnya
prediksi = model.predict(X)     # 2. pakai: object menghasilkan angka

print("Rata-rata dipelajari :", model.rata_)
print("Prediksi             :", prediksi)
print("Nilai sebenarnya     :", y)

Output yang Dihasilkan:

text
Rata-rata dipelajari : 82.0
Prediksi             : [82.0, 82.0, 82.0, 82.0, 82.0]
Nilai sebenarnya     : [70, 85, 55, 110, 90]

Mari kita baca ulang contoh tersebut dengan kacamata tiga baris terakhirnya:

  1. model = SederhanaRataRata(): instansiasi — sama seperti TarifTaksi(8000, 2500), hanya saja class ini tidak menuntut lewat __init__.
  2. model.fit(X, y): method fit menghitung rata-rata y (410 dibagi 5 = 82.0) lalu menyimpannya pada object sebagai atribut self.rata_, bukan mengembalikannya sebagai nilai balik. Perhatikan pula parameter X: karena model rata-rata tidak membaca fitur, X hanya dipakai untuk menghitung len(X) di predict.
  3. model.predict(X): method predict membaca self.rata_ dan mengembalikan satu prediksi per baris X — lima baris, lima angka 82.0.

Mulai bab machine learning nanti kita akan menulis model = LinearRegression(), model.fit(X, y), model.predict(X_baru) — tiga baris yang bentuknya persis contoh di atas; satu-satunya perbedaan adalah fit() milik LinearRegression mempelajari rumus yang jauh lebih pintar dari sekadar rata-rata.

Sebagai penutup konsep: di balik layar, library-library tersebut juga memakai inheritance (pewarisan) — mekanisme class baru menerima atribut dan method dari class lain. Untuk membaca dan memakai API-nya, kita tidak perlu membedah mekanisme itu; cukup tahu istilahnya agar dokumentasi tidak asing.

Rangkuman #

  • Class adalah cetakan; object adalah wujud hasil cetakan; pencetakan disebut instansiasi dan dilakukan dengan memanggil nama class seperti fungsi.
  • Atribut menampung data pada satu object (objek.nama); method adalah perilakunya (objek.nama()); __init__ mengisi nilai awal dan self menunjuk object pemanggil.
  • Tiap instans berdiri sendiri — mengubah atribut satu object tidak memengaruhi object lain; isinstance(objek, Class) memeriksa asal-usul object.
  • API library data science berbentuk object: model.fit()/model.predict() di scikit-learn, df.head() di pandas, ax.plot() di matplotlib — semuanya method yang dipanggil lewat titik.
  • Atribut hasil pelatihan scikit-learn memakai akhiran garis bawah, misalnya coef_.

Konsep titik di saku kita sekarang. Library pertama yang kita bedah, NumPy, justru memperlihatkan konsepnya secara halus: array NumPy adalah object yang membawa data numerik beserta puluhan method agregasi seperti arr.sum() dan arr.mean(). Kita masuk ke sana pada chapter berikutnya, Dasar NumPy .

Referensi #

  1. scikit-learn — LinearRegression — halaman API resmi; perhatikan method fit(X, y) dan predict(X) beserta atribut coef_ yang mengikuti konvensi garis bawah.