27. OOP sederhana dengan class — Solusi pekerjaan rumah

File solusi .py ada di exercises/27/homework/solutions/.

Soal 1 — Class Point dengan move

Soal. Class Point dengan __init__, distance, dan move.

Cara berpikir. Mulai dari class Point di bab ini. Tambahkan satu method, move(dx, dy), yang mengubah self.x dan self.y.

Solusi.

import math

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def distance(self, other):
        dx = self.x - other.x
        dy = self.y - other.y
        return math.sqrt(dx * dx + dy * dy)

    def move(self, dx, dy):
        self.x = self.x + dx
        self.y = self.y + dy

a = Point(0, 0)
b = Point(3, 4)
print(a.distance(b))   # 5.0

a.move(1, 1)
print(a.x, a.y)        # 1 1
print(a.distance(b))   # sqrt(2^2 + 3^2) ~ 3.6055...

Kesalahan umum.

  • Lupa self pada move dan distance. Python akan komplain bahwa method menerima terlalu banyak argumen.
  • Menulis x = x alih-alih self.x = x di __init__. Field tidak akan tersimpan di instance sama sekali.

Soal 2 — Class Character

Solusi.

class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp
        self.max_hp = hp

    def take_damage(self, amount):
        self.hp = self.hp - amount
        if self.hp < 0:
            self.hp = 0

    def heal(self, amount):
        self.hp = self.hp + amount
        if self.hp > self.max_hp:
            self.hp = self.max_hp

    def is_alive(self):
        return self.hp > 0

    def report(self):
        print(f"{self.name}: {self.hp} / {self.max_hp} HP (alive: {self.is_alive()})")

c = Character("Keiko", 100)
c.report()
c.take_damage(30)
c.report()
c.take_damage(80)
c.report()
c.heal(20)
c.report()

Contoh output:

Keiko: 100 / 100 HP (alive: True)
Keiko: 70 / 100 HP (alive: True)
Keiko: 0 / 100 HP (alive: False)
Keiko: 20 / 100 HP (alive: True)

report adalah tempat yang tepat untuk f-string — tiga nilai dalam satu baris template tetap.

Kesalahan umum.

  • Membiarkan hp turun negatif atau naik di atas max_hp. Dua pengecekan if di take_damage dan heal membatasi nilainya.

Soal 3 — Class Rectangle

Solusi.

class Rectangle:
    def __init__(self, w, h):
        self.w = w
        self.h = h

    def area(self):
        return self.w * self.h

    def perimeter(self):
        return 2 * (self.w + self.h)

r1 = Rectangle(3, 4)
r2 = Rectangle(10, 2)

print(r1.area())        # 12
print(r1.perimeter())   # 14
print(r2.area())        # 20
print(r2.perimeter())   # 24

Kesalahan umum.

  • Menyimpan width dan height sebagai local variable di level method alih-alih self.w dan self.h. Object ada agar field-nya tersimpan bersama instance di antara pemanggilan method.

Tantangan — Animal, Dog, Cat

Solusi.

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

    def describe(self):
        print(f"I am {self.name}.")

# Dog inherits Animal
class Dog(Animal):
    def __init__(self, name):
        super().__init__(name)

    def bark(self):
        print(f"{self.name}: Woof!")

# Cat inherits Animal
class Cat(Animal):
    def __init__(self, name):
        super().__init__(name)

    def meow(self):
        print(f"{self.name}: Meow.")

rex = Dog("Rex")
whiskers = Cat("Whiskers")

rex.describe()       # I am Rex.
whiskers.describe()  # I am Whiskers.

rex.bark()           # Rex: Woof!
whiskers.meow()      # Whiskers: Meow.

Dog dan Cat keduanya memanggil super().__init__(name) agar constructor Animal berjalan dan mengatur self.name. Setiap anak hanya menambahkan apa yang unik bagi dirinya.

Kesalahan umum.

  • Menghilangkan super().__init__(name) di __init__ anak. Maka self.name tidak pernah diatur, dan describe() memunculkan AttributeError.
  • Mendefinisikan __init__ di anak tapi lupa memanggil super(). Constructor anak berjalan, tapi pengaturan field milik parent tidak.

Selesai?

Object berbasis class (Point, Character, Rectangle) dan rantai inheritance kecil (Animal -> Dog/Cat) kini ada di kotak peralatanmu. Tiga bab berikutnya membangun di atas ini: Banyak object bersama mengelola koleksi instance, Inheritance mendalam mengkhususkan satu class dari class lain, dan Merancang class kecil membuat class nyaman digunakan.