Menu
CoddyTech

Decorator Property Tingkat Lanjut

Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan Python di Coddy. Pelajaran 31 dari 64.

Decorator property tingkat lanjut memberikan kontrol yang lebih canggih atas akses atribut, termasuk property terhitung, penghapus, dan pengelolaan property secara menyeluruh.

Berikut contoh property terhitung yang menghasilkan nilai dari atribut lain:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    @property
    def area(self):
        return self.width * self.height
    
    @property
    def perimeter(self):
        return 2 * (self.width + self.height)

Gunakan property hasil perhitungan seperti atribut biasa:

rect = Rectangle(5, 3)
print(rect.area)      # 15, dihitung secara otomatis
print(rect.perimeter) # 16, dihitung secara otomatis

Buat property dengan getter, setter, dan deleter:

class Temperature:
    def __init__(self):
        self._temp = 0
    
    @property
    def temperature(self):
        return self._temp
    
    @temperature.setter
    def temperature(self, value):
        if value < -273.15:
            raise ValueError("Temperature below absolute zero!")
        self._temp = value
    
    @temperature.deleter
    def temperature(self):
        print("Resetting temperature to 0")
        self._temp = 0

Gunakan seluruh fungsionalitas property:

temp = Temperature()

Gunakan setter dengan validasi:

temp.temperature = 25
print(temp.temperature)  # 25

# temp.temperature = -300  # Akan memunculkan ValueError

Gunakan deleter:

del temp.temperature
print(temp.temperature)  # 0

Buat contoh yang lebih kompleks dengan skor permainan:

class Player:
    def __init__(self, name):
        self.name = name
        self._score = 0
        self._level = 1
    
    @property
    def score(self):
        return self._score
    
    @score.setter
    def score(self, value):
        if value >= 0:
            self._score = value
            self._level = (value // 1000) + 1
        else:
            raise ValueError("Score cannot be negative")
    
    @score.deleter
    def score(self):
        print(f"Resetting {self.name}'s progress")
        self._score = 0
        self._level = 1
    
    @property
    def level(self):
        return self._level

player = Player("Alice")
player.score = 2500
print(f"Score: {player.score}, Level: {player.level}")  # Score: 2500, Level: 3

del player.score
print(f"Score: {player.score}, Level: {player.level}")  # Score: 0, Level: 1

Output:

15
16
25
Resetting temperature to 0
0
Score: 2500, Level: 3
Resetting Alice's progress
Score: 0, Level: 1

Poin Penting: Dekorator property tingkat lanjut memungkinkan property terhitung (dihitung dari data lain), penghapusan property dengan @property.deleter, serta kontrol penuh atas pengambilan, pengaturan, dan penghapusan atribut. Ini menciptakan antarmuka yang intuitif sekaligus mempertahankan validasi data dan enkapsulasi yang kuat.

challenge icon

Tantangan

Sedang

Dalam tantangan ini, kamu akan mengimplementasikan class Rectangle dengan enkapsulasi dan validasi property yang tepat.

  • rectangle.py - Ini adalah file yang perlu kamu edit, berisi komentar TODO untuk memandu implementasimu
  • driver.py - Berisi berbagai skenario pengujian (jangan diubah)
  1. Simpan nilai dalam atribut terlindungi (internal) _width dan _height
  2. Buat property untuk width dan height dengan validasi (harus positif)
    • Tampilkan pesan ValueError yang sesuai seperti yang ditentukan dalam TODO
    • Penting: Periksa apakah nilai kurang dari atau sama dengan 0, lalu tampilkan error (ini memastikan perilaku yang konsisten)
  3. Implementasikan property hanya-baca untuk area dan perimeter
  4. Buat property dimensions dengan fungsi getter, setter, dan deleter seperti yang dijelaskan dalam TODO
    • Penting: Di setter, gunakan sintaks tuple unpacking: width, height = dimensions

Coba sendiri

from rectangle import Rectangle

# Penangan kasus uji
test_case = input()

# Uji fungsi dasar
if test_case == "default_test":
    rect = Rectangle(5, 3)
    print(f"Width: {rect.width}, Height: {rect.height}")
    print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")
    
    # Uji property dimensions
    print(f"Dimensions: {rect.dimensions}")
    rect.dimensions = (10, 8)
    print(f"New area: {rect.area}")
    
    # Uji validasi
    try:
        rect.width = -2
    except ValueError as e:
        print(f"Validation error: {e}")
    
    # Uji deleter
    del rect.dimensions
    print(f"After reset: {rect.dimensions}")

# Uji dengan nilai nol
elif test_case == "zero_values":
    try:
        rect = Rectangle(0, 5)
    except ValueError as e:
        print(f"Error creating rectangle: {e}")
    
    try:
        rect = Rectangle(5, 0)
    except ValueError as e:
        print(f"Error creating rectangle: {e}")

# Uji dengan nilai negatif
elif test_case == "negative_values":
    rect = Rectangle(5, 3)
    original_dimensions = rect.dimensions
    
    try:
        rect.dimensions = (5, -3)
    except ValueError as e:
        print(f"Error setting dimensions: {e}")
    
    print(f"Dimensions after failed update: {rect.dimensions}")
    print(f"Original dimensions preserved: {rect.dimensions == original_dimensions}")

# Uji dengan nilai besar
elif test_case == "large_values":
    rect = Rectangle(1000000, 2000000)
    print(f"Large rectangle area: {rect.area}")
    print(f"Large rectangle perimeter: {rect.perimeter}")

# Uji dengan nilai pecahan
elif test_case == "float_values":
    rect = Rectangle(3.5, 2.75)
    print(f"Dimensions: {rect.dimensions}")
    print(f"Area: {rect.area}")
    print(f"Perimeter: {rect.perimeter}")
    
    rect.dimensions = (1.1, 2.2)
    print(f"New area with float dimensions: {rect.area}")

# Uji dengan error tipe data
elif test_case == "type_errors":
    try:
        rect = Rectangle("5", 3)
    except Exception as e:
        print(f"Type error during creation: {type(e).__name__}: {e}")
    
    rect = Rectangle(5, 3)
    try:
        rect.dimensions = 10  # Bukan tuple
    except Exception as e:
        print(f"Type error setting dimensions: {type(e).__name__}: {e}")

# Uji beberapa operasi property
elif test_case == "property_operations":
    rect = Rectangle(5, 10)
    print(f"Initial - Width: {rect.width}, Height: {rect.height}, Area: {rect.area}")
    
    rect.width = 8
    print(f"After width change - Width: {rect.width}, Area: {rect.area}")
    
    rect.height = 6
    print(f"After height change - Height: {rect.height}, Area: {rect.area}")
    
    rect.dimensions = (12, 9)
    print(f"After dimensions change - Dimensions: {rect.dimensions}, Area: {rect.area}")
    
    del rect.dimensions
    print(f"After reset - Dimensions: {rect.dimensions}, Area: {rect.area}")

# Uji kasus tepi validasi property
elif test_case == "validation_edge_cases":
    rect = Rectangle(5, 3)
    
    try:
        rect.width = 0
    except ValueError as e:
        print(f"Zero width error: {e}")
    
    # Nilai positif yang sangat kecil harus diterima
    rect.height = 0.0001
    print(f"Small height accepted: {rect.height}")
    print(f"Area with small height: {rect.area}")

# Uji beberapa persegi panjang
elif test_case == "multiple_rectangles":
    rect1 = Rectangle(5, 3)
    rect2 = Rectangle(10, 2)
    rect3 = Rectangle(4, 4)
    
    print(f"Rectangle 1 - Area: {rect1.area}, Perimeter: {rect1.perimeter}")
    print(f"Rectangle 2 - Area: {rect2.area}, Perimeter: {rect2.perimeter}")
    print(f"Rectangle 3 - Area: {rect3.area}, Perimeter: {rect3.perimeter}")
    
    # Ubah setiap persegi panjang
    rect1.width = 7
    rect2.height = 5
    rect3.dimensions = (6, 6)
    
    print(f"After modifications:")
    print(f"Rectangle 1 - Dimensions: {rect1.dimensions}")
    print(f"Rectangle 2 - Dimensions: {rect2.dimensions}")
    print(f"Rectangle 3 - Dimensions: {rect3.dimensions}")

# Uji performa dengan banyak operasi
elif test_case == "performance_test":
    rect = Rectangle(5, 5)
    
    # Lakukan banyak operasi property
    for i in range(1000):
        rect.width = i % 10 + 1  # Nilai 1-10
        rect.height = i % 5 + 1  # Nilai 1-5
        area = rect.area  # Akses property yang dihitung
        perimeter = rect.perimeter  # Akses property yang dihitung
        dims = rect.dimensions  # Akses getter property
    
    print(f"Final state after 1000 operations:")
    print(f"Width: {rect.width}, Height: {rect.height}")
    print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Objek

Latihan sendiri: Compiler Python online