面向对象的三大特征:封装、继承、多态
面向对象编程基础
一、封装(Encapsulation)
1. 封装的定义
封装是面向对象编程的核心概念之一,其主要目的是隐藏对象的内部细节,仅对外提供必要的操作接口。通过封装,可以保护对象的内部状态,防止外部直接访问和修改,从而提高代码的安全性和可维护性。
封装的核心在于:
- 隐藏内部实现:将对象的内部数据和实现细节隐藏起来,外部代码无法直接访问。
- 提供操作接口:通过公有方法(Public Methods)对外提供操作接口,允许外部代码通过这些接口与对象交互。
2. 权限控制
在 Python 中,可以通过对属性或方法添加单下划线、双下划线以及首尾双下划线来实现权限控制。
2.1 单下划线开头(Protected)
以单下划线开头的属性或方法表示受保护的成员(Protected)。这类成员被视为仅供内部使用,允许类本身和子类进行访问,但不建议外部代码直接访问。
示例:
class Student:
def __init__(self, name, age):
self._name = name # 受保护的属性
self._age = age # 受保护的属性
def _display_info(self): # 受保护的方法
print(f"Name: {self._name}, Age: {self._age}")
2.2 双下划线开头(Private)
以双下划线开头的属性或方法表示私有的成员(Private)。这类成员只允许定义该属性或方法的类本身进行访问。Python 通过名称重整(Name Mangling)来实现私有化。
示例:
class Student:
def __init__(self, name, age):
self.__name = name # 私有属性
self.__age = age # 私有属性
def __display_info(self): # 私有方法
print(f"Name: {self.__name}, Age: {self.__age}")
2.3 首尾双下划线(Special Methods)
以首尾双下划线开头和结尾的属性或方法通常表示特殊的方法(如 __init__、__str__ 等),这些方法有特殊的语义和用途。
示例:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Student(name={self.name}, age={self.age})"
3. 示例:封装的实现
3.1 权限控制
class Student:
def __init__(self, name, age, gender):
self._name = name # 受保护的属性
self.__age = age # 私有属性
self.gender = gender # 公有属性
def _display_info(self): # 受保护的方法
print(f"Name: {self._name}, Age: {self.__age}")
def __display_private_info(self): # 私有方法
print(f"Private Info: {self.__age}")
def show(self): # 公有方法
self._display_info()
self.__display_private_info()
print(f"Gender: {self.gender}")
# 创建对象
stu = Student("Kyle", 20, "man")
# 访问受保护的属性和方法
print(stu._name) # 输出:Kyle
stu._display_info() # 输出:Name: Kyle, Age: 20
# 尝试访问私有属性和方法(不推荐)
print(stu._Student__age) # 输出:20
stu._Student__display_private_info() # 输出:Private Info: 20
# 调用公有方法
stu.show()
输出结果:
Kyle
Name: Kyle, Age: 20
Name: Kyle, Age: 20
Private Info: 20
Gender: man
3.2 属性的设置与访问控制
class Student:
def __init__(self, name, gender):
self.name = name
self.__gender = gender # 私有属性
@property
def gender(self):
"""获取性别属性"""
return self.__gender
@gender.setter
def gender(self, value):
"""设置性别属性,添加校验逻辑"""
if value not in ["man", "woman"]:
print("性别有误,已将性别默认设置为 woman")
self.__gender = "woman"
else:
self.__gender = value
# 创建对象
stu = Student("Kyle", "man")
print(f"{stu.name} 的性别是 {stu.gender}") # 输出:Kyle 的性别是 man
# 尝试修改性别属性
stu.gender = "other" # 性别有误,已将性别默认设置为 woman
print(f"{stu.name} 的性别是 {stu.gender}") # 输出:Kyle 的性别是 woman
输出结果:
Kyle 的性别是 man
性别有误,已将性别默认设置为 woman
Kyle 的性别是 woman
3.3 定义一个 BankAccount 类
class BankAccount:
def __init__(self, owner, balance=0):
self.__owner = owner # 私有属性:账户所有者
self.__balance = balance # 私有属性:账户余额
def deposit(self, amount): # 公有方法:存款
if amount > 0:
self.__balance += amount
print(f"Deposited {amount}. New balance: {self.__balance}")
else:
print("Invalid deposit amount.")
def withdraw(self, amount): # 公有方法:取款
if amount > 0 and amount <= self.__balance:
self.__balance -= amount
print(f"Withdrew {amount}. New balance: {self.__balance}")
else:
print("Invalid withdrawal amount or insufficient funds.")
def get_balance(self): # 公有方法:获取余额
return self.__balance
def __str__(self): # 特殊方法:打印对象信息
return f"BankAccount(owner={self.__owner}, balance={self.__balance})"
# 创建 BankAccount 对象并操作
account = BankAccount("Alice", 100)
account.deposit(50) # 输出:Deposited 50. New balance: 150
account.withdraw(30) # 输出:Withdrew 30. New balance: 120
print(account.get_balance()) # 输出:120
print(account) # 输出:BankAccount(owner=Alice, balance=120)
4. 封装的优势
- 隐藏内部实现:封装使得对象的内部实现细节对外部不可见,仅通过公开的接口进行交互,降低了代码的耦合度。
- 保护数据安全:通过私有化属性和方法,防止外部代码直接访问和修改对象的内部状态,避免数据被错误操作。
- 提供统一的接口:封装允许开发者通过统一的接口操作对象,而不必关心具体的实现细节,提高了代码的可读性和可维护性。
5. 总结
封装是面向对象编程的核心概念之一,通过权限控制(如单下划线、双下划线等)和属性访问控制(如 @property 和 @<属性名>.setter),可以有效地隐藏对象的内部细节,保护数据安全,同时提供统一的接口供外部使用。掌握封装的使用方法,可以帮助你更好地设计和实现面向对象的程序。
二、继承(Inheritance)
1. 定义
- 继承:允许一个类(子类)继承另一个类(父类)的属性和方法。
- 子类:继承了父类的属性和方法,并可以添加新的属性和方法或修改父类的方法。
- 父类:被继承的类。
- 一个子类可以继承多个父类;一个父类也可以拥有多个子类;如果一个类没有继承任何类,那么这个类默认继承
object类。
2. 语法结构
class 类名(父类1, 父类2, ..., 父类N):
pass
3. 示例:定义一个 Student 类,继承自 Person 类
class Person: # 默认继承 object
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
print(f'Hello everyone, my name is {self.name} and I am {self.age} years old.')
class Student(Person): # Student 类继承 Person 类
def __init__(self, name, age, stuno):
super().__init__(name, age) # 调用父类的初始化方法
self.stuno = stuno # 新增属性:学生ID
def study(self): # 新增方法:学习
print(f"{self.name} is studying with student ID {self.stuno}.")
创建 Student 对象
# 创建一个 Student 对象
student1 = Student("Bob", 20, "S12345")
# 调用继承自父类的方法
student1.show() # 输出:Hello everyone, my name is Bob and I am 20 years old.
# 调用子类新增的方法
student1.study() # 输出:Bob is studying with student ID S12345.
4. 多级继承示例:一个父类多个子类
class Teacher(Person): # 另一个子类,继承自 Person
def __init__(self, name, age, subject):
super().__init__(name, age) # 调用父类的构造方法
self.subject = subject # 新增属性:教授的科目
def teach(self): # 新增方法:教学
print(f"{self.name} is teaching {self.subject}.")
创建 Teacher 对象
teacher1 = Teacher("Alice", 35, "Mathematics")
# 调用继承自父类的方法
teacher1.show() # 输出:Hello everyone, my name is Alice and I am 35 years old.
# 调用子类新增的方法
teacher1.teach() # 输出:Alice is teaching Mathematics.
5. 多继承示例:一个子类多个父类
class FatherA:
def __init__(self, name):
self.name = name
def showA(self):
print('父类 A 中的方法')
class FatherB:
def __init__(self, age):
self.age = age
def showB(self):
print('父类 B 中的方法')
# 多继承
class Son(FatherA, FatherB):
def __init__(self, name, age, gender):
# 需要调用两个父类的初始化方法,使用父类的名称来区别
FatherA.__init__(self, name)
FatherB.__init__(self, age)
self.gender = gender
def show(self):
print(f'My name is {self.name}, age {self.age}, and I am a {self.gender}.')
测试多继承
son = Son('Kyle', 20, 'man')
son.showA() # 输出:父类 A 中的方法
son.showB() # 输出:父类 B 中的方法
son.show() # 输出:My name is Kyle, age 20, and I am a man.
6. 方法重写
- 子类继承了父类就拥有了父类中公有成员和受保护的成员。
- 父类的方法可能并不能完全适合子类的需求,这时子类可以重写父类的方法。
- 子类在重写父类的方法时,要求方法的名称必须与父类方法的名称相同。在子类重写后的方法中,可以通过
super().xxx()调用父类中的方法。
class Person: # 默认继承 object
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
print(f'Hello everyone, my name is {self.name} and I am {self.age} years old.')
# Student 类继承 Person 类
class Student(Person):
def __init__(self, name, age, stuno):
super().__init__(name, age) # 调用父类的初始化方法
self.stuno = stuno
def show(self):
# 调用父类中的方法
super().show()
print(f'My student ID is {self.stuno}.')
# Doctor 类继承 Person 类
class Doctor(Person):
def __init__(self, name, age, department):
super().__init__(name, age) # 调用父类的初始化方法
self.department = department
def show(self):
# 调用父类中的方法
# super().show()
print(f'Hello everyone, my name is {self.name}, I am {self.age} years old, and I work in the {self.department} department.')
测试方法重写
stu = Student('Kyle', 20, '1001')
stu.show()
# 输出:
# Hello everyone, my name is Kyle and I am 20 years old.
# My student ID is 1001.
doctor = Doctor('Ming', 25, 'WK')
doctor.show()
# 输出:
# Hello everyone, my name is Ming, I am 25 years old, and I work in the WK department.
7. 总结
继承是面向对象编程的重要特性之一,它允许子类继承父类的属性和方法,从而实现代码复用。通过继承,可以构建层次化的类结构,提高代码的可维护性和可扩展性。然而,过度使用继承可能导致代码复杂化,因此在设计类时应合理使用继承,避免多级继承过深。
三、多态(Polymorphism)
1. 定义
多态是面向对象编程中的一个重要概念,它允许不同类的对象对同一消息做出响应,即同一个接口可以被不同的底层实现替换。通俗理解就是:同一个方法名,不同的行为。多态的核心在于动态决定使用哪个对象的方法,从而实现程序的可扩展性。
- 多态的特性:
- 接口一致性:不同类的对象可以共享同一个接口(方法名)。
- 行为多样性:同一个接口在不同类中可以有不同的实现。
- 动态绑定:在运行时根据对象的实际类型调用相应的方法。
2. 示例:定义 Animal 类及其子类
class Animal:
def __init__(self, name):
self.name = name # 初始化 name 属性
def make_sound(self): # 父类中的方法
print("Some generic animal sound.")
class Dog(Animal):
def make_sound(self): # 重写父类方法
print("Woof!")
class Cat(Animal):
def make_sound(self): # 重写父类方法
print("Meow!")
3. 多态的体现
3.1 不同对象调用相同方法
# 创建 Animal 对象
animal1 = Animal("Generic Animal") # 传入 "Generic Animal" 作为 name
animal1.make_sound() # 输出:Some generic animal sound.
# 创建 Dog 对象
dog1 = Dog("Buddy") # 传入 "Buddy" 作为 name
dog1.make_sound() # 输出:Woof!
# 创建 Cat 对象
cat1 = Cat("Whiskers") # 传入 "Whiskers" 作为 name
cat1.make_sound() # 输出:Meow!
3.2 通过函数体现多态
# 定义一个函数,接受一个 Animal 类型的对象
def animal_sound(animal: Animal):
animal.make_sound()
# 传入不同类型的对象
animal_sound(animal1) # 输出:Some generic animal sound.
animal_sound(dog1) # 输出:Woof!
animal_sound(cat1) # 输出:Meow!
示例:使用 name 属性
class Animal:
def __init__(self, name):
self.name = name
def make_sound(self):
print(f"{self.name} makes a generic animal sound.")
class Dog(Animal):
def make_sound(self):
print(f"{self.name} says Woof!")
class Cat(Animal):
def make_sound(self):
print(f"{self.name} says Meow!")
创建对象并调用方法
animal1 = Animal("Generic Animal")
animal1.make_sound() # 输出:Generic Animal makes a generic animal sound.
dog1 = Dog("Buddy")
dog1.make_sound() # 输出:Buddy says Woof!
cat1 = Cat("Whiskers")
cat1.make_sound() # 输出:Whiskers says Meow!
3.3 多态的优势
- 代码复用:通过多态,可以编写通用的代码,而无需关心具体的对象类型。
- 扩展性:新增类时,只要遵循相同的接口,无需修改现有代码。
- 灵活性:可以在运行时动态决定调用哪个类的方法。
4. 更多示例:多态的应用
4.1 不同类的同名方法
class Person:
def eat(self):
print('五谷杂粮')
class Cat:
def eat(self):
print('鱼')
class Dog:
def eat(self):
print('骨头')
4.2 通过函数调用体现多态
# 定义一个函数,接受一个对象并调用其 eat 方法
def fun(obj):
obj.eat()
# 创建三个类的对象
per = Person()
cat = Cat()
dog = Dog()
# 调用 fun 函数
fun(per) # 输出:五谷杂粮
fun(cat) # 输出:鱼
fun(dog) # 输出:骨头
4.3 使用循环体现多态
# 将不同类的对象放入列表
animals = [per, cat, dog]
# 遍历列表,调用每个对象的 eat 方法
for item in animals:
item.eat()
输出结果:
五谷杂粮
鱼
骨头
5. 多态的实现机制
5.1 方法重写(Override)
子类重写父类的方法,使得同一个方法名在不同类中可以有不同的实现。这是多态的基础。
class Animal:
def make_sound(self):
print("Some generic animal sound.")
class Dog(Animal):
def make_sound(self):
print("Woof!")
class Cat(Animal):
def make_sound(self):
print("Meow!")
5.2 动态绑定
Python 在运行时根据对象的实际类型调用相应的方法,而不是根据变量的声明类型。这使得多态成为可能。
def animal_sound(animal: Animal):
animal.make_sound()
animal_sound(Dog()) # 输出:Woof!
animal_sound(Cat()) # 输出:Meow!
# 等效于
dog = Dog()
cat = Cat()
animal_sound(dog) # 输出:Woof!
animal_sound(cat) # 输出:Meow!
---------
# 函数调用过程
创建对象:
Dog() 创建了一个 Dog 类的对象。
Cat() 创建了一个 Cat 类的对象。
传递对象:
animal_sound(Dog()) 将 Dog 类的对象传递给 animal_sound 函数。
animal_sound(Cat()) 将 Cat 类的对象传递给 animal_sound 函数。
函数内部:
在 animal_sound 函数中,animal 是一个 Animal 类型的对象(或者其子类的对象)。
调用 animal.make_sound() 时,Python 会根据 animal 的实际类型(Dog 或 Cat)调用相应的方法。
-
animal: Animal 是一种
类型注解 -
animal:这是函数的参数名,表示函数接收一个参数。
-
Animal:这是参数的类型注解,表示传入的参数应该是一个 Animal 类型的对象(或者其子类的对象)。
通过这种方式,animal_sound 函数明确地表示它接收一个 Animal 类型的对象,并调用该对象的 make_sound 方法。
6. 多态的应用场景
6.1 图形绘制
假设有一个图形类的层次结构,每个图形都有一个 draw 方法。通过多态,可以编写通用的代码来绘制不同类型的图形。
class Shape:
def draw(self):
print("Drawing a generic shape.")
class Circle(Shape):
def draw(self):
print("Drawing a circle.")
class Square(Shape):
def draw(self):
print("Drawing a square.")
def draw_shape(shape: Shape):
shape.draw()
draw_shape(Circle()) # 输出:Drawing a circle.
draw_shape(Square()) # 输出:Drawing a square.
6.2 用户界面组件
假设有一个组件类的层次结构,每个组件都有一个 render 方法。通过多态,可以编写通用的代码来渲染不同类型的组件。
class Component:
def render(self):
print("Rendering a generic component.")
class Button(Component):
def render(self):
print("Rendering a button.")
class Label(Component):
def render(self):
print("Rendering a label.")
def render_component(component: Component):
component.render()
render_component(Button()) # 输出:Rendering a button.
render_component(Label()) # 输出:Rendering a label.
7. 总结
多态是面向对象编程的核心特性之一,它允许不同类的对象对同一消息做出响应,即同一个接口可以被不同的底层实现替换。通过多态,可以编写通用的代码,提高程序的可扩展性和灵活性。多态的实现基于方法重写和动态绑定,使得同一个方法名在不同类中可以有不同的行为。掌握多态的使用方法,可以帮助你更好地设计和实现面向对象的程序。

浙公网安备 33010602011771号