Bootstrap

Python学习之面向对象

面向对象;封装、继承、多态

今天我们来聊一聊面向对象的三要素,封装、继承、多态

  • 封装,顾名思义,就是封装起来,隐藏内部实现细节,只暴露必要的接口。
  • 继承,指一个类(子类)可以继承另一个类(父类)的属性和方法。通过继承,可以实现代码的重用和扩展。
  • 多态,就是同一个方法在不同类中,可以有不同的实现

下面就通过实例来一一了解下他们的使用方法

封装

可以看到,私有属性和私有方法;只能在类里面进行使用,类外则不可以

封装的核心思想,就是数据和逻辑隐藏起来,只暴露接口

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!

面向对象的三大特性,封装、继承、多态就差不多了;细节性的可以自己去研究深入

;