# 219. Abstract Properties in Python

**🛠 10 Python Snippets for Abstract Properties**

**🔹 1. Basic Abstract Property**

```python
from abc import ABC, abstractmethod

class Animal(ABC):
    @property
    @abstractmethod
    def sound(self):
        """Subclasses must implement this property"""
        pass

class Dog(Animal):
    @property
    def sound(self):
        return "Woof!"

d = Dog()
print(d.sound)  # ✅ Output: Woof!
```

✅ **Fix:** Using `@property` with `@abstractmethod` forces subclasses to define the property.

***

**🔹 2. Abstract Property with Setter**

```python
from abc import ABC, abstractmethod

class Vehicle(ABC):
    @property
    @abstractmethod
    def speed(self):
        pass
    
    @speed.setter
    @abstractmethod
    def speed(self, value):
        pass

class Car(Vehicle):
    def __init__(self):
        self._speed = 0

    @property
    def speed(self):
        return self._speed

    @speed.setter
    def speed(self, value):
        if value < 0:
            raise ValueError("Speed cannot be negative")
        self._speed = value

c = Car()
c.speed = 60
print(c.speed)  # ✅ Output: 60
```

✅ **Fix:** A subclass **must implement** both **getter and setter**.

***

**🔹 3. Abstract Property with a Default Implementation**

```python
from abc import ABC, abstractmethod

class Appliance(ABC):
    @property
    @abstractmethod
    def power(self):
        return "Unknown"

class Fan(Appliance):
    @property
    def power(self):
        return "50W"

f = Fan()
print(f.power)  # ✅ Output: 50W
```

✅ **Fix:** Abstract properties can have **default values**, overridden in subclasses.

***

**🔹 4. Abstract Read-Only Property**

```python
from abc import ABC, abstractmethod

class Shape(ABC):
    @property
    @abstractmethod
    def area(self):
        """Subclasses must define this property"""
        pass

class Square(Shape):
    def __init__(self, side):
        self._side = side

    @property
    def area(self):
        return self._side ** 2

s = Square(4)
print(s.area)  # ✅ Output: 16
```

✅ **Fix:** A **getter-only property** ensures the attribute is **read-only**.

***

**🔹 5. Enforcing Multiple Abstract Properties**

```python
from abc import ABC, abstractmethod

class Device(ABC):
    @property
    @abstractmethod
    def brand(self):
        pass

    @property
    @abstractmethod
    def model(self):
        pass

class Phone(Device):
    def __init__(self, brand, model):
        self._brand = brand
        self._model = model

    @property
    def brand(self):
        return self._brand

    @property
    def model(self):
        return self._model

p = Phone("Apple", "iPhone 15")
print(p.brand, p.model)  # ✅ Output: Apple iPhone 15
```

✅ **Fix:** Enforce **multiple abstract properties** in a single class.

***

**🔹 6. Using `super()` in Abstract Properties**

```python
from abc import ABC, abstractmethod

class Laptop(ABC):
    @property
    @abstractmethod
    def os(self):
        return "Unknown OS"

class MacBook(Laptop):
    @property
    def os(self):
        return super().os + " (Modified) - macOS"

m = MacBook()
print(m.os)  # ✅ Output: Unknown OS (Modified) - macOS
```

✅ **Fix:** **Use `super()` to extend an abstract property’s behavior**.

***

**🔹 7. Abstract Property with Class Variables**

```python
from abc import ABC, abstractmethod

class Planet(ABC):
    @property
    @abstractmethod
    def gravity(self):
        pass

class Earth(Planet):
    GRAVITY = 9.8  # Class variable

    @property
    def gravity(self):
        return self.GRAVITY

e = Earth()
print(e.gravity)  # ✅ Output: 9.8
```

✅ **Fix:** Abstract properties can return **class variables**.

***

**🔹 8. Abstract Property with Validation**

```python
from abc import ABC, abstractmethod

class Employee(ABC):
    @property
    @abstractmethod
    def salary(self):
        pass

    @salary.setter
    @abstractmethod
    def salary(self, value):
        pass

class Developer(Employee):
    def __init__(self, salary):
        self._salary = salary

    @property
    def salary(self):
        return self._salary

    @salary.setter
    def salary(self, value):
        if value < 30000:
            raise ValueError("Salary must be at least 30,000")
        self._salary = value

d = Developer(50000)
print(d.salary)  # ✅ Output: 50000
```

✅ **Fix:** Use **validation logic** in the setter.

***

**🔹 9. Abstract Properties with Multiple Inheritance**

```python
from abc import ABC, abstractmethod

class Mammal(ABC):
    @property
    @abstractmethod
    def has_fur(self):
        pass

class Carnivore(ABC):
    @property
    @abstractmethod
    def diet(self):
        pass

class Tiger(Mammal, Carnivore):
    @property
    def has_fur(self):
        return True

    @property
    def diet(self):
        return "Meat"

t = Tiger()
print(t.has_fur, t.diet)  # ✅ Output: True Meat
```

✅ **Fix:** Abstract properties work with **multiple inheritance**.

***

**🔹 10. Abstract Property with Caching (`functools.lru_cache`)**

```python
from abc import ABC, abstractmethod
from functools import lru_cache

class ExpensiveCalculation(ABC):
    @property
    @abstractmethod
    def compute(self):
        pass

class DataProcessor(ExpensiveCalculation):
    @property
    @lru_cache(maxsize=1)  # Caches the result for performance
    def compute(self):
        print("Running expensive computation...")
        return 42  # Simulating expensive operation

dp = DataProcessor()
print(dp.compute)  # ✅ Output: Running expensive computation... 42
print(dp.compute)  # ✅ Output: 42 (cached result, no print)
```

✅ **Fix:** Combine `@abstractmethod` with **`@lru_cache` for performance**.

***

#### **🚀 Summary: Abstract Properties in Python**

| **Feature**                             | **Use Case**                                |
| --------------------------------------- | ------------------------------------------- |
| `@property @abstractmethod`             | Enforce read-only abstract properties.      |
| `@property @abstractmethod` + `@setter` | Enforce read/write properties.              |
| Default abstract property values        | Provide a default implementation.           |
| `super()` in abstract properties        | Extend base class logic.                    |
| Multiple abstract properties            | Ensure multiple attributes are implemented. |
| `lru_cache` with abstract property      | Optimize expensive computations.            |
