面向对象;封装、继承、多态
今天我们来聊一聊面向对象的三要素,封装、继承、多态
- 封装,顾名思义,就是封装起来,隐藏内部实现细节,只暴露必要的接口。
- 继承,指一个类(子类)可以继承另一个类(父类)的属性和方法。通过继承,可以实现代码的重用和扩展。
- 多态,就是同一个方法在不同类中,可以有不同的实现
下面就通过实例来一一了解下他们的使用方法
封装
可以看到,私有属性和私有方法;只能在类里面进行使用,类外则不可以
封装的核心思想,就是数据和逻辑隐藏起来,只暴露接口
class Employee:
def __init__(self, name, salary):
self.name = name # 公共属性
self.__salary = salary # 私有属性
# 公共方法:显示员工信息
def display_info(self):
print(f"Name: {self.name}")
self.__display_salary() # 在内部调用私有方法
# 私有方法:显示工资
def __display_salary(self):
print(f"Salary: {self.__salary}") # 在内部访问私有属性
# 公共方法:增加工资
def increase_salary(self, amount):
if amount > 0:
self.__salary += amount
self.__log_salary_change(f"Increased by {amount}") # 在内部调用私有方法
else:
print("Amount must be positive.")
# 私有方法:记录工资变化
def __log_salary_change(self, message):
print(f"Log: {message}. New salary: {self.__salary}") # 在内部访问私有属性
# 创建实例
emp = Employee("John", 5000)
# 调用公共方法
emp.display_info()
# 输出:
# Name: John
# Salary: 5000
emp.increase_salary(1000)
# 输出:
# Log: Increased by 1000. New salary: 6000
# 尝试访问私有属性(会报错)
# print(emp.__salary) # AttributeError: 'Employee' object has no attribute '__salary'
# 尝试调用私有方法(会报错)
# emp.__display_salary() # AttributeError: 'Employee' object has no attribute '__display_salary'
但是呢,类外访问私有属性、私有方法也是有办法的;不过不推荐大家这样使用
但是上面说了都是私有得了, 怎么还可以访问呢;这个就是Python的名称改写了;Python的私有成员是通过名称改写实现的;这样做的目的就是避免子类意外覆盖父类的私有成员,而不是严格意义上的访问控制
# 通过名称改写后的名字访问私有成员(不推荐)
print('-' * 28)
print("Private attribute outside :", emp._Employee__salary)
print('+' * 28)
print("Private method outside :", emp._Employee__log_salary_change("hahah"))
"""
----------------------------
Private attribute outside : 6000
++++++++++++++++++++++++++++
Log: hahah. New salary: 6000
Private method outside : None
"""
为什么不推荐上面的方式呢,主要有以下方面
- 破坏了封装,私有成员的设计初衷就是为了隐藏细节,外部直接访问就破坏了封装的意义;本末倒置
- 代码可维护性变差,如果以来名称改写后的名字访问私有成员,代码会变得难以理解
- 不保证兼容性,私有成员的名称改写是Python的实现细节,未来难免Python会进行修改
继承
继承就是指子类可以继承父类的属性和方法,通过继承,可以实现代码的复用和扩展;
通过下面例子可以看到,子类可以重写父类的方法,也可以扩展新的属性和方法
- 我们可以看到子类里面的
__init__
方法,使用了super().__init__()
,意思是调用父类的构造函数,初始化父类的属性 - 子类定义了新的属性和方法
- 重写了父类的方法
- 还可以看到ElectricCar类里面使用了,一个Battery实例来当作属性;这种叫做组合
继承和组合的区别
- 继承是"是一个"关系,如
ElectricCar
是一种Car
- 组合是"有一个"关系,如
ElectricCar
有一个Battery
# 父类:Vehicle(车辆)
class Vehicle:
def __init__(self, make, model, year):
# 初始化父类的属性
self.make = make # 品牌
self.model = model # 型号
self.year = year # 年份
self.mileage = 0 # 里程数,默认值为 0
# 父类方法:描述车辆信息
def describe(self):
return f"{self.year} {self.make} {self.model}"
# 父类方法:更新里程数
def update_mileage(self, miles):
if miles > 0:
self.mileage += miles
else:
print("Miles must be positive.")
# 父类方法:显示里程数
def read_mileage(self):
return f"This vehicle has {self.mileage} miles on it."
# 子类:Car(汽车)
class Car(Vehicle):
def __init__(self, make, model, year, num_doors):
# 调用父类的构造函数
super().__init__(make, model, year)
# 子类新增属性:车门数量
self.num_doors = num_doors
# 重写父类方法:描述车辆信息
def describe(self):
# 调用父类的 describe 方法,并扩展子类的信息
return f"{super().describe()} with {self.num_doors} doors"
# 子类新增方法:鸣笛
def honk(self):
return "Beep beep!"
# 子类:ElectricCar(电动汽车)
class ElectricCar(Car):
def __init__(self, make, model, year, num_doors, battery_size):
# 调用父类的构造函数
super().__init__(make, model, year, num_doors)
# 子类新增属性:电池容量
self.battery_size = battery_size
# 将一个实例作为属性:Battery(电池)
self.battery = Battery(battery_size)
# 重写父类方法:描述车辆信息
def describe(self):
# 调用父类的 describe 方法,并扩展子类的信息
return f"{super().describe()} with a {self.battery_size}-kWh battery"
# 将一个实例作为属性:Battery(电池)
class Battery:
def __init__(self, battery_size):
self.battery_size = battery_size
# 描述电池信息
def describe_battery(self):
return f"This car has a {self.battery_size}-kWh battery."
# 计算续航里程
def get_range(self):
if self.battery_size == 75:
return "Range: 260 miles"
elif self.battery_size == 100:
return "Range: 315 miles"
else:
return "Range: Unknown"
# 测试代码
if __name__ == "__main__":
# 创建 Vehicle 实例
my_vehicle = Vehicle("Toyota", "Camry", 2020)
print(my_vehicle.describe()) # 输出: 2020 Toyota Camry
my_vehicle.update_mileage(15000)
print(my_vehicle.read_mileage()) # 输出: This vehicle has 15000 miles on it.
# 创建 Car 实例
my_car = Car("Honda", "Civic", 2021, 4)
print(my_car.describe()) # 输出: 2021 Honda Civic with 4 doors
print(my_car.honk()) # 输出: Beep beep!
# 创建 ElectricCar 实例
my_tesla = ElectricCar("Tesla", "Model S", 2022, 4, 100)
print(my_tesla.describe()) # 输出: 2022 Tesla Model S with 4 doors with a 100-kWh battery
print(my_tesla.battery.describe_battery()) # 输出: This car has a 100-kWh battery.
print(my_tesla.battery.get_range()) # 输出: Range: 315 miles
多态
多态就是一个方法再不同类中有不同的实现,可以用统一的接口调用不同类的对象,
多态的核心思想是,一个接口,多种实现;可以有以下几种方式
- 子类重写父类的方法
- 父类的引用调用子类的方法
"""
1、父类定义了方法,不实现逻辑
2、子类重写方法,实现了不同的逻辑
3、多态函数,接受一个父类类型的参数
4、无需关心具体的动物类型
5、通过统一接口调用不同类的speak方法
"""
# 父类:Animal(动物)
class Animal:
def speak(self):
# 父类的方法,子类需要重写
raise NotImplementedError("Subclass must implement this method")
# 子类:Dog(狗)
class Dog(Animal):
def speak(self):
return "Woof!"
# 子类:Cat(猫)
class Cat(Animal):
def speak(self):
return "Meow!"
# 子类:Bird(鸟)
class Bird(Animal):
def speak(self):
return "Chirp!"
# 多态函数:让动物发出声音
def animal_sound(animal):
# 统一的接口调用 speak 方法
print(animal.speak())
# 测试代码
if __name__ == "__main__":
# 创建不同动物的实例
dog = Dog()
cat = Cat()
bird = Bird()
# 调用多态函数
animal_sound(dog) # 输出: Woof!
animal_sound(cat) # 输出: Meow!
animal_sound(bird) # 输出: Chirp!
面向对象的三大特性,封装、继承、多态就差不多了;细节性的可以自己去研究深入