2Python untuk AI

Object-Oriented Programming (OOP)

8 jam16 min baca
Tujuan

Paham class, inheritance, dan dunder methods. Wajib karena PyTorch, scikit-learn, HuggingFace semuanya OOP.

06 โ€” Object-Oriented Programming (OOP)

Estimasi: 8 jam Tujuan: Paham class, inheritance, dan dunder methods. Wajib karena PyTorch, scikit-learn, HuggingFace semuanya OOP.


Kenapa Materi Ini Penting?

Kalau kamu buka dokumentasi PyTorch, scikit-learn, atau HuggingFace, hampir semua API-nya class-based. model = LinearRegression(), tokenizer = AutoTokenizer.from_pretrained(...), class MyModel(nn.Module). Tanpa pemahaman OOP yang solid, kamu akan kesulitan baca dokumentasi, debug error, atau extend behavior model.

OOP juga adalah cara berpikir tentang enkapsulasi โ€” menyembunyikan kompleksitas di balik interface yang clean. Saat kamu bikin model ML production, kamu mau user-nya cukup panggil .predict(), tidak peduli bagaimana model tersebut load weights, preprocess input, dan post-process output. OOP yang membuat itu mungkin.

Analogi besar:

  • Class = blueprint rumah (gambar arsitektur).
  • Object/Instance = rumah jadi yang kamu tinggal di dalamnya.
  • Satu blueprint bisa bikin banyak rumah, masing-masing punya interior beda (attribute), tapi struktur dasar sama (method).

Peta Konsep

flowchart TD
    A[๐Ÿ›๏ธ OOP] --> B[๐Ÿ“ Class & Object]
    A --> C[๐Ÿ“Š Attributes]
    A --> D[โš™๏ธ Methods]
    A --> E[๐ŸŒณ Inheritance]
    A --> F[๐Ÿ”’ Encapsulation]
    A --> G[โœจ Dunder Methods]
    A --> H[๐Ÿ“ฆ Dataclass]

    B --> B1[Blueprint vs Instance]
    C --> C1[Instance vs Class attr]
    D --> D1[Instance method]
    D --> D2[classmethod]
    D --> D3[staticmethod]
    D --> D4[@property]
    E --> E1[Single]
    E --> E2[Multiple]
    E --> E3[super]
    G --> G1[__init__, __str__, __repr__]
    G --> G2[__eq__, __lt__]
    G --> G3[__add__, __mul__]

Bagian 1 โ€” Kenapa OOP?

Tanpa OOP

# Data user disimpan di dict
user1 = {"nama": "Budi", "umur": 25, "saldo": 1000000}
user2 = {"nama": "Ani", "umur": 30, "saldo": 500000}

# Function untuk operasi
def tambah_saldo(user, jumlah):
    user["saldo"] += jumlah

def info_user(user):
    print(f"{user['nama']}: Rp{user['saldo']:,}")

tambah_saldo(user1, 50000)
info_user(user1)

Masalah:

  • Data dan function terpisah
  • Mudah salah pakai (misal pass dict yang bukan user)
  • Tidak ada validasi
  • Sulit di-extend

Dengan OOP

class User:
    def __init__(self, nama, umur, saldo=0):
        self.nama = nama
        self.umur = umur
        self.saldo = saldo
    
    def tambah_saldo(self, jumlah):
        if jumlah < 0:
            raise ValueError("Jumlah harus positif")
        self.saldo += jumlah
    
    def info(self):
        print(f"{self.nama}: Rp{self.saldo:,}")

# Pakai
user1 = User("Budi", 25, 1000000)
user1.tambah_saldo(50000)
user1.info()

Keuntungan:

  • Data + behavior dalam satu unit (encapsulation)
  • Tidak bisa salah pass
  • Validasi terpusat
  • Mudah extend (inheritance)

Bagian 2 โ€” Class & Object

Diagram: Class = Blueprint, Object = Instance

Cara Membaca Diagram

Top: class Mobil sebagai blueprint. Layer 2: komponen class (__init__, attributes, methods). Layer 3: 3 instance konkret (mobil1, mobil2, mobil3) โ€” masing-masing punya state berbeda tapi struktur sama. Bottom-right: panggil method .info() yang kerja di tiap instance.

Walkthrough Step-by-Step

  1. Definisi class โ€” class Mobil: dengan __init__, attributes (self.merk, self.warna), dan methods (def info(self):).
  2. Instantiation โ€” mobil1 = Mobil("Toyota", "merah") panggil __init__ dan bikin instance baru.
  3. Tiap instance punya state independen: mobil1.merk = "Toyota", mobil2.merk = "Honda", mobil3.merk = "Suzuki".
  4. Method call โ€” mobil1.info() dipanggil. Python translate ke Mobil.info(mobil1). self = mobil1.
  5. Banyak instance pakai logic yang sama (method body), tapi data beda (attributes per instance).

Analogi Sehari-hari

Class = resep kue, instance = kue jadi. Resep punya bahan (attributes) dan cara membuat (methods). Tiap kue jadi punya rasa beda (data berbeda) โ€” kue coklat, kue keju, kue strawberry. Tapi semuanya pakai resep dasar yang sama. Kalau kamu update resep (ubah class), semua kue baru ikut ter-update โ€” tapi kue yang sudah jadi tetap sama.

Diagram statis Mermaid sebagai fallback:

classDiagram
    class Mobil {
        +str merk
        +str warna
        +info()
    }
    Mobil <|.. mobil1 : instance
    Mobil <|.. mobil2 : instance
    note for mobil1 "merk = 'Toyota'<br/>warna = 'merah'"
    note for mobil2 "merk = 'Honda'<br/>warna = 'biru'"

Analogi: class itu cetakan kue, object itu kue jadi. Cetakan menentukan bentuk (struktur), tapi tiap kue bisa beda rasa/topping (attribute).

Class = template/blueprint Object (instance) = wujud nyata dari class

class Mobil:                   # class definition
    def __init__(self, merk, warna):
        self.merk = merk       # attribute
        self.warna = warna
    
    def info(self):            # method
        print(f"{self.merk} warna {self.warna}")

# Instantiation (bikin object)
mobil1 = Mobil("Toyota", "merah")
mobil2 = Mobil("Honda", "biru")

mobil1.info()    # Toyota warna merah
mobil2.info()    # Honda warna biru

Anatomi

  • class Mobil: โ€” definisi class
  • __init__ โ€” konstruktor (dipanggil saat bikin instance)
  • self โ€” referensi ke instance saat ini
  • self.merk โ€” attribute (data)
  • def info(self): โ€” method (function dalam class)

self Itu Apa?

self = parameter pertama otomatis untuk semua method, merujuk ke instance.

mobil1.info()
# Sebenarnya Python panggil:
# Mobil.info(mobil1)
# self = mobil1

Aturan: parameter pertama method selalu self (konvensi).


Bagian 3 โ€” Attributes

Instance Attribute

Beda antar instance:

class Mobil:
    def __init__(self, merk):
        self.merk = merk    # instance attribute

m1 = Mobil("Toyota")
m2 = Mobil("Honda")
print(m1.merk, m2.merk)    # Toyota Honda

Class Attribute

Sama untuk semua instance:

class Mobil:
    roda = 4    # class attribute (shared)
    
    def __init__(self, merk):
        self.merk = merk

m1 = Mobil("Toyota")
m2 = Mobil("Honda")

print(m1.roda, m2.roda)    # 4 4
print(Mobil.roda)          # 4 (akses lewat class)

# Hati-hati modifikasi
Mobil.roda = 6             # ubah class attribute
print(m1.roda)             # 6 (semua instance ter-update)

Pitfall: kalau class attribute mutable (list, dict), bisa jadi shared state bug. Hindari.

Cek Attribute

hasattr(m1, "merk")       # True
getattr(m1, "merk")       # "Toyota"
getattr(m1, "xyz", None)  # None (default)
setattr(m1, "merk", "Suzuki")
delattr(m1, "merk")

Bagian 4 โ€” Methods

Instance Method (Standar)

class Calculator:
    def __init__(self, value=0):
        self.value = value
    
    def add(self, n):
        self.value += n
        return self      # return self โ†’ method chaining
    
    def multiply(self, n):
        self.value *= n
        return self

c = Calculator(10)
c.add(5).multiply(2)    # method chaining
print(c.value)          # 30

Class Method

Tidak butuh instance, akses ke class.

class User:
    user_count = 0
    
    def __init__(self, nama):
        self.nama = nama
        User.user_count += 1
    
    @classmethod
    def total_users(cls):
        return cls.user_count

User("a")
User("b")
print(User.total_users())    # 2

Static Method

Tidak butuh instance maupun class. Cuma function di dalam class untuk grouping logical.

class MathUtils:
    @staticmethod
    def is_even(n):
        return n % 2 == 0

print(MathUtils.is_even(10))    # True

Property (Getter/Setter Pythonic)

class Lingkaran:
    def __init__(self, radius):
        self._radius = radius   # convention: _ untuk "private"
    
    @property
    def radius(self):
        return self._radius
    
    @radius.setter
    def radius(self, value):
        if value < 0:
            raise ValueError("Radius tidak boleh negatif")
        self._radius = value
    
    @property
    def luas(self):
        return 3.14159 * self._radius ** 2

c = Lingkaran(5)
print(c.radius)      # 5
print(c.luas)        # 78.539... (akses kayak attribute, tapi computed)
c.radius = 10        # pakai setter
c.radius = -1        # โŒ ValueError

Penting: @property membuat method bertingkah seperti attribute. Pythonic untuk computed values.


Bagian 5 โ€” Inheritance

Diagram: Class Inheritance

Cara Membaca Diagram

Top: base class Hewan dengan attribute & method generic. Layer tengah: 4 subclass (Anjing, Kucing, Ikan, Burung) yang inherit dari Hewan. Bottom: hasil method bersuara() yang berbeda-beda (override) โ€” bukti polymorphism. Edge dashed = relasi inheritance.

Walkthrough Step-by-Step

  1. Hewan = base class dengan nama, umur, dan bersuara() (default).
  2. Anjing(Hewan) inherit semua dari Hewan, tambah method lari(), override bersuara() jadi "Guk!".
  3. Kucing(Hewan) override bersuara() jadi "Meong!", tambah manjat().
  4. Ikan(Hewan) override bersuara() jadi silent, tambah berenang().
  5. Burung(Hewan) override bersuara() jadi "Cuit!", tambah terbang().
  6. Polymorphism โ€” panggil .bersuara() di list of Hewan, tiap object respon dengan suara sendiri tanpa cek tipe manual.

Analogi Sehari-hari

Hewan = template gen DNA dasar. Anjing, Kucing, Ikan, Burung = spesies turunan yang dapat semua sifat dasar (makan, bernafas, ada nama-umur), tapi tambah/ubah karakteristik sendiri. Anjing override "suara" jadi gonggongan, Kucing jadi meong. Polymorphism = kamu bisa bilang "semua hewan suara", tiap hewan jawab sesuai jenisnya โ€” tidak perlu instruksi spesifik per spesies.

Diagram statis Mermaid sebagai fallback:

classDiagram
    class Hewan {
        +str nama
        +int umur
        +bersuara()
    }
    class Anjing {
        +str ras
        +bersuara() override
    }
    class Kucing {
        +bersuara() override
    }
    class Ikan {
        +berenang()
    }
    Hewan <|-- Anjing
    Hewan <|-- Kucing
    Hewan <|-- Ikan

Analogi: inheritance = mewarisi gen dari orang tua. Anjing dapat semua fitur Hewan (nama, umur, bersuara), tapi bisa tambahkan fitur sendiri (ras) atau ubah behavior (bersuara jadi guk-guk).

Class bisa "mewarisi" dari class lain.

Bentuk Dasar

class Hewan:
    def __init__(self, nama):
        self.nama = nama
    
    def bersuara(self):
        print("Suara generic")

class Anjing(Hewan):     # inherit dari Hewan
    def bersuara(self):  # override
        print(f"{self.nama}: Guk guk!")

class Kucing(Hewan):
    def bersuara(self):
        print(f"{self.nama}: Meong!")

a = Anjing("Rex")
a.bersuara()    # Rex: Guk guk!

Memanggil Parent dengan super()

class Hewan:
    def __init__(self, nama, umur):
        self.nama = nama
        self.umur = umur

class Anjing(Hewan):
    def __init__(self, nama, umur, ras):
        super().__init__(nama, umur)    # panggil parent __init__
        self.ras = ras
    
    def info(self):
        print(f"{self.nama}, {self.umur}, ras {self.ras}")

a = Anjing("Rex", 3, "Husky")
a.info()

Multiple Inheritance (Hati-hati)

class Berenang:
    def aksi(self):
        print("Berenang")

class Terbang:
    def aksi(self):
        print("Terbang")

class Bebek(Berenang, Terbang):
    pass

b = Bebek()
b.aksi()    # Berenang (yang pertama di-list)

Best practice: hindari multiple inheritance kompleks. Lebih baik composition (akan dibahas).

MRO (Method Resolution Order)

Python pakai algoritma C3 linearization:

print(Bebek.mro())
# [Bebek, Berenang, Terbang, object]

object adalah parent dari semua class.


Bagian 6 โ€” Encapsulation (Public/Private)

Python tidak punya private murni seperti Java. Pakai konvensi:

class BankAccount:
    def __init__(self, saldo):
        self.saldo = saldo            # public
        self._pin = "1234"            # protected (konvensi)
        self.__token = "secret-xyz"   # "private" (name mangling)
    
    def get_token(self):
        return self.__token

acc = BankAccount(1000)
print(acc.saldo)            # OK
print(acc._pin)             # bisa, tapi konvensi: jangan
print(acc.__token)          # โŒ AttributeError
print(acc._BankAccount__token)  # bisa (name mangled), tapi jangan

Aturan konvensi Python:

  • nama โ€” public
  • _nama โ€” protected (jangan akses dari luar, tapi tidak dipaksa)
  • __nama โ€” "private" (Python rename ke _ClassName__nama)

Python pakai konsensus, bukan paksaan. "We're all consenting adults here."


Bagian 7 โ€” Dunder Methods (Magic Methods)

Method dengan __nama__ โ€” Python panggil otomatis untuk operasi tertentu.

__init__ โ€” Konstruktor (sudah dibahas)

__str__ & __repr__

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __str__(self):
        return f"({self.x}, {self.y})"          # untuk user
    
    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})" # untuk developer

p = Point(3, 4)
print(p)            # (3, 4)         โ† __str__
print(repr(p))      # Point(x=3, y=4) โ† __repr__
[p, p]              # [Point(x=3, y=4), Point(x=3, y=4)] โ† list pakai __repr__

Best practice: selalu implement __repr__ minimal. Sangat membantu debugging.

__eq__ โ€” Operator ==

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

p1 = Point(3, 4)
p2 = Point(3, 4)
print(p1 == p2)     # True (tanpa __eq__: False, karena beda instance)

__lt__, __gt__, dll โ€” Comparison

class Buku:
    def __init__(self, judul, halaman):
        self.judul = judul
        self.halaman = halaman
    
    def __lt__(self, other):
        return self.halaman < other.halaman

books = [Buku("A", 100), Buku("B", 50), Buku("C", 200)]
books.sort()    # sort berdasarkan halaman

Arithmetic (__add__, __sub__, dll)

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
    
    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)      # Vector(4, 6)
print(v1 * 3)       # Vector(3, 6)

__len__, __getitem__, __iter__

class Deck:
    def __init__(self):
        self.cards = ["Aโ™ฅ", "Kโ™ฅ", "Qโ™ฅ"]
    
    def __len__(self):
        return len(self.cards)
    
    def __getitem__(self, i):
        return self.cards[i]

d = Deck()
print(len(d))       # 3
print(d[0])         # Aโ™ฅ
for card in d:      # iterable! karena ada __getitem__
    print(card)

Daftar Dunder Lengkap

__init__       konstruktor
__del__        destruktor
__str__        str(obj), print(obj)
__repr__       repr(obj)
__eq__         ==
__ne__         !=
__lt__         <
__le__         <=
__gt__         >
__ge__         >=
__add__        +
__sub__        -
__mul__        *
__truediv__    /
__len__        len()
__getitem__    obj[i]
__setitem__    obj[i] = x
__contains__   x in obj
__iter__       for x in obj
__call__       obj()
__enter__      with obj:
__exit__       with obj:

Bagian 8 โ€” Composition vs Inheritance

Diagram: Composition (has-a) vs Inheritance (is-a)

classDiagram
    class Engine {
        +start()
    }
    class Car_C {
        -Engine engine
        +drive()
    }
    Car_C *-- Engine : composition (has-a)

    class Vehicle {
        +move()
    }
    class Car_I {
        +honk()
    }
    Vehicle <|-- Car_I : inheritance (is-a)

Analogi:

  • Inheritance = "Kucing adalah Hewan" (sifat keturunan)
  • Composition = "Mobil punya Mesin" (komponen)

Kalau ragu, tanya: bisa diganti komponen-nya tanpa ganti identitas? Kalau iya โ†’ composition.

Composition

Class punya instance dari class lain (relasi "has-a"):

class Engine:
    def start(self):
        print("Engine starting...")

class Car:
    def __init__(self):
        self.engine = Engine()    # composition
    
    def drive(self):
        self.engine.start()
        print("Driving!")

Inheritance

Class is-a class lain (relasi "is-a"):

class Vehicle:
    pass

class Car(Vehicle):    # inheritance: Car is a Vehicle
    pass

Aturan

  • Composition = "has-a" (Car has Engine)
  • Inheritance = "is-a" (Dog is Animal)

Best practice modern: Favor composition over inheritance. Inheritance fleksibel di awal tapi sering jadi rigid. Composition lebih maintainable.


Bagian 9 โ€” Dataclass (Pythonic Modern)

Untuk class yang hanya wadah data, pakai @dataclass:

from dataclasses import dataclass

@dataclass
class User:
    nama: str
    umur: int
    email: str = "no-email"   # default

# Otomatis dapat:
# - __init__
# - __repr__
# - __eq__

u = User("Budi", 25, "budi@x.com")
print(u)               # User(nama='Budi', umur=25, email='budi@x.com')

Bandingkan dengan class biasa yang ribet boilerplate. Dataclass = produktivitas.

@dataclass(frozen=True)   # immutable
class Point:
    x: float
    y: float

frozen=True bikin instance tidak bisa diubah setelah dibuat โ€” mirip tuple tapi dengan field bernama.


Bagian 10 โ€” OOP di Konteks AI

Untuk konteks bootcamp Dicoding GenAI:

scikit-learn API

from sklearn.linear_model import LinearRegression

model = LinearRegression()    # instantiate
model.fit(X, y)               # method
prediction = model.predict(X) # method
print(model.coef_)            # attribute

Semua model sklearn ikut interface yang sama: .fit(), .predict(), .score(). Itu pola OOP.

PyTorch

import torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer1 = nn.Linear(10, 5)
        self.layer2 = nn.Linear(5, 1)
    
    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        return x

model = MyModel()

PyTorch model selalu subclass nn.Module. Kalau tidak paham OOP, Fase 6 akan struggle.

HuggingFace

from transformers import AutoModel, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
model = AutoModel.from_pretrained("bert-base-uncased")

from_pretrained adalah class method (bukan instance method). Pattern OOP.


Common Mistakes & FAQ

โŒ Mistake 1: Lupa self

class Counter:
    def __init__(self):
        count = 0       # โŒ ini local variable!
    
    def increment(self):
        count += 1      # โŒ NameError

# Fix: pakai self
class Counter:
    def __init__(self):
        self.count = 0
    
    def increment(self):
        self.count += 1

โŒ Mistake 2: Mutable class attribute

class Student:
    grades = []    # โŒ shared antar instance!
    
    def add_grade(self, g):
        self.grades.append(g)

s1 = Student()
s2 = Student()
s1.add_grade(90)
print(s2.grades)    # [90] โ€” bug! semua instance share

# Fix: pakai instance attribute
class Student:
    def __init__(self):
        self.grades = []

โŒ Mistake 3: Lupa super().__init__()

class Animal:
    def __init__(self, name):
        self.name = name

class Dog(Animal):
    def __init__(self, name, breed):
        # โŒ lupa super().__init__(name)
        self.breed = breed

d = Dog("Rex", "Husky")
print(d.name)    # AttributeError!

โŒ Mistake 4: Bingung @classmethod vs @staticmethod

class Math:
    @staticmethod
    def add(a, b):
        return a + b      # tidak akses class/instance
    
    @classmethod
    def from_string(cls, s):
        return cls(...)   # akses class (untuk factory)

Aturan:

  • instance method = butuh self (akses instance state)
  • classmethod = butuh class (factory pattern, alternative constructor)
  • staticmethod = utility murni dalam scope class

โŒ Mistake 5: Akses dunder dari luar

class A:
    def __secret(self):    # double underscore = name mangling
        pass

a = A()
a.__secret()    # โŒ AttributeError

Gunakan single underscore _method untuk "internal" yang masih bisa diakses.

FAQ

Q: Apa beda __init__ dan __new__? A: __new__ bikin instance, __init__ initialize-nya. Hampir tidak pernah override __new__ di kode normal โ€” kecuali bikin singleton atau metaclass.

Q: Apakah Python punya interface seperti Java? A: Tidak persis. Pakai abc.ABC (abstract base class) dengan @abstractmethod untuk paksa subclass implement method tertentu. Atau pakai duck typing biasa.

Q: Kapan pakai @dataclass? A: Kalau class kamu sebagian besar wadah data (attribute + minimal logic). Untuk class dengan banyak behavior, pakai class biasa.

Q: Multiple inheritance aman dipakai? A: Bisa, tapi rumit. Pakai mixin pattern (class kecil dengan method spesifik) lebih maintainable. Hindari diamond inheritance.

Q: Property vs getter/setter biasa? A: Property Pythonic. Akses obj.value (kayak attribute), tapi belakang layar bisa ada validasi/komputasi. Java-style getter/setter (getValue()) tidak idiomatic di Python.

Q: Kenapa PyTorch model selalu nn.Module? A: nn.Module handle weights, gradients, GPU transfer otomatis. Dengan inherit, kamu dapat semua infrastruktur "gratis", tinggal define forward().


Cek Pemahaman

  • Bisa bikin class dengan __init__ dan method?
  • Tahu beda instance vs class attribute?
  • Bisa pakai @property?
  • Paham inheritance dan super()?
  • Bisa implement __str__, __repr__, __eq__?
  • Tahu kapan pakai composition vs inheritance?
  • Bisa pakai @dataclass?

Challenge 2.6

Challenge 1 โ€” Class Buku

Bikin class Buku dengan:

  • Attribute: judul, penulis, halaman, tahun
  • Method info() print info buku
  • Method __str__ dan __repr__
  • Method __eq__ (dua buku sama jika judul + penulis sama)

Challenge 2 โ€” BankAccount

Bikin class BankAccount:

  • Attribute: nomor_rekening, pemilik, saldo (private dengan property)
  • Method setor(jumlah), tarik(jumlah), transfer(target_account, jumlah)
  • Validasi: tidak bisa tarik melebihi saldo, jumlah harus positif
  • History transaksi (list)

Challenge 3 โ€” Inheritance: Hewan

Bikin hierarchy:

  • Hewan (base) โ€” nama, umur, bersuara() (abstract)
  • Anjing(Hewan) โ€” bersuara "Guk!", method lari()
  • Kucing(Hewan) โ€” bersuara "Meong!", method manjat()
  • Ikan(Hewan) โ€” bersuara "..." (silent), method berenang()

Test dengan list of hewan, panggil method yang sesuai polymorphic.

Challenge 4 โ€” Property & Validation

Bikin class Suhu:

  • Internal store celsius
  • Property celsius, fahrenheit, kelvin (computed)
  • Set lewat property mana saja (dia auto-konversi)
  • Validasi: tidak bisa di bawah -273.15ยฐC
s = Suhu(celsius=25)
print(s.fahrenheit)    # 77.0
s.kelvin = 300
print(s.celsius)       # ~26.85

Challenge 5 โ€” Composition: Library System

Bikin sistem perpustakaan:

  • Class Buku (judul, penulis, ISBN)
  • Class Anggota (nama, ID, list buku_dipinjam)
  • Class Perpustakaan (list buku, list anggota)
  • Method: pinjam, kembali, cari_buku, daftar_anggota

Pakai composition (Perpustakaan has buku + anggota).

Challenge 6 โ€” Dunder Methods: Vector

Bikin class Vector (3D):

  • __init__(x, y, z)
  • __add__, __sub__, __mul__ (scalar), __eq__
  • __abs__ (magnitude/length)
  • __str__ dan __repr__
  • Method dot(other) (dot product), cross(other) (cross product)

Challenge 7 โ€” Dataclass

Refactor class User dari Challenge 2 jadi dataclass. Tambahkan:

  • Field email dengan default
  • Validasi di __post_init__

Challenge 8 โ€” Mini ML Model API

Bikin class SimpleClassifier:

  • __init__(threshold=0.5)
  • fit(X, y) โ€” train (untuk simpler, hitung mean of class 1)
  • predict(X) โ€” predict
  • score(X, y) โ€” accuracy
  • Internal state: _is_fitted, raise error kalau predict sebelum fit

Test dengan dummy data. Ini latihan pola sklearn API.


Selanjutnya: 07-file-io-modules.md โ€” file I/O, JSON, CSV, dan organisasi code (modules + packages).