筆記如何使用 Python 進行物件導向開發與設計常見的語法 property
, setter
, __str__
, __init__
, __iter__
。
說明
app.py
from Car import Car
car1 = Car("Red", 100)
car2 = Car("Black", 60)
car1.fuel = 50
print(car1) # 50
car1.refuel(25)
print(car1) # 75
car1.__fuel = 10
print(car1) # 75 😮
for wheel in car1:
wheel.inflate(5)
print(wheel)
透過 self.__attributeName
的方式可以簡單的隱藏 attribute,但其實 python 只是 rename 成 _ClassName__attribute
。
而 python 的 getter 與 setter,pythonic 的方式是透過 @property
來修飾 method 作為 getter,並透過 @propertyName.setter
的方式來修飾 method 來構成 setter。
class Car:
def __init__(self, color, fuel):
self.__fuel = fuel
@property
def fuel(self):
return self.__fuel
@fuel.setter
def fuel(self, gallons):
if int(gallons) > 0:
self.__fuel = gallons
else:
raise ValueError("Invalid fuel gallons")
Class Field 直接在 Class 下定義,Class Method 則透過 @classmethod
來修飾:
class Car:
# Class field (shared by all instances of the class)
cars = []
@classmethod
def get_cars(cls):
# A class method to access the class variable 'cars'
return len(cls.cars)
如果想要透過 del
來消滅物件,可能無法隨心所欲,python 有自己的物件 GC 機制,即使移除了物件的指向 car1
實際的 Car
instance 仍會存在。保險的方式是明確的透過 method 的呼叫去進行移除。
car1 = Car('black', 100)
car2 = Car('white', 90)
del car1
print(Car.get_cars()) # 2 😮
Car.py
from Tire import Tire
class Car:
# Class field (shared by all instances of the class)
wheels = 4
cars = []
def __init__(self, color, fuel):
# Instance fields (unique to each instance)
self.color = color
self.__fuel = fuel # Intended as a private attribute
self.wheels = []
for _ in range(Car.wheels):
self.wheels.append(Tire(brand="Michelin", size="205/55R16", pressure=32))
Car.cars.append(self)
@property
def fuel(self):
# Property: getter method for the __fuel attribute
return self.__fuel
@fuel.setter
def fuel(self, gallons):
# Property: setter method for the __fuel attribute
if int(gallons) > 0:
self.__fuel = gallons
else:
raise ValueError("Invalid fuel gallons")
def refuel(self, gallons):
if int(gallons) > 0:
self.__fuel += gallons
else:
raise ValueError("Invalid fuel gallons")
def __str__(self):
return f'A {self.color} car with {self.__fuel} gallons.'
def __iter__(self):
for wheel in self.wheels:
yield wheel
def remove_car(self):
# Method to explicitly remove a car
if self in Car.cars:
Car.cars.remove(self)
@classmethod
def get_cars(cls):
# A class method to access the class variable 'cars'
return len(cls.cars)
Tire.py
class Tire:
def __init__(self, brand, size, pressure):
self.brand = brand # Brand of the tire (e.g., Michelin, Goodyear)
self.size = size # Size of the tire (e.g., 205/55R16)
self._pressure = pressure # Private variable to store tire pressure in PSI
@property
def pressure(self):
"""
Get the current tire pressure.
"""
return self._pressure
@pressure.setter
def pressure(self, value):
"""
Set the tire pressure, but ensure it's within a valid range.
"""
if value >= 0:
self._pressure = value
else:
raise ValueError("Tire pressure must be a non-negative value")
def inflate(self, amount):
"""
Inflate the tire by a specified amount of PSI.
"""
if amount > 0:
self.pressure += amount
print(f"Inflated {self.brand} tire to {self.pressure} PSI")
else:
print("Invalid inflation amount. Amount must be greater than 0.")
def deflate(self, amount):
"""
Deflate the tire by a specified amount of PSI.
"""
if amount > 0 and self.pressure - amount >= 0:
self.pressure -= amount
print(f"Deflated {self.brand} tire to {self.pressure} PSI")
else:
print("Invalid deflation amount or tire pressure too low.")
def __str__(self):
"""
Return a string representation of the tire.
"""
return f"{self.brand} Tire ({self.size}) - Pressure: {self.pressure} PSI"